diff options
325 files changed, 4604 insertions, 6877 deletions
diff --git a/core/java/Android.bp b/core/java/Android.bp index 236d0b5814d7..f8f4cc3523f2 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -34,15 +34,6 @@ filegroup { } filegroup { - name: "feature_flags_aidl", - srcs: [ - "android/flags/IFeatureFlags.aidl", - "android/flags/IFeatureFlagsCallback.aidl", - "android/flags/SyncableFlag.aidl", - ], -} - -filegroup { name: "ITracingServiceProxy.aidl", srcs: ["android/tracing/ITracingServiceProxy.aidl"], } diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 70c3d7ae3f82..f33d2991329a 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -1346,8 +1346,26 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } // Set the child animators to the right end: if (mShouldResetValuesAtStart) { - initChildren(); - skipToEndValue(!mReversing); + if (isInitialized()) { + skipToEndValue(!mReversing); + } else if (mReversing) { + // Reversing but haven't initialized all the children yet. + initChildren(); + skipToEndValue(!mReversing); + } else { + // If not all children are initialized and play direction is forward + for (int i = mEvents.size() - 1; i >= 0; i--) { + if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { + Animator anim = mEvents.get(i).mNode.mAnimation; + // Only reset the animations that have been initialized to start value, + // so that if they are defined without a start value, they will get the + // values set at the right time (i.e. the next animation run) + if (anim.isInitialized()) { + anim.skipToEndValue(true); + } + } + } + } } if (mReversing || mStartDelay == 0 || mSeekState.isActive()) { diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl index 3d6ab6fd9478..9a818e49ae4a 100644 --- a/core/java/android/app/IGameManagerService.aidl +++ b/core/java/android/app/IGameManagerService.aidl @@ -20,6 +20,7 @@ import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameModeListener; +import android.app.IGameStateListener; /** * @hide @@ -49,4 +50,6 @@ interface IGameManagerService { void addGameModeListener(IGameModeListener gameModeListener); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void removeGameModeListener(IGameModeListener gameModeListener); + void addGameStateListener(IGameStateListener gameStateListener); + void removeGameStateListener(IGameStateListener gameStateListener); } diff --git a/core/java/android/flags/SyncableFlag.aidl b/core/java/android/app/IGameStateListener.aidl index 1526ec148967..34cff489d077 100644 --- a/core/java/android/flags/SyncableFlag.aidl +++ b/core/java/android/app/IGameStateListener.aidl @@ -14,9 +14,14 @@ * limitations under the License. */ -package android.flags; +package android.app; -/** - * A parcelable data class for serializing {@link Flag} across a Binder. - */ -parcelable SyncableFlag;
\ No newline at end of file +import android.app.GameState; + +/** @hide */ +interface IGameStateListener { + /** + * Called when the state of the game has changed. + */ + oneway void onGameStateChanged(String packageName, in GameState state, int userId); +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 2a6d84b1acc6..6d82922484bc 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5298,13 +5298,6 @@ public abstract class Context { public static final String APP_PREDICTION_SERVICE = "app_prediction"; /** - * Used for reading system-wide, overridable flags. - * - * @hide - */ - public static final String FEATURE_FLAGS_SERVICE = "feature_flags"; - - /** * Official published name of the search ui service. * * <p><b>NOTE: </b> this service is optional; callers of diff --git a/core/java/android/flags/BooleanFlag.java b/core/java/android/flags/BooleanFlag.java deleted file mode 100644 index d4a35b25f623..000000000000 --- a/core/java/android/flags/BooleanFlag.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.flags; - -import android.annotation.NonNull; - -/** - * A flag representing a true or false value. - * - * The value will always be the same during the lifetime of the process it is read in. - * - * @hide - */ -public class BooleanFlag extends BooleanFlagBase { - private final boolean mDefault; - - /** - * @param namespace A namespace for this flag. See {@link android.provider.DeviceConfig}. - * @param name A name for this flag. - * @param defaultValue The value of this flag if no other override is present. - */ - BooleanFlag(String namespace, String name, boolean defaultValue) { - super(namespace, name); - mDefault = defaultValue; - } - - @Override - @NonNull - public Boolean getDefault() { - return mDefault; - } - - @Override - public BooleanFlag defineMetaData(String label, String description, String categoryName) { - super.defineMetaData(label, description, categoryName); - return this; - } -} diff --git a/core/java/android/flags/BooleanFlagBase.java b/core/java/android/flags/BooleanFlagBase.java deleted file mode 100644 index 985dbe3f2f01..000000000000 --- a/core/java/android/flags/BooleanFlagBase.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.flags; - -import android.annotation.NonNull; - -abstract class BooleanFlagBase implements Flag<Boolean> { - - private final String mNamespace; - private final String mName; - private String mLabel; - private String mDescription; - private String mCategoryName; - - /** - * @param namespace A namespace for this flag. See {@link android.provider.DeviceConfig}. - * @param name A name for this flag. - */ - BooleanFlagBase(String namespace, String name) { - mNamespace = namespace; - mName = name; - mLabel = name; - } - - public abstract Boolean getDefault(); - - @Override - @NonNull - public String getNamespace() { - return mNamespace; - } - - @Override - @NonNull - public String getName() { - return mName; - } - - @Override - public BooleanFlagBase defineMetaData(String label, String description, String categoryName) { - mLabel = label; - mDescription = description; - mCategoryName = categoryName; - return this; - } - - @Override - @NonNull - public String getLabel() { - return mLabel; - } - - @Override - public String getDescription() { - return mDescription; - } - - @Override - public String getCategoryName() { - return mCategoryName; - } - - @Override - @NonNull - public String toString() { - return getNamespace() + "." + getName() + "[" + getDefault() + "]"; - } -} diff --git a/core/java/android/flags/DynamicBooleanFlag.java b/core/java/android/flags/DynamicBooleanFlag.java deleted file mode 100644 index 271a8c5f4d15..000000000000 --- a/core/java/android/flags/DynamicBooleanFlag.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.flags; - -/** - * A flag representing a true or false value. - * - * The value may be different from one read to the next. - * - * @hide - */ -public class DynamicBooleanFlag extends BooleanFlagBase implements DynamicFlag<Boolean> { - - private final boolean mDefault; - - /** - * @param namespace A namespace for this flag. See {@link android.provider.DeviceConfig}. - * @param name A name for this flag. - * @param defaultValue The value of this flag if no other override is present. - */ - DynamicBooleanFlag(String namespace, String name, boolean defaultValue) { - super(namespace, name); - mDefault = defaultValue; - } - - @Override - public Boolean getDefault() { - return mDefault; - } - - @Override - public DynamicBooleanFlag defineMetaData(String label, String description, String categoryName) { - super.defineMetaData(label, description, categoryName); - return this; - } -} diff --git a/core/java/android/flags/FeatureFlags.java b/core/java/android/flags/FeatureFlags.java deleted file mode 100644 index 8d3112c35d51..000000000000 --- a/core/java/android/flags/FeatureFlags.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.flags; - -import android.annotation.NonNull; -import android.content.Context; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.util.ArraySet; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * A class for querying constants from the system - primarily booleans. - * - * Clients using this class can define their flags and their default values in one place, - * can override those values on running devices for debugging and testing purposes, and can control - * what flags are available to be used on release builds. - * - * TODO(b/279054964): A lot. This is skeleton code right now. - * @hide - */ -public class FeatureFlags { - private static final String TAG = "FeatureFlags"; - private static FeatureFlags sInstance; - private static final Object sInstanceLock = new Object(); - - private final Set<Flag<?>> mKnownFlags = new ArraySet<>(); - private final Set<Flag<?>> mDirtyFlags = new ArraySet<>(); - - private IFeatureFlags mIFeatureFlags; - private final Map<String, Map<String, Boolean>> mBooleanOverrides = new HashMap<>(); - private final Set<ChangeListener> mListeners = new HashSet<>(); - - /** - * Obtain a per-process instance of FeatureFlags. - * @return A singleton instance of {@link FeatureFlags}. - */ - @NonNull - public static FeatureFlags getInstance() { - synchronized (sInstanceLock) { - if (sInstance == null) { - sInstance = new FeatureFlags(); - } - } - - return sInstance; - } - - /** See {@link FeatureFlagsFake}. */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - public static void setInstance(FeatureFlags instance) { - synchronized (sInstanceLock) { - sInstance = instance; - } - } - - private final IFeatureFlagsCallback mIFeatureFlagsCallback = new IFeatureFlagsCallback.Stub() { - @Override - public void onFlagChange(SyncableFlag flag) { - for (Flag<?> f : mKnownFlags) { - if (flagEqualsSyncableFlag(f, flag)) { - if (f instanceof DynamicFlag<?>) { - if (f instanceof DynamicBooleanFlag) { - String value = flag.getValue(); - if (value == null) { // Null means any existing overrides were erased. - value = ((DynamicBooleanFlag) f).getDefault().toString(); - } - addBooleanOverride(flag.getNamespace(), flag.getName(), value); - } - FeatureFlags.this.onFlagChange((DynamicFlag<?>) f); - } - break; - } - } - } - }; - - private FeatureFlags() { - this(null); - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - public FeatureFlags(IFeatureFlags iFeatureFlags) { - mIFeatureFlags = iFeatureFlags; - - if (mIFeatureFlags != null) { - try { - mIFeatureFlags.registerCallback(mIFeatureFlagsCallback); - } catch (RemoteException e) { - // Shouldn't happen with things passed into tests. - Log.e(TAG, "Could not register callbacks!", e); - } - } - } - - /** - * Construct a new {@link BooleanFlag}. - * - * Use this instead of constructing a {@link BooleanFlag} directly, as it registers the flag - * with the internals of the flagging system. - */ - @NonNull - public static BooleanFlag booleanFlag( - @NonNull String namespace, @NonNull String name, boolean def) { - return getInstance().addFlag(new BooleanFlag(namespace, name, def)); - } - - /** - * Construct a new {@link FusedOffFlag}. - * - * Use this instead of constructing a {@link FusedOffFlag} directly, as it registers the - * flag with the internals of the flagging system. - */ - @NonNull - public static FusedOffFlag fusedOffFlag(@NonNull String namespace, @NonNull String name) { - return getInstance().addFlag(new FusedOffFlag(namespace, name)); - } - - /** - * Construct a new {@link FusedOnFlag}. - * - * Use this instead of constructing a {@link FusedOnFlag} directly, as it registers the flag - * with the internals of the flagging system. - */ - @NonNull - public static FusedOnFlag fusedOnFlag(@NonNull String namespace, @NonNull String name) { - return getInstance().addFlag(new FusedOnFlag(namespace, name)); - } - - /** - * Construct a new {@link DynamicBooleanFlag}. - * - * Use this instead of constructing a {@link DynamicBooleanFlag} directly, as it registers - * the flag with the internals of the flagging system. - */ - @NonNull - public static DynamicBooleanFlag dynamicBooleanFlag( - @NonNull String namespace, @NonNull String name, boolean def) { - return getInstance().addFlag(new DynamicBooleanFlag(namespace, name, def)); - } - - /** - * Add a listener to be alerted when a {@link DynamicFlag} changes. - * - * See also {@link #removeChangeListener(ChangeListener)}. - * - * @param listener The listener to add. - */ - public void addChangeListener(@NonNull ChangeListener listener) { - mListeners.add(listener); - } - - /** - * Remove a listener that was added earlier. - * - * See also {@link #addChangeListener(ChangeListener)}. - * - * @param listener The listener to remove. - */ - public void removeChangeListener(@NonNull ChangeListener listener) { - mListeners.remove(listener); - } - - protected void onFlagChange(@NonNull DynamicFlag<?> flag) { - for (ChangeListener l : mListeners) { - l.onFlagChanged(flag); - } - } - - /** - * Returns whether the supplied flag is true or not. - * - * {@link BooleanFlag} should only be used in debug builds. They do not get optimized out. - * - * The first time a flag is read, its value is cached for the lifetime of the process. - */ - public boolean isEnabled(@NonNull BooleanFlag flag) { - return getBooleanInternal(flag); - } - - /** - * Returns whether the supplied flag is true or not. - * - * Always returns false. - */ - public boolean isEnabled(@NonNull FusedOffFlag flag) { - return false; - } - - /** - * Returns whether the supplied flag is true or not. - * - * Always returns true; - */ - public boolean isEnabled(@NonNull FusedOnFlag flag) { - return true; - } - - /** - * Returns whether the supplied flag is true or not. - * - * Can return a different value for the flag each time it is called if an override comes in. - */ - public boolean isCurrentlyEnabled(@NonNull DynamicBooleanFlag flag) { - return getBooleanInternal(flag); - } - - private boolean getBooleanInternal(Flag<Boolean> flag) { - sync(); - Map<String, Boolean> ns = mBooleanOverrides.get(flag.getNamespace()); - Boolean value = null; - if (ns != null) { - value = ns.get(flag.getName()); - } - if (value == null) { - throw new IllegalStateException("Boolean flag being read but was not synced: " + flag); - } - - return value; - } - - private <T extends Flag<?>> T addFlag(T flag) { - synchronized (FeatureFlags.class) { - mDirtyFlags.add(flag); - mKnownFlags.add(flag); - } - return flag; - } - - /** - * Sync any known flags that have not yet been synced. - * - * This is called implicitly when any flag is read, and is not generally needed except in - * exceptional circumstances. - */ - public void sync() { - synchronized (FeatureFlags.class) { - if (mDirtyFlags.isEmpty()) { - return; - } - syncInternal(mDirtyFlags); - mDirtyFlags.clear(); - } - } - - /** - * Called when new flags have been declared. Gives the implementation a chance to act on them. - * - * Guaranteed to be called from a synchronized, thread-safe context. - */ - protected void syncInternal(Set<Flag<?>> dirtyFlags) { - IFeatureFlags iFeatureFlags = bind(); - List<SyncableFlag> syncableFlags = new ArrayList<>(); - for (Flag<?> f : dirtyFlags) { - syncableFlags.add(flagToSyncableFlag(f)); - } - - List<SyncableFlag> serverFlags = List.of(); // Need to initialize the list with something. - try { - // New values come back from the service. - serverFlags = iFeatureFlags.syncFlags(syncableFlags); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - - for (Flag<?> f : dirtyFlags) { - boolean found = false; - for (SyncableFlag sf : serverFlags) { - if (flagEqualsSyncableFlag(f, sf)) { - if (f instanceof BooleanFlag || f instanceof DynamicBooleanFlag) { - addBooleanOverride(sf.getNamespace(), sf.getName(), sf.getValue()); - } - found = true; - break; - } - } - if (!found) { - if (f instanceof BooleanFlag) { - addBooleanOverride( - f.getNamespace(), - f.getName(), - ((BooleanFlag) f).getDefault() ? "true" : "false"); - } - } - } - } - - private void addBooleanOverride(String namespace, String name, String override) { - Map<String, Boolean> nsOverrides = mBooleanOverrides.get(namespace); - if (nsOverrides == null) { - nsOverrides = new HashMap<>(); - mBooleanOverrides.put(namespace, nsOverrides); - } - nsOverrides.put(name, parseBoolean(override)); - } - - private SyncableFlag flagToSyncableFlag(Flag<?> f) { - return new SyncableFlag( - f.getNamespace(), - f.getName(), - f.getDefault().toString(), - f instanceof DynamicFlag<?>); - } - - private IFeatureFlags bind() { - if (mIFeatureFlags == null) { - mIFeatureFlags = IFeatureFlags.Stub.asInterface( - ServiceManager.getService(Context.FEATURE_FLAGS_SERVICE)); - try { - mIFeatureFlags.registerCallback(mIFeatureFlagsCallback); - } catch (RemoteException e) { - Log.e(TAG, "Failed to listen for flag changes!"); - } - } - - return mIFeatureFlags; - } - - static boolean parseBoolean(String value) { - // Check for a truish string. - boolean result = value.equalsIgnoreCase("true") - || value.equals("1") - || value.equalsIgnoreCase("t") - || value.equalsIgnoreCase("on"); - if (!result) { // Expect a falsish string, else log an error. - if (!(value.equalsIgnoreCase("false") - || value.equals("0") - || value.equalsIgnoreCase("f") - || value.equalsIgnoreCase("off"))) { - Log.e(TAG, - "Tried parsing " + value + " as boolean but it doesn't look like one. " - + "Value expected to be one of true|false, 1|0, t|f, on|off."); - } - } - return result; - } - - private static boolean flagEqualsSyncableFlag(Flag<?> f, SyncableFlag sf) { - return f.getName().equals(sf.getName()) && f.getNamespace().equals(sf.getNamespace()); - } - - - /** - * A simpler listener that is alerted when a {@link DynamicFlag} changes. - * - * See {@link #addChangeListener(ChangeListener)} - */ - public interface ChangeListener { - /** - * Called when a {@link DynamicFlag} changes. - * - * @param flag The flag that has changed. - */ - void onFlagChanged(DynamicFlag<?> flag); - } -} diff --git a/core/java/android/flags/FeatureFlagsFake.java b/core/java/android/flags/FeatureFlagsFake.java deleted file mode 100644 index daedcdae0b5f..000000000000 --- a/core/java/android/flags/FeatureFlagsFake.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.flags; - -import android.annotation.NonNull; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * An implementation of {@link FeatureFlags} for testing. - * - * Before you read a flag from using this Fake, you must set that flag using - * {@link #setFlagValue(BooleanFlagBase, boolean)}. This ensures that your tests are deterministic. - * - * If you are relying on {@link FeatureFlags#getInstance()} to access FeatureFlags in your code - * under test, (instead of dependency injection), you can pass an instance of this fake to - * {@link FeatureFlags#setInstance(FeatureFlags)}. Be sure to call that method again, passing null, - * to ensure hermetic testing - you don't want static state persisting between your test methods. - * - * @hide - */ -public class FeatureFlagsFake extends FeatureFlags { - private final Map<BooleanFlagBase, Boolean> mFlagValues = new HashMap<>(); - private final Set<BooleanFlagBase> mReadFlags = new HashSet<>(); - - public FeatureFlagsFake(IFeatureFlags iFeatureFlags) { - super(iFeatureFlags); - } - - @Override - public boolean isEnabled(@NonNull BooleanFlag flag) { - return requireFlag(flag); - } - - @Override - public boolean isEnabled(@NonNull FusedOffFlag flag) { - return requireFlag(flag); - } - - @Override - public boolean isEnabled(@NonNull FusedOnFlag flag) { - return requireFlag(flag); - } - - @Override - public boolean isCurrentlyEnabled(@NonNull DynamicBooleanFlag flag) { - return requireFlag(flag); - } - - @Override - protected void syncInternal(Set<Flag<?>> dirtyFlags) { - } - - /** - * Explicitly set a flag's value for reading in tests. - * - * You _must_ call this for every flag your code-under-test will read. Otherwise, an - * {@link IllegalStateException} will be thrown. - * - * You are able to set values for {@link FusedOffFlag} and {@link FusedOnFlag}, despite those - * flags having a fixed value at compile time, since unit tests should still test the state of - * those flags as both true and false. I.e. a flag that is off might be turned on in a future - * build or vice versa. - * - * You can not call this method _after_ a non-dynamic flag has been read. Non-dynamic flags - * are held stable in the system, so changing a value after reading would not match - * real-implementation behavior. - * - * Calling this method will trigger any {@link android.flags.FeatureFlags.ChangeListener}s that - * are registered for the supplied flag if the flag is a {@link DynamicFlag}. - * - * @param flag The BooleanFlag that you want to set a value for. - * @param value The value that the flag should return when accessed. - */ - public void setFlagValue(@NonNull BooleanFlagBase flag, boolean value) { - if (!(flag instanceof DynamicBooleanFlag) && mReadFlags.contains(flag)) { - throw new RuntimeException( - "You can not set the value of a flag after it has been read. Tried to set " - + flag + " to " + value + " but it already " + mFlagValues.get(flag)); - } - mFlagValues.put(flag, value); - if (flag instanceof DynamicBooleanFlag) { - onFlagChange((DynamicFlag<?>) flag); - } - } - - private boolean requireFlag(BooleanFlagBase flag) { - if (!mFlagValues.containsKey(flag)) { - throw new IllegalStateException( - "Tried to access " + flag + " in test but no overrided specified. You must " - + "call #setFlagValue for each flag read in a test."); - } - mReadFlags.add(flag); - - return mFlagValues.get(flag); - } - -} diff --git a/core/java/android/flags/Flag.java b/core/java/android/flags/Flag.java deleted file mode 100644 index b97a4c8a0fe7..000000000000 --- a/core/java/android/flags/Flag.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.flags; - -import android.annotation.NonNull; - -/** - * Base class for constants read via {@link android.flags.FeatureFlags}. - * - * @param <T> The type of value that this flag stores. E.g. Boolean or String. - * - * @hide - */ -public interface Flag<T> { - /** The namespace for a flag. Should combine uniquely with its name. */ - @NonNull - String getNamespace(); - - /** The name of the flag. Should combine uniquely with its namespace. */ - @NonNull - String getName(); - - /** The value of this flag if no override has been set. Null values are not supported. */ - @NonNull - T getDefault(); - - /** Returns true if the value of this flag can change at runtime. */ - default boolean isDynamic() { - return false; - } - - /** - * Add human-readable details to the flag. Flag client's are not required to set this. - * - * See {@link #getLabel()}, {@link #getDescription()}, and {@link #getCategoryName()}. - * - * @return Returns `this`, to make a fluent api. - */ - Flag<T> defineMetaData(String label, String description, String categoryName); - - /** - * A human-readable name for the flag. Defaults to {@link #getName()} - * - * See {@link #defineMetaData(String, String, String)} - */ - @NonNull - default String getLabel() { - return getName(); - } - - /** - * A human-readable description for the flag. Defaults to null if unset. - * - * See {@link #defineMetaData(String, String, String)} - */ - default String getDescription() { - return null; - } - - /** - * A human-readable category name for the flag. Defaults to null if unset. - * - * See {@link #defineMetaData(String, String, String)} - */ - default String getCategoryName() { - return null; - } -} diff --git a/core/java/android/flags/FusedOffFlag.java b/core/java/android/flags/FusedOffFlag.java deleted file mode 100644 index 6844b8faafef..000000000000 --- a/core/java/android/flags/FusedOffFlag.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.flags; - -import android.annotation.NonNull; -import android.provider.DeviceConfig; - -/** - * A flag representing a false value. - * - * The flag can never be changed or overridden. It is false at compile time. - * - * @hide - */ -public final class FusedOffFlag extends BooleanFlagBase { - /** - * @param namespace A namespace for this flag. See {@link DeviceConfig}. - * @param name A name for this flag. - */ - FusedOffFlag(String namespace, String name) { - super(namespace, name); - } - - @Override - @NonNull - public Boolean getDefault() { - return false; - } - - @Override - public FusedOffFlag defineMetaData(String label, String description, String categoryName) { - super.defineMetaData(label, description, categoryName); - return this; - } -} diff --git a/core/java/android/flags/FusedOnFlag.java b/core/java/android/flags/FusedOnFlag.java deleted file mode 100644 index e9adba7595c1..000000000000 --- a/core/java/android/flags/FusedOnFlag.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.flags; - -import android.annotation.NonNull; -import android.provider.DeviceConfig; - -/** - * A flag representing a true value. - * - * The flag can never be changed or overridden. It is true at compile time. - * - * @hide - */ -public final class FusedOnFlag extends BooleanFlagBase { - /** - * @param namespace A namespace for this flag. See {@link DeviceConfig}. - * @param name A name for this flag. - */ - FusedOnFlag(String namespace, String name) { - super(namespace, name); - } - - @Override - @NonNull - public Boolean getDefault() { - return true; - } - - @Override - public FusedOnFlag defineMetaData(String label, String description, String categoryName) { - super.defineMetaData(label, description, categoryName); - return this; - } -} diff --git a/core/java/android/flags/IFeatureFlags.aidl b/core/java/android/flags/IFeatureFlags.aidl deleted file mode 100644 index 3efcec97fe6d..000000000000 --- a/core/java/android/flags/IFeatureFlags.aidl +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - package android.flags; - -import android.flags.IFeatureFlagsCallback; -import android.flags.SyncableFlag; - -/** - * Binder interface for communicating with {@link com.android.server.flags.FeatureFlagsService}. - * - * This interface is used by {@link android.flags.FeatureFlags} and developers should use that to - * interface with the service. FeatureFlags is the "client" in this documentation. - * - * The methods allow client apps to communicate what flags they care about, and receive back - * current values for those flags. For stable flags, this is the finalized value until the device - * restarts. For {@link DynamicFlag}s, this is the last known value, though it may change in the - * future. Clients can listen for changes to flag values so that it can react accordingly. - * @hide - */ -interface IFeatureFlags { - /** - * Synchronize with the {@link com.android.server.flags.FeatureFlagsService} about flags of - * interest. - * - * The client should pass in a list of flags that it is using as {@link SyncableFlag}s, which - * includes what it thinks the default values of the flags are. - * - * The response will contain a list of matching SyncableFlags, whose values are set to what the - * value of the flags actually are. The client should update its internal state flag data to - * match. - * - * Generally speaking, if a flag that is passed in is new to the FeatureFlagsService, the - * service will cache the passed-in value, and return it back out. If, however, a different - * client has synced that flag with the service previously, FeatureFlagsService will return the - * existing cached value, which may or may not be what the current client passed in. This allows - * FeatureFlagsService to keep clients in agreement with one another. - */ - List<SyncableFlag> syncFlags(in List<SyncableFlag> flagList); - - /** - * Pass in an {@link IFeatureFlagsCallback} that will be called whenever a {@link DymamicFlag} - * changes. - */ - void registerCallback(IFeatureFlagsCallback callback); - - /** - * Remove a {@link IFeatureFlagsCallback} that was previously registered with - * {@link #registerCallback}. - */ - void unregisterCallback(IFeatureFlagsCallback callback); - - /** - * Query the {@link com.android.server.flags.FeatureFlagsService} for flags, but don't - * cache them. See {@link #syncFlags}. - * - * You almost certainly don't want this method. This is intended for the Flag Flipper - * application that needs to query the state of system but doesn't want to affect it by - * doing so. All other clients should use {@link syncFlags}. - */ - List<SyncableFlag> queryFlags(in List<SyncableFlag> flagList); - - /** - * Change a flags value in the system. - * - * This is intended for use by the Flag Flipper application. - */ - void overrideFlag(in SyncableFlag flag); - - /** - * Restore a flag to its default value. - * - * This is intended for use by the Flag Flipper application. - */ - void resetFlag(in SyncableFlag flag); -}
\ No newline at end of file diff --git a/core/java/android/flags/IFeatureFlagsCallback.aidl b/core/java/android/flags/IFeatureFlagsCallback.aidl deleted file mode 100644 index f708667ea4c4..000000000000 --- a/core/java/android/flags/IFeatureFlagsCallback.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - package android.flags; - -import android.flags.SyncableFlag; - -/** - * Callback for {@link IFeatureFlags#registerCallback} to get alerts when a {@link DynamicFlag} - * changes. - * - * DynamicFlags can change at run time. Stable flags will never result in a call to this method. - * - * @hide - */ -oneway interface IFeatureFlagsCallback { - void onFlagChange(in SyncableFlag flag); -}
\ No newline at end of file diff --git a/core/java/android/flags/OWNERS b/core/java/android/flags/OWNERS deleted file mode 100644 index fa125c4a159c..000000000000 --- a/core/java/android/flags/OWNERS +++ /dev/null @@ -1,7 +0,0 @@ -# Bug component: 1306523 - -mankoff@google.com -pixel@google.com - -dsandler@android.com - diff --git a/core/java/android/flags/SyncableFlag.java b/core/java/android/flags/SyncableFlag.java deleted file mode 100644 index 449bcc3c49f5..000000000000 --- a/core/java/android/flags/SyncableFlag.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.flags; - -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * @hide - */ -public final class SyncableFlag implements Parcelable { - private final String mNamespace; - private final String mName; - private final String mValue; - private final boolean mDynamic; - private final boolean mOverridden; - - public SyncableFlag( - @NonNull String namespace, - @NonNull String name, - @NonNull String value, - boolean dynamic) { - this(namespace, name, value, dynamic, false); - } - - public SyncableFlag( - @NonNull String namespace, - @NonNull String name, - @NonNull String value, - boolean dynamic, - boolean overridden - ) { - mNamespace = namespace; - mName = name; - mValue = value; - mDynamic = dynamic; - mOverridden = overridden; - } - - @NonNull - public String getNamespace() { - return mNamespace; - } - - @NonNull - public String getName() { - return mName; - } - - @NonNull - public String getValue() { - return mValue; - } - - public boolean isDynamic() { - return mDynamic; - } - - public boolean isOverridden() { - return mOverridden; - } - - @NonNull - public static final Parcelable.Creator<SyncableFlag> CREATOR = new Parcelable.Creator<>() { - public SyncableFlag createFromParcel(Parcel in) { - return new SyncableFlag( - in.readString(), - in.readString(), - in.readString(), - in.readBoolean(), - in.readBoolean()); - } - - public SyncableFlag[] newArray(int size) { - return new SyncableFlag[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(mNamespace); - dest.writeString(mName); - dest.writeString(mValue); - dest.writeBoolean(mDynamic); - dest.writeBoolean(mOverridden); - } - - @Override - public String toString() { - return getNamespace() + "." + getName() + "[" + getValue() + "]"; - } -} diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 99b297abe92a..0e45787c1340 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -283,7 +283,8 @@ public abstract class CameraDevice implements AutoCloseable { * @see StreamConfigurationMap#getInputFormats * @see StreamConfigurationMap#getInputSizes * @see StreamConfigurationMap#getValidOutputFormatsForInput - * @see StreamConfigurationMap#getOutputSizes + * @see StreamConfigurationMap#getOutputSizes(int) + * @see StreamConfigurationMap#getOutputSizes(Class) * @see android.media.ImageWriter * @see android.media.ImageReader * @deprecated Please use {@link diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index a098362f16aa..c2fe0800812f 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -130,14 +130,17 @@ public final class CameraManager { /** * Enable physical camera availability callbacks when the logical camera is unavailable * - * <p>Previously once a logical camera becomes unavailable, no {@link - * #onPhysicalCameraAvailable} or {@link #onPhysicalCameraUnavailable} will be called until - * the logical camera becomes available again. The results in the app opening the logical - * camera not able to receive physical camera availability change.</p> - * - * <p>With this change, the {@link #onPhysicalCameraAvailable} and {@link - * #onPhysicalCameraUnavailable} can still be called while the logical camera is unavailable. - * </p> + * <p>Previously once a logical camera becomes unavailable, no + * {@link AvailabilityCallback#onPhysicalCameraAvailable} or + * {@link AvailabilityCallback#onPhysicalCameraUnavailable} will + * be called until the logical camera becomes available again. The + * results in the app opening the logical camera not able to + * receive physical camera availability change.</p> + * + * <p>With this change, the {@link + * AvailabilityCallback#onPhysicalCameraAvailable} and {@link + * AvailabilityCallback#onPhysicalCameraUnavailable} can still be + * called while the logical camera is unavailable. </p> */ @ChangeId @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) diff --git a/core/java/android/hardware/camera2/package.html b/core/java/android/hardware/camera2/package.html index 719c2f620234..3fd5d7c60832 100644 --- a/core/java/android/hardware/camera2/package.html +++ b/core/java/android/hardware/camera2/package.html @@ -62,12 +62,28 @@ RAW buffers for {@link android.hardware.camera2.DngCreator} can be done with {@link android.media.ImageReader} with the {@link android.graphics.ImageFormat#JPEG} and {@link android.graphics.ImageFormat#RAW_SENSOR} formats. Application-driven -processing of camera data in RenderScript, OpenGL ES, or directly in -managed or native code is best done through {@link -android.renderscript.Allocation} with a YUV {@link -android.renderscript.Type}, {@link android.graphics.SurfaceTexture}, -and {@link android.media.ImageReader} with a {@link -android.graphics.ImageFormat#YUV_420_888} format, respectively.</p> +processing of camera data in OpenGL ES, or directly in managed or +native code is best done through {@link +android.graphics.SurfaceTexture}, or {@link android.media.ImageReader} +with a {@link android.graphics.ImageFormat#YUV_420_888} format, +respectively. </p> + +<p>By default, YUV-format buffers provided by the camera are using the +JFIF YUV<->RGB transform matrix (equivalent to Rec.601 full-range +encoding), and after conversion to RGB with this matrix, the resulting +RGB data is in the sRGB colorspace. Captured JPEG images may contain +an ICC profile to specify their color space information; if not, they +should be assumed to be in the sRGB space as well. On some devices, +the output colorspace can be changed via {@link +android.hardware.camera2.params.SessionConfiguration#setColorSpace}. +</p> +<p> +Note that although the YUV->RGB transform is the JFIF matrix (Rec.601 +full-range), due to legacy and compatibility reasons, the output is in +the sRGB colorspace, which uses the Rec.709 color primaries. Image +processing code can safely treat the output RGB as being in the sRGB +colorspace. +</p> <p>The application then needs to construct a {@link android.hardware.camera2.CaptureRequest}, which defines all the diff --git a/core/java/android/hardware/camera2/params/ColorSpaceProfiles.java b/core/java/android/hardware/camera2/params/ColorSpaceProfiles.java index 2e3af80f9cc0..bb154a96cbe9 100644 --- a/core/java/android/hardware/camera2/params/ColorSpaceProfiles.java +++ b/core/java/android/hardware/camera2/params/ColorSpaceProfiles.java @@ -192,7 +192,7 @@ public final class ColorSpaceProfiles { * @see OutputConfiguration#setDynamicRangeProfile * @see SessionConfiguration#setColorSpace * @see ColorSpace.Named - * @see DynamicRangeProfiles.Profile + * @see DynamicRangeProfiles */ public @NonNull Set<Long> getSupportedDynamicRangeProfiles(@NonNull ColorSpace.Named colorSpace, @ImageFormat.Format int imageFormat) { @@ -230,7 +230,7 @@ public final class ColorSpaceProfiles { * @see SessionConfiguration#setColorSpace * @see OutputConfiguration#setDynamicRangeProfile * @see ColorSpace.Named - * @see DynamicRangeProfiles.Profile + * @see DynamicRangeProfiles */ public @NonNull Set<ColorSpace.Named> getSupportedColorSpacesForDynamicRange( @ImageFormat.Format int imageFormat, diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java index 80db38fc9d8f..d4ce0ebbc528 100644 --- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java @@ -45,7 +45,7 @@ import java.util.Set; * Immutable class to store the recommended stream configurations to set up * {@link android.view.Surface Surfaces} for creating a * {@link android.hardware.camera2.CameraCaptureSession capture session} with - * {@link android.hardware.camera2.CameraDevice#createCaptureSession}. + * {@link android.hardware.camera2.CameraDevice#createCaptureSession(SessionConfiguration)}. * * <p>The recommended list does not replace or deprecate the exhaustive complete list found in * {@link StreamConfigurationMap}. It is a suggestion about available power and performance @@ -70,7 +70,7 @@ import java.util.Set; * }</code></pre> * * @see CameraCharacteristics#getRecommendedStreamConfigurationMap - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) */ public final class RecommendedStreamConfigurationMap { @@ -282,7 +282,7 @@ public final class RecommendedStreamConfigurationMap { /** * Determine whether or not output surfaces with a particular user-defined format can be passed - * {@link CameraDevice#createCaptureSession createCaptureSession}. + * {@link CameraDevice#createCaptureSession(SessionConfiguration) createCaptureSession}. * * <p> * For further information refer to {@link StreamConfigurationMap#isOutputSupportedFor}. @@ -292,7 +292,7 @@ public final class RecommendedStreamConfigurationMap { * @param format an image format from either {@link ImageFormat} or {@link PixelFormat} * @return * {@code true} if using a {@code surface} with this {@code format} will be - * supported with {@link CameraDevice#createCaptureSession} + * supported with {@link CameraDevice#createCaptureSession(SessionConfiguration)} * * @throws IllegalArgumentException * if the image format was not a defined named constant @@ -508,8 +508,10 @@ public final class RecommendedStreamConfigurationMap { } /** - * Determine whether or not the {@code surface} in its current state is suitable to be included - * in a {@link CameraDevice#createCaptureSession capture session} as an output. + * Determine whether or not the {@code surface} in its current + * state is suitable to be included in a {@link + * CameraDevice#createCaptureSession(SessionConfiguration) capture + * session} as an output. * * <p>For more information refer to {@link StreamConfigurationMap#isOutputSupportedFor}. * </p> diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java index 385f10719509..8f611a831204 100644 --- a/core/java/android/hardware/camera2/params/SessionConfiguration.java +++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java @@ -55,7 +55,7 @@ public final class SessionConfiguration implements Parcelable { * at regular non high speed FPS ranges and optionally {@link InputConfiguration} for * reprocessable sessions. * - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) * @see CameraDevice#createReprocessableCaptureSession */ public static final int SESSION_REGULAR = CameraDevice.SESSION_OPERATION_MODE_NORMAL; @@ -110,10 +110,7 @@ public final class SessionConfiguration implements Parcelable { * * @see #SESSION_REGULAR * @see #SESSION_HIGH_SPEED - * @see CameraDevice#createCaptureSession(List, CameraCaptureSession.StateCallback, Handler) - * @see CameraDevice#createCaptureSessionByOutputConfigurations - * @see CameraDevice#createReprocessableCaptureSession - * @see CameraDevice#createConstrainedHighSpeedCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) */ public SessionConfiguration(@SessionMode int sessionType, @NonNull List<OutputConfiguration> outputs, diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index aabe149d16f5..ef0db7f8a41c 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -41,7 +41,7 @@ import java.util.Set; * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP configurations} to set up * {@link android.view.Surface Surfaces} for creating a * {@link android.hardware.camera2.CameraCaptureSession capture session} with - * {@link android.hardware.camera2.CameraDevice#createCaptureSession}. + * {@link android.hardware.camera2.CameraDevice#createCaptureSession(SessionConfiguration)}. * <!-- TODO: link to input stream configuration --> * * <p>This is the authoritative list for all <!-- input/ -->output formats (and sizes respectively @@ -62,7 +62,7 @@ import java.util.Set; * }</code></pre> * * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) */ public final class StreamConfigurationMap { @@ -456,7 +456,7 @@ public final class StreamConfigurationMap { /** * Determine whether or not output surfaces with a particular user-defined format can be passed - * {@link CameraDevice#createCaptureSession createCaptureSession}. + * {@link CameraDevice#createCaptureSession(SessionConfiguration) createCaptureSession}. * * <p>This method determines that the output {@code format} is supported by the camera device; * each output {@code surface} target may or may not itself support that {@code format}. @@ -468,7 +468,7 @@ public final class StreamConfigurationMap { * @param format an image format from either {@link ImageFormat} or {@link PixelFormat} * @return * {@code true} iff using a {@code surface} with this {@code format} will be - * supported with {@link CameraDevice#createCaptureSession} + * supported with {@link CameraDevice#createCaptureSession(SessionConfiguration)} * * @throws IllegalArgumentException * if the image format was not a defined named constant @@ -476,7 +476,7 @@ public final class StreamConfigurationMap { * * @see ImageFormat * @see PixelFormat - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) */ public boolean isOutputSupportedFor(int format) { checkArgumentFormat(format); @@ -521,7 +521,7 @@ public final class StreamConfigurationMap { * * <p>Generally speaking this means that creating a {@link Surface} from that class <i>may</i> * provide a producer endpoint that is suitable to be used with - * {@link CameraDevice#createCaptureSession}.</p> + * {@link CameraDevice#createCaptureSession(SessionConfiguration)}.</p> * * <p>Since not all of the above classes support output of all format and size combinations, * the particular combination should be queried with {@link #isOutputSupportedFor(Surface)}.</p> @@ -531,7 +531,7 @@ public final class StreamConfigurationMap { * * @throws NullPointerException if {@code klass} was {@code null} * - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) * @see #isOutputSupportedFor(Surface) */ public static <T> boolean isOutputSupportedFor(Class<T> klass) { @@ -555,8 +555,10 @@ public final class StreamConfigurationMap { } /** - * Determine whether or not the {@code surface} in its current state is suitable to be included - * in a {@link CameraDevice#createCaptureSession capture session} as an output. + * Determine whether or not the {@code surface} in its current + * state is suitable to be included in a {@link + * CameraDevice#createCaptureSession(SessionConfiguration) capture + * session} as an output. * * <p>Not all surfaces are usable with the {@link CameraDevice}, and not all configurations * of that {@code surface} are compatible. Some classes that provide the {@code surface} are @@ -588,7 +590,7 @@ public final class StreamConfigurationMap { * @throws NullPointerException if {@code surface} was {@code null} * @throws IllegalArgumentException if the Surface endpoint is no longer valid * - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) * @see #isOutputSupportedFor(Class) */ public boolean isOutputSupportedFor(Surface surface) { @@ -623,14 +625,16 @@ public final class StreamConfigurationMap { } /** - * Determine whether or not the particular stream configuration is suitable to be included - * in a {@link CameraDevice#createCaptureSession capture session} as an output. + * Determine whether or not the particular stream configuration is + * suitable to be included in a {@link + * CameraDevice#createCaptureSession(SessionConfiguration) capture + * session} as an output. * * @param size stream configuration size * @param format stream configuration format * @return {@code true} if this is supported, {@code false} otherwise * - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) * @see #isOutputSupportedFor(Class) * @hide */ diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index a9c4818393a8..e472a40617ee 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -103,11 +103,10 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Xml; import android.util.proto.ProtoOutputStream; -import android.view.BatchedInputEventReceiver.SimpleBatchedInputEventReceiver; -import android.view.Choreographer; import android.view.Gravity; import android.view.InputChannel; import android.view.InputDevice; +import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -1052,17 +1051,22 @@ public class InputMethodService extends AbstractInputMethodService { stylusEvents.forEach(InputMethodService.this::onStylusHandwritingMotionEvent); // create receiver for channel - mHandwritingEventReceiver = new SimpleBatchedInputEventReceiver( - channel, - Looper.getMainLooper(), Choreographer.getInstance(), - event -> { + mHandwritingEventReceiver = new InputEventReceiver(channel, Looper.getMainLooper()) { + @Override + public void onInputEvent(InputEvent event) { + boolean handled = false; + try { if (!(event instanceof MotionEvent)) { - return false; + return; } onStylusHandwritingMotionEvent((MotionEvent) event); scheduleHandwritingSessionTimeout(); - return true; - }); + handled = true; + } finally { + finishInputEvent(event, handled); + } + } + }; scheduleHandwritingSessionTimeout(); } diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 7664bada2c28..e6bdfe1b95c4 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -112,17 +112,9 @@ public class GraphicsEnvironment { private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0; // Values for ANGLE_GL_DRIVER_SELECTION_VALUES - private enum AngleDriverChoice { - DEFAULT("default"), - ANGLE("angle"), - NATIVE("native"); - - public final String choice; - - AngleDriverChoice(String choice) { - this.choice = choice; - } - } + private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default"; + private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle"; + private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native"; private static final String PROPERTY_RO_ANGLE_SUPPORTED = "ro.gfx.angle.supported"; @@ -203,16 +195,15 @@ public class GraphicsEnvironment { } /** - * Query to determine the ANGLE driver choice. + * Query to determine if ANGLE should be used */ - private AngleDriverChoice queryAngleChoice(Context context, Bundle coreSettings, - String packageName) { + private boolean shouldUseAngle(Context context, Bundle coreSettings, String packageName) { if (TextUtils.isEmpty(packageName)) { Log.v(TAG, "No package name specified; use the system driver"); - return AngleDriverChoice.DEFAULT; + return false; } - return queryAngleChoiceInternal(context, coreSettings, packageName); + return shouldUseAngleInternal(context, coreSettings, packageName); } private int getVulkanVersion(PackageManager pm) { @@ -433,11 +424,10 @@ public class GraphicsEnvironment { * forces a choice; * 3) Use ANGLE if isAngleEnabledByGameMode() returns true; */ - private AngleDriverChoice queryAngleChoiceInternal(Context context, Bundle bundle, - String packageName) { + private boolean shouldUseAngleInternal(Context context, Bundle bundle, String packageName) { // Make sure we have a good package name if (TextUtils.isEmpty(packageName)) { - return AngleDriverChoice.DEFAULT; + return false; } // Check the semi-global switch (i.e. once system has booted enough) for whether ANGLE @@ -452,7 +442,7 @@ public class GraphicsEnvironment { } if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) { Log.v(TAG, "Turn on ANGLE for all applications."); - return AngleDriverChoice.ANGLE; + return true; } // Get the per-application settings lists @@ -475,7 +465,7 @@ public class GraphicsEnvironment { + optInPackages.size() + ", " + "number of values: " + optInValues.size()); - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } // See if this application is listed in the per-application settings list @@ -483,7 +473,7 @@ public class GraphicsEnvironment { if (pkgIndex < 0) { Log.v(TAG, packageName + " is not listed in per-application setting"); - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } mAngleOptInIndex = pkgIndex; @@ -493,14 +483,14 @@ public class GraphicsEnvironment { Log.v(TAG, "ANGLE Developer option for '" + packageName + "' " + "set to: '" + optInValue + "'"); - if (optInValue.equals(AngleDriverChoice.ANGLE.choice)) { - return AngleDriverChoice.ANGLE; - } else if (optInValue.equals(AngleDriverChoice.NATIVE.choice)) { - return AngleDriverChoice.NATIVE; + if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) { + return true; + } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) { + return false; } else { // The user either chose default or an invalid value; go with the default driver or what // the game mode indicates - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } } @@ -568,13 +558,7 @@ public class GraphicsEnvironment { private boolean setupAngle(Context context, Bundle bundle, PackageManager packageManager, String packageName) { - final AngleDriverChoice angleDriverChoice = queryAngleChoice(context, bundle, packageName); - if (angleDriverChoice == AngleDriverChoice.DEFAULT) { - return false; - } - - if (queryAngleChoice(context, bundle, packageName) == AngleDriverChoice.NATIVE) { - nativeSetAngleInfo("", true, packageName, null); + if (!shouldUseAngle(context, bundle, packageName)) { return false; } @@ -643,10 +627,10 @@ public class GraphicsEnvironment { Log.d(TAG, "ANGLE package libs: " + paths); } - // If we make it to here, ANGLE apk will be used. Call nativeSetAngleInfo() with the - // application package name and ANGLE features to use. + // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name, + // and features to use. final String[] features = getAngleEglFeatures(context, bundle); - nativeSetAngleInfo(paths, false, packageName, features); + setAngleInfo(paths, false, packageName, features); return true; } @@ -668,10 +652,10 @@ public class GraphicsEnvironment { return false; } - // If we make it to here, system ANGLE will be used. Call nativeSetAngleInfo() with - // the application package name and ANGLE features to use. + // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name, + // and features to use. final String[] features = getAngleEglFeatures(context, bundle); - nativeSetAngleInfo("system", false, packageName, features); + setAngleInfo("", true, packageName, features); return true; } @@ -952,8 +936,8 @@ public class GraphicsEnvironment { private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries); private static native void setGpuStats(String driverPackageName, String driverVersionName, long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion); - private static native void nativeSetAngleInfo(String path, boolean useNativeDriver, - String packageName, String[] features); + private static native void setAngleInfo(String path, boolean useSystemAngle, String packageName, + String[] features); private static native boolean setInjectLayersPrSetDumpable(); private static native void nativeToggleAngleAsSystemDriver(boolean enabled); diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 0071a0d00105..827600c83fae 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -149,6 +149,13 @@ public class FeatureFlagUtils { */ public static final String SETTINGS_BIOMETRICS2_ENROLLMENT = "settings_biometrics2_enrollment"; + /** + * Flag to enable/disable FingerprintSettings v2 + * @hide + */ + public static final String SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS = + "settings_biometrics2_fingerprint"; + /** Flag to enable/disable entire page in Accessibility -> Hearing aids * @hide */ @@ -239,6 +246,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "true"); DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "true"); DEFAULT_FLAGS.put(SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, "true"); + DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS, "false"); } private static final Set<String> PERSISTENT_FLAGS; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f1cde3b4bb7e..2499be999f8d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -21821,6 +21821,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mCurrentAnimation = null; if ((mViewFlags & TOOLTIP) == TOOLTIP) { + removeCallbacks(mTooltipInfo.mShowTooltipRunnable); + removeCallbacks(mTooltipInfo.mHideTooltipRunnable); hideTooltip(); } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 3f308e6fccee..b3233141f126 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -50,6 +50,7 @@ import android.annotation.TestApi; import android.annotation.UiThread; import android.annotation.UserIdInt; import android.app.ActivityThread; +import android.app.PropertyInvalidatedCache; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; @@ -536,6 +537,13 @@ public final class InputMethodManager { @UnsupportedAppUsage Rect mCursorRect = new Rect(); + /** Cached value for {@link #isStylusHandwritingAvailable} for userId. */ + @GuardedBy("mH") + private PropertyInvalidatedCache<Integer, Boolean> mStylusHandwritingAvailableCache; + + private static final String CACHE_KEY_STYLUS_HANDWRITING_PROPERTY = + "cache_key.system_server.stylus_handwriting"; + @GuardedBy("mH") private int mCursorSelStart; @GuardedBy("mH") @@ -662,6 +670,15 @@ public final class InputMethodManager { private static final int MSG_UPDATE_VIRTUAL_DISPLAY_TO_SCREEN_MATRIX = 30; private static final int MSG_ON_SHOW_REQUESTED = 31; + /** + * Calling this will invalidate Local stylus handwriting availability Cache which + * forces the next query in any process to recompute the cache. + * @hide + */ + public static void invalidateLocalStylusHandwritingAvailabilityCaches() { + PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STYLUS_HANDWRITING_PROPERTY); + } + private static boolean isAutofillUIShowing(View servedView) { AutofillManager afm = servedView.getContext().getSystemService(AutofillManager.class); return afm != null && afm.isAutofillUiShowing(); @@ -1577,8 +1594,21 @@ public final class InputMethodManager { if (fallbackContext == null) { return false; } - - return IInputMethodManagerGlobalInvoker.isStylusHandwritingAvailableAsUser(userId); + boolean isAvailable; + synchronized (mH) { + if (mStylusHandwritingAvailableCache == null) { + mStylusHandwritingAvailableCache = new PropertyInvalidatedCache<>( + 4 /* maxEntries */, CACHE_KEY_STYLUS_HANDWRITING_PROPERTY) { + @Override + public Boolean recompute(Integer userId) { + return IInputMethodManagerGlobalInvoker.isStylusHandwritingAvailableAsUser( + userId); + } + }; + } + isAvailable = mStylusHandwritingAvailableCache.query(userId); + } + return isAvailable; } /** diff --git a/core/java/android/widget/RemoteViews.aidl b/core/java/android/widget/RemoteViews.aidl index ec86410cf89c..6a5fc03fcc6e 100644 --- a/core/java/android/widget/RemoteViews.aidl +++ b/core/java/android/widget/RemoteViews.aidl @@ -17,3 +17,4 @@ package android.widget; parcelable RemoteViews; +parcelable RemoteViews.RemoteCollectionItems; diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index bd7f5a0924cc..d9e76fefad7f 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -33,16 +33,19 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityOptions; import android.app.ActivityThread; +import android.app.AppGlobals; import android.app.Application; import android.app.LoadedApk; import android.app.PendingIntent; import android.app.RemoteInput; import android.appwidget.AppWidgetHostView; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentSender; +import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.ColorStateList; @@ -65,10 +68,12 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; +import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.Process; +import android.os.RemoteException; import android.os.StrictMode; import android.os.UserHandle; import android.system.Os; @@ -98,8 +103,10 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.CompoundButton.OnCheckedChangeListener; import com.android.internal.R; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.util.ContrastColorUtil; import com.android.internal.util.Preconditions; +import com.android.internal.widget.IRemoteViewsFactory; import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; @@ -124,7 +131,9 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Stack; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; @@ -324,6 +333,13 @@ public class RemoteViews implements Parcelable, Filter { (clazz) -> clazz.isAnnotationPresent(RemoteViews.RemoteView.class); /** + * The maximum waiting time for remote adapter conversion in milliseconds + * + * @hide + */ + private static final int MAX_ADAPTER_CONVERSION_WAITING_TIME_MS = 2000; + + /** * Application that hosts the remote views. * * @hide @@ -1042,28 +1058,96 @@ public class RemoteViews implements Parcelable, Filter { } private class SetRemoteCollectionItemListAdapterAction extends Action { - private final RemoteCollectionItems mItems; + private @NonNull CompletableFuture<RemoteCollectionItems> mItemsFuture; - SetRemoteCollectionItemListAdapterAction(@IdRes int id, RemoteCollectionItems items) { + SetRemoteCollectionItemListAdapterAction(@IdRes int id, + @NonNull RemoteCollectionItems items) { viewId = id; - mItems = items; - mItems.setHierarchyRootData(getHierarchyRootData()); + items.setHierarchyRootData(getHierarchyRootData()); + mItemsFuture = CompletableFuture.completedFuture(items); + } + + SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) { + viewId = id; + mItemsFuture = getItemsFutureFromIntentWithTimeout(intent); + setHierarchyRootData(getHierarchyRootData()); + } + + private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout( + Intent intent) { + if (intent == null) { + Log.e(LOG_TAG, "Null intent received when generating adapter future"); + return CompletableFuture.completedFuture(new RemoteCollectionItems + .Builder().build()); + } + + final Context context = ActivityThread.currentApplication(); + final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>(); + + context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE), + result.defaultExecutor(), new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName componentName, + IBinder iBinder) { + RemoteCollectionItems items; + try { + items = IRemoteViewsFactory.Stub.asInterface(iBinder) + .getRemoteCollectionItems(); + } catch (RemoteException re) { + items = new RemoteCollectionItems.Builder().build(); + Log.e(LOG_TAG, "Error getting collection items from the factory", + re); + } finally { + context.unbindService(this); + } + + result.complete(items); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { } + }); + + result.completeOnTimeout( + new RemoteCollectionItems.Builder().build(), + MAX_ADAPTER_CONVERSION_WAITING_TIME_MS, TimeUnit.MILLISECONDS); + + return result; } SetRemoteCollectionItemListAdapterAction(Parcel parcel) { viewId = parcel.readInt(); - mItems = new RemoteCollectionItems(parcel, getHierarchyRootData()); + mItemsFuture = CompletableFuture.completedFuture( + new RemoteCollectionItems(parcel, getHierarchyRootData())); } @Override public void setHierarchyRootData(HierarchyRootData rootData) { - mItems.setHierarchyRootData(rootData); + mItemsFuture = mItemsFuture + .thenApply(rc -> { + rc.setHierarchyRootData(rootData); + return rc; + }); + } + + private static RemoteCollectionItems getCollectionItemsFromFuture( + CompletableFuture<RemoteCollectionItems> itemsFuture) { + RemoteCollectionItems items; + try { + items = itemsFuture.get(); + } catch (Exception e) { + Log.e(LOG_TAG, "Error getting collection items from future", e); + items = new RemoteCollectionItems.Builder().build(); + } + + return items; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); - mItems.writeToParcel(dest, flags, /* attached= */ true); + RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); + items.writeToParcel(dest, flags, /* attached= */ true); } @Override @@ -1072,6 +1156,8 @@ public class RemoteViews implements Parcelable, Filter { View target = root.findViewById(viewId); if (target == null) return; + RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); + // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { Log.e(LOG_TAG, "setRemoteAdapter can only be used for " @@ -1092,10 +1178,10 @@ public class RemoteViews implements Parcelable, Filter { // recycling in setAdapter, so we must call setAdapter again if the number of view types // increases. if (adapter instanceof RemoteCollectionItemsAdapter - && adapter.getViewTypeCount() >= mItems.getViewTypeCount()) { + && adapter.getViewTypeCount() >= items.getViewTypeCount()) { try { ((RemoteCollectionItemsAdapter) adapter).setData( - mItems, params.handler, params.colorResources); + items, params.handler, params.colorResources); } catch (Throwable throwable) { // setData should never failed with the validation in the items builder, but if // it does, catch and rethrow. @@ -1105,7 +1191,7 @@ public class RemoteViews implements Parcelable, Filter { } try { - adapterView.setAdapter(new RemoteCollectionItemsAdapter(mItems, + adapterView.setAdapter(new RemoteCollectionItemsAdapter(items, params.handler, params.colorResources)); } catch (Throwable throwable) { // This could throw if the AdapterView somehow doesn't accept BaseAdapter due to @@ -4679,6 +4765,12 @@ public class RemoteViews implements Parcelable, Filter { * providing data to the RemoteViewsAdapter */ public void setRemoteAdapter(@IdRes int viewId, Intent intent) { + if (AppGlobals.getIntCoreSetting( + SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION, + SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT ? 1 : 0) == 1) { + addAction(new SetRemoteCollectionItemListAdapterAction(viewId, intent)); + return; + } addAction(new SetRemoteViewsAdapterIntent(viewId, intent)); } diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java index 214e5cc01b9e..d4f4d19e9bad 100644 --- a/core/java/android/widget/RemoteViewsService.java +++ b/core/java/android/widget/RemoteViewsService.java @@ -43,6 +43,13 @@ public abstract class RemoteViewsService extends Service { private static final Object sLock = new Object(); /** + * Used for determining the maximum number of entries to retrieve from RemoteViewsFactory + * + * @hide + */ + private static final int MAX_NUM_ENTRY = 25; + + /** * An interface for an adapter between a remote collection view (ListView, GridView, etc) and * the underlying data for that view. The implementor is responsible for making a RemoteView * for each item in the data set. This interface is a thin wrapper around {@link Adapter}. @@ -227,6 +234,30 @@ public abstract class RemoteViewsService extends Service { } } + @Override + public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() { + RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems + .Builder().build(); + + try { + RemoteViews.RemoteCollectionItems.Builder itemsBuilder = + new RemoteViews.RemoteCollectionItems.Builder(); + mFactory.onDataSetChanged(); + + itemsBuilder.setHasStableIds(mFactory.hasStableIds()); + final int numOfEntries = Math.min(mFactory.getCount(), MAX_NUM_ENTRY); + for (int i = 0; i < numOfEntries; i++) { + itemsBuilder.addItem(mFactory.getItemId(i), mFactory.getViewAt(i)); + } + + items = itemsBuilder.build(); + } catch (Exception ex) { + Thread t = Thread.currentThread(); + Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); + } + return items; + } + private RemoteViewsFactory mFactory; private boolean mIsCreated; } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index b65c1a17e26b..cb5dbe6c5618 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -1550,6 +1550,7 @@ public class ScrollView extends FrameLayout { float deltaDistance = -unconsumed * FLING_DESTRETCH_FACTOR / size; int consumed = Math.round(-size / FLING_DESTRETCH_FACTOR * mEdgeGlowTop.onPullDistance(deltaDistance, 0.5f)); + mEdgeGlowTop.onRelease(); if (consumed != unconsumed) { mEdgeGlowTop.finish(); } @@ -1560,6 +1561,7 @@ public class ScrollView extends FrameLayout { float deltaDistance = unconsumed * FLING_DESTRETCH_FACTOR / size; int consumed = Math.round(size / FLING_DESTRETCH_FACTOR * mEdgeGlowBottom.onPullDistance(deltaDistance, 0.5f)); + mEdgeGlowBottom.onRelease(); if (consumed != unconsumed) { mEdgeGlowBottom.finish(); } diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java index 413f0ccbc275..e153bb70a7ca 100644 --- a/core/java/android/window/TaskFragmentOperation.java +++ b/core/java/android/window/TaskFragmentOperation.java @@ -73,6 +73,13 @@ public final class TaskFragmentOperation implements Parcelable { /** Sets the relative bounds with {@link WindowContainerTransaction#setRelativeBounds}. */ public static final int OP_TYPE_SET_RELATIVE_BOUNDS = 9; + /** + * Reorders the TaskFragment to be the front-most TaskFragment in the Task. + * Note that there could still have other WindowContainer on top of the front-most + * TaskFragment, such as a non-embedded Activity. + */ + public static final int OP_TYPE_REORDER_TO_FRONT = 10; + @IntDef(prefix = { "OP_TYPE_" }, value = { OP_TYPE_UNKNOWN, OP_TYPE_CREATE_TASK_FRAGMENT, @@ -84,7 +91,8 @@ public final class TaskFragmentOperation implements Parcelable { OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT, OP_TYPE_SET_COMPANION_TASK_FRAGMENT, OP_TYPE_SET_ANIMATION_PARAMS, - OP_TYPE_SET_RELATIVE_BOUNDS + OP_TYPE_SET_RELATIVE_BOUNDS, + OP_TYPE_REORDER_TO_FRONT }) @Retention(RetentionPolicy.SOURCE) public @interface OperationType {} diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 9ffccb34f44d..0ba271f253fb 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -532,6 +532,17 @@ public final class SystemUiDeviceConfigFlags { public static final String TASK_MANAGER_SHOW_FOOTER_DOT = "task_manager_show_footer_dot"; /** + * (boolean) Whether to enable the adapter conversion in RemoteViews + */ + public static final String REMOTEVIEWS_ADAPTER_CONVERSION = "remoteviews_adapter_conversion"; + + /** + * Default value for whether the adapter conversion is enabled or not. This is set for + * RemoteViews and should not be a common practice. + */ + public static final boolean REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT = false; + + /** * (boolean) Whether the task manager should show a stop button if the app is allowlisted * by the user. */ diff --git a/core/java/com/android/internal/flags/CoreFlags.java b/core/java/com/android/internal/flags/CoreFlags.java deleted file mode 100644 index f177ef88c38f..000000000000 --- a/core/java/com/android/internal/flags/CoreFlags.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.flags; - -import android.flags.BooleanFlag; -import android.flags.DynamicBooleanFlag; -import android.flags.FeatureFlags; -import android.flags.FusedOffFlag; -import android.flags.FusedOnFlag; -import android.flags.SyncableFlag; - -import java.util.ArrayList; -import java.util.List; - -/** - * Flags defined here are can be read by code in core. - * - * Flags not defined here will throw a security exception if third-party processes attempts to read - * them. - * - * DO NOT define a flag here unless you explicitly intend for that flag to be readable by code that - * runs inside a third party process. - */ -public abstract class CoreFlags { - private static final List<SyncableFlag> sKnownFlags = new ArrayList<>(); - - public static BooleanFlag BOOL_FLAG = booleanFlag("core", "bool_flag", false); - public static FusedOffFlag OFF_FLAG = fusedOffFlag("core", "off_flag"); - public static FusedOnFlag ON_FLAG = fusedOnFlag("core", "on_flag"); - public static DynamicBooleanFlag DYN_FLAG = dynamicBooleanFlag("core", "dyn_flag", true); - - /** Returns true if the passed in flag matches a flag in this class. */ - public static boolean isCoreFlag(SyncableFlag flag) { - for (SyncableFlag knownFlag : sKnownFlags) { - if (knownFlag.getName().equals(flag.getName()) - && knownFlag.getNamespace().equals(flag.getNamespace())) { - return true; - } - } - return false; - } - - public static List<SyncableFlag> getCoreFlags() { - return sKnownFlags; - } - - private static BooleanFlag booleanFlag(String namespace, String name, boolean defaultValue) { - BooleanFlag f = FeatureFlags.booleanFlag(namespace, name, defaultValue); - - sKnownFlags.add(new SyncableFlag(namespace, name, Boolean.toString(defaultValue), false)); - - return f; - } - - private static FusedOffFlag fusedOffFlag(String namespace, String name) { - FusedOffFlag f = FeatureFlags.fusedOffFlag(namespace, name); - - sKnownFlags.add(new SyncableFlag(namespace, name, "false", false)); - - return f; - } - - private static FusedOnFlag fusedOnFlag(String namespace, String name) { - FusedOnFlag f = FeatureFlags.fusedOnFlag(namespace, name); - - sKnownFlags.add(new SyncableFlag(namespace, name, "true", false)); - - return f; - } - - private static DynamicBooleanFlag dynamicBooleanFlag( - String namespace, String name, boolean defaultValue) { - DynamicBooleanFlag f = FeatureFlags.dynamicBooleanFlag(namespace, name, defaultValue); - - sKnownFlags.add(new SyncableFlag(namespace, name, Boolean.toString(defaultValue), true)); - - return f; - } -} diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl index c06dab9f75d6..918d9c029ef5 100644 --- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl +++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl @@ -39,5 +39,6 @@ interface IRemoteViewsFactory { boolean hasStableIds(); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) boolean isCreated(); + RemoteViews.RemoteCollectionItems getRemoteCollectionItems(); } diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS index d068a3a0f62e..e2672f5b03ba 100644 --- a/core/java/com/android/internal/widget/OWNERS +++ b/core/java/com/android/internal/widget/OWNERS @@ -21,3 +21,6 @@ per-file ImageFloatingTextView.java = file:/services/core/java/com/android/serve per-file ObservableTextView.java = file:/services/core/java/com/android/server/notification/OWNERS per-file RemeasuringLinearLayout.java = file:/services/core/java/com/android/server/notification/OWNERS per-file ViewClippingUtil.java = file:/services/core/java/com/android/server/notification/OWNERS + +# Appwidget related +per-file *RemoteViews* = file:/services/appwidget/java/com/android/server/appwidget/OWNERS diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 8fc30d1c248d..afc3cbd15f88 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -50,7 +50,7 @@ void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName, appPackageNameChars.c_str(), vulkanVersion); } -void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useNativeDriver, +void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useSystemAngle, jstring packageName, jobjectArray featuresObj) { ScopedUtfChars pathChars(env, path); ScopedUtfChars packageNameChars(env, packageName); @@ -73,7 +73,7 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useN } } - android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useNativeDriver, + android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useSystemAngle, packageNameChars.c_str(), features); } @@ -118,7 +118,7 @@ const JNINativeMethod g_methods[] = { reinterpret_cast<void*>(setGpuStats_native)}, {"setInjectLayersPrSetDumpable", "()Z", reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)}, - {"nativeSetAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V", + {"setAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V", reinterpret_cast<void*>(setAngleInfo_native)}, {"setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native)}, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 10cf353bf5e9..a01c7b6355c1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -7655,24 +7655,6 @@ <permission android:name="android.permission.GET_ANY_PROVIDER_TYPE" android:protectionLevel="signature" /> - - <!-- @hide Allows internal applications to read and synchronize non-core flags. - Apps without this permission can only read a subset of flags specifically intended - for use in "core", (i.e. third party apps). Apps with this permission can define their - own flags, and federate those values with other system-level apps. - <p>Not for use by third-party applications. - <p>Protection level: signature - --> - <permission android:name="android.permission.SYNC_FLAGS" - android:protectionLevel="signature" /> - - <!-- @hide Allows internal applications to override flags in the FeatureFlags service. - <p>Not for use by third-party applications. - <p>Protection level: signature - --> - <permission android:name="android.permission.WRITE_FLAGS" - android:protectionLevel="signature" /> - <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index f5d73f9021c0..208d5a6d6973 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Kan nie toegang tot die foon se kamera op jou <xliff:g id="DEVICE">%1$s</xliff:g> kry nie"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Kan nie toegang tot die tablet se kamera op jou <xliff:g id="DEVICE">%1$s</xliff:g> kry nie"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Jy kan nie toegang hiertoe kry terwyl daar gestroom word nie. Probeer eerder op jou foon."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Kan nie prent-in-prent sien terwyl jy stroom nie"</string> <string name="system_locale_title" msgid="711882686834677268">"Stelselverstek"</string> <string name="default_card_name" msgid="9198284935962911468">"KAART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Metgeselhorlosieprofiel se toestemming om horlosies te bestuur"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 9b306818356f..231f4b2a0987 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"የስልኩን ካሜራ ከእርስዎ <xliff:g id="DEVICE">%1$s</xliff:g> መድረስ አይቻልም"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ጡባዊውን ካሜራ ከእርስዎ <xliff:g id="DEVICE">%1$s</xliff:g> መድረስ አይቻልም"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ዥረት በመልቀቅ ላይ ሳለ ይህ ሊደረስበት አይችልም። በምትኩ በስልክዎ ላይ ይሞክሩ።"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"በዥረት በመልቀቅ ወቅት በሥዕል-ላይ-ሥዕል ማየት አይችሉም"</string> <string name="system_locale_title" msgid="711882686834677268">"የሥርዓት ነባሪ"</string> <string name="default_card_name" msgid="9198284935962911468">"ካርድ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"የእጅ ሰዓቶችን ለማስተዳደር የአጃቢ የእጅ ሰዓት መገለጫ ፍቃድ"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index f480d3a0581e..c71ee6354323 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -2315,7 +2315,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"يتعذّر الوصول إلى كاميرا الهاتف من على جهاز <xliff:g id="DEVICE">%1$s</xliff:g>."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"يتعذّر الوصول إلى كاميرا الجهاز اللوحي من على جهاز <xliff:g id="DEVICE">%1$s</xliff:g>."</string> <string name="vdm_secure_window" msgid="161700398158812314">"لا يمكن الوصول إلى هذا المحتوى أثناء البث. بدلاً من ذلك، جرِّب استخدام هاتفك."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"لا يمكن عرض نافذة ضمن النافذة أثناء البث."</string> <string name="system_locale_title" msgid="711882686834677268">"الإعداد التلقائي للنظام"</string> <string name="default_card_name" msgid="9198284935962911468">"رقم البطاقة <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"إذن الملف الشخصي في Companion Watch لإدارة الساعات"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 15563c177eeb..4c63a20c96f7 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"আপোনাৰ <xliff:g id="DEVICE">%1$s</xliff:g>ৰ পৰা ফ’নটোৰ কেমেৰা এক্সেছ কৰিব নোৱাৰি"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"আপোনাৰ <xliff:g id="DEVICE">%1$s</xliff:g>ৰ পৰা টেবলেটটোৰ কেমেৰা এক্সেছ কৰিব নোৱাৰি"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ষ্ট্ৰীম কৰি থকাৰ সময়ত এইটো এক্সেছ কৰিব নোৱাৰি। তাৰ পৰিৱৰ্তে আপোনাৰ ফ’নত চেষ্টা কৰি চাওক।"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ষ্ট্ৰীম কৰি থকাৰ সময়ত picture-in-picture চাব নোৱাৰি"</string> <string name="system_locale_title" msgid="711882686834677268">"ছিষ্টেম ডিফ’ল্ট"</string> <string name="default_card_name" msgid="9198284935962911468">"কাৰ্ড <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ঘড়ী পৰিচালনা কৰিবলৈ সহযোগী ঘড়ীৰ প্ৰ’ফাইলৰ অনুমতি"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index d7bec0613265..6a9f5fed9f51 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan telefonun kamerasına giriş etmək olmur"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan planşetin kamerasına giriş etmək olmur"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Yayım zamanı buna giriş mümkün deyil. Telefonunuzda sınayın."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Yayım zamanı şəkildə şəkilə baxmaq mümkün deyil"</string> <string name="system_locale_title" msgid="711882686834677268">"Sistem defoltu"</string> <string name="default_card_name" msgid="9198284935962911468">"KART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Saatları idarə etmək üçün Kompanyon Saat profili icazəsi"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index a6254f628f02..8e09389b182a 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ne može da se pristupi kameri telefona sa <xliff:g id="DEVICE">%1$s</xliff:g> uređaja"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ne može da se pristupi kameri tableta sa <xliff:g id="DEVICE">%1$s</xliff:g> uređaja"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Ovom ne možete da pristupate tokom strimovanja. Probajte na telefonu."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Ne možete da gledate sliku u slici pri strimovanju"</string> <string name="system_locale_title" msgid="711882686834677268">"Podrazumevani sistemski"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTICA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Dozvola za profil pratećeg sata za upravljanje satovima"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 624de9c04c47..538e1267fb44 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -2313,7 +2313,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не ўдалося атрымаць доступ да камеры тэлефона з прылады \"<xliff:g id="DEVICE">%1$s</xliff:g>\""</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не ўдалося атрымаць доступ да камеры планшэта з прылады \"<xliff:g id="DEVICE">%1$s</xliff:g>\""</string> <string name="vdm_secure_window" msgid="161700398158812314">"Не ўдаецца атрымаць доступ у час перадачы плынню. Паспрабуйце скарыстаць тэлефон."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Падчас перадачы плынню прагляд у рэжыме \"Відарыс у відарысе\" немагчымы"</string> <string name="system_locale_title" msgid="711882686834677268">"Стандартная сістэмная налада"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Дазвол для спадарожнай праграмы кіраваць гадзіннікамі"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index a71c0af44f37..9b00b2ca70c8 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Няма достъп до камерата на телефона от вашия <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Няма достъп до камерата на таблета от вашия <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"До това съдържание не може да се осъществи достъп при поточно предаване. Вместо това опитайте от телефона си."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Функцията „Картина в картината“ не е налице при поточно предаване"</string> <string name="system_locale_title" msgid="711882686834677268">"Стандартно за системата"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Разрешение на придружаващото приложение за достъп до потребителския профил на часовника с цел управление на часовници"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index ea0b94951fcc..bf5ad8d15e8c 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"আপনার <xliff:g id="DEVICE">%1$s</xliff:g> থেকে ফোনের ক্যামেরা অ্যাক্সেস করা যাচ্ছে না"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"আপনার <xliff:g id="DEVICE">%1$s</xliff:g> থেকে ট্যাবলেটের ক্যামেরা অ্যাক্সেস করা যাচ্ছে না"</string> <string name="vdm_secure_window" msgid="161700398158812314">"স্ট্রিমিংয়ের সময় এটি অ্যাক্সেস করা যাবে না। পরিবর্তে আপনার ফোনে ব্যবহার করে দেখুন।"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"স্ট্রিম করার সময় \'ছবির-মধ্যে-ছবি\' দেখা যাবে না"</string> <string name="system_locale_title" msgid="711882686834677268">"সিস্টেম ডিফল্ট"</string> <string name="default_card_name" msgid="9198284935962911468">"কার্ড <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ওয়াচ ম্যানেজ করতে, কম্প্যানিয়ন ওয়াচ প্রোফাইল সংক্রান্ত অনুমতি"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index afafa1c9a9df..ab8789c701d1 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nije moguće pristupiti kameri telefona s uređaja <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nije moguće pristupiti kameri tableta s uređaja <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Ovom ne možete pristupiti tokom prijenosa. Umjesto toga pokušajte na telefonu."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Tokom prijenosa nije moguće gledati sliku u slici"</string> <string name="system_locale_title" msgid="711882686834677268">"Sistemski zadano"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTICA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Odobrenje za profil pratećeg sata da upravlja satovima"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 5264557a227a..ac37eb15d168 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"No es pot accedir a la càmera del telèfon des del teu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"No es pot accedir a la càmera de la tauleta des del teu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"No s\'hi pot accedir mentre s\'està reproduint en continu. Prova-ho al telèfon."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"No es pot veure el mode d\'imatge sobre imatge durant la reproducció en continu"</string> <string name="system_locale_title" msgid="711882686834677268">"Valor predeterminat del sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"TARGETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permís del perfil del rellotge perquè l\'aplicació complementària gestioni els rellotges"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 4510eff9a321..a517a18440b8 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -2313,7 +2313,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ze zařízení <xliff:g id="DEVICE">%1$s</xliff:g> nelze získat přístup k fotoaparátu telefonu"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ze zařízení <xliff:g id="DEVICE">%1$s</xliff:g> nelze získat přístup k fotoaparátu tabletu"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Tento obsah při streamování nelze zobrazit. Zkuste to na telefonu."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Během streamování nelze zobrazit obraz v obraze"</string> <string name="system_locale_title" msgid="711882686834677268">"Výchozí nastavení systému"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Oprávnění profilu doprovodných hodinek ke správě hodinek"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 2f6253120a86..01ecd96625ad 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Kameraet på din telefon kan ikke tilgås via din <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Kameraet på din tablet kan ikke tilgås via din <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Der er ikke adgang til dette indhold under streaming. Prøv på din telefon i stedet."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Funktionen Integreret billede er ikke tilgængelig, når der streames"</string> <string name="system_locale_title" msgid="711882686834677268">"Systemstandard"</string> <string name="default_card_name" msgid="9198284935962911468">"KORT <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Tilladelse til at administrere ure for urprofilens medfølgende app"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 713a62f741aa..4430ef2f1217 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Zugriff auf die Kamera des Smartphones über dein Gerät (<xliff:g id="DEVICE">%1$s</xliff:g>) nicht möglich"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Zugriff auf die Kamera des Tablets über dein Gerät (<xliff:g id="DEVICE">%1$s</xliff:g>) nicht möglich"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Während des Streamings ist kein Zugriff möglich. Versuch es stattdessen auf deinem Smartphone."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Funktion „Bild im Bild“ kann beim Streamen nicht verwendet werden"</string> <string name="system_locale_title" msgid="711882686834677268">"Standardeinstellung des Systems"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTE <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Berechtigung für Companion-Smartwatch-Profil zum Verwalten von Smartwatches"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index a58b78028042..d9051a4f54ba 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Δεν είναι δυνατή η πρόσβαση στην κάμερα τηλεφώνου από το <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Δεν είναι δυνατή η πρόσβαση στην κάμερα του tablet από τη συσκευή <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Δεν είναι δυνατή η πρόσβαση σε αυτό το στοιχείο κατά τη ροή. Δοκιμάστε στο τηλέφωνό σας."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Δεν είναι δυνατή η προβολή picture-in-picture κατά τη ροή"</string> <string name="system_locale_title" msgid="711882686834677268">"Προεπιλογή συστήματος"</string> <string name="default_card_name" msgid="9198284935962911468">"ΚΑΡΤΑ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Άδεια προφίλ συνοδευτικής εφαρμογής ρολογιού για τη διαχείριση ρολογιών"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 2a5847c911d9..1feb601d1ce5 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Can’t access the phone’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Can’t access the tablet’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"This can’t be accessed while streaming. Try on your phone instead."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Can’t view picture-in-picture while streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"System default"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Companion watch profile permission to manage watches"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 19424289b8a9..037c55d8d771 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Can’t access the phone’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Can’t access the tablet’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"This can’t be accessed while streaming. Try on your phone instead."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Can’t view picture-in-picture while streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"System default"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Companion Watch profile permission to manage watches"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 45b09f5d84ab..cdf3677265b0 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Can’t access the phone’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Can’t access the tablet’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"This can’t be accessed while streaming. Try on your phone instead."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Can’t view picture-in-picture while streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"System default"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Companion watch profile permission to manage watches"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 81145e8fb299..8952bdc3342e 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Can’t access the phone’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Can’t access the tablet’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"This can’t be accessed while streaming. Try on your phone instead."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Can’t view picture-in-picture while streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"System default"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Companion watch profile permission to manage watches"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 54d02784ae39..749b2fffb0ea 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Can’t access the phone’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Can’t access the tablet’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"This can’t be accessed while streaming. Try on your phone instead."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Can’t view picture-in-picture while streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"System default"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Companion Watch profile permission to manage watches"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index fcb3f08d09eb..56aaf5af22f4 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"No se puede acceder a la cámara del dispositivo desde tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"No se puede acceder a la cámara de la tablet desde tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"No se puede acceder a este contenido durante una transmisión. Inténtalo en tu teléfono."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"No puedes ver pantalla en pantalla mientras transmites"</string> <string name="system_locale_title" msgid="711882686834677268">"Predeterminado del sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"TARJETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permiso de perfil del reloj complementario para administrar relojes"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 4c5939c055b6..47cf2cc5af79 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"No se puede acceder a la cámara del teléfono desde tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"No se puede acceder a la cámara del tablet desde tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"No se puede acceder a este contenido durante una emisión. Prueba en tu teléfono."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"No se puede usar imagen en imagen mientras se emite contenido"</string> <string name="system_locale_title" msgid="711882686834677268">"Predeterminado del sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"TARJETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permiso del perfil del reloj complementario para gestionar relojes"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index df2e2aff712f..3bd876fef14c 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Teie seadmest <xliff:g id="DEVICE">%1$s</xliff:g> ei pääse telefoni kaamerale juurde."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Teie seadmest <xliff:g id="DEVICE">%1$s</xliff:g> ei pääse tahvelarvuti kaamerale juurde"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Sellele ei pääse voogesituse ajal juurde. Proovige juurde pääseda oma telefonis."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Voogesitamise ajal ei saa pilt pildis funktsiooni kasutada"</string> <string name="system_locale_title" msgid="711882686834677268">"Süsteemi vaikeseade"</string> <string name="default_card_name" msgid="9198284935962911468">"KAART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Kaasrakenduse profiili luba kellade haldamiseks"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 5ccae4a86029..5b7741fb3627 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ezin da atzitu telefonoaren kamera <xliff:g id="DEVICE">%1$s</xliff:g> gailutik"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ezin da atzitu tabletaren kamera <xliff:g id="DEVICE">%1$s</xliff:g> gailutik"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Ezin da atzitu edukia hura igorri bitartean. Oraingo gailuaren ordez, erabili telefonoa."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Edukia zuzenean erreproduzitu bitartean ezin da pantaila txiki gainjarrian ikusi"</string> <string name="system_locale_title" msgid="711882686834677268">"Sistemaren balio lehenetsia"</string> <string name="default_card_name" msgid="9198284935962911468">"<xliff:g id="CARDNUMBER">%d</xliff:g> TXARTELA"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Aplikazio osagarrien erloju-profilaren baimena erlojuak kudeatzeko"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 226be578086d..ad027bc2924b 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"از <xliff:g id="DEVICE">%1$s</xliff:g> به دوربین تلفن دسترسی ندارید"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"نمیتوان از <xliff:g id="DEVICE">%1$s</xliff:g> شما به دوربین رایانه لوحی دسترسی داشت"</string> <string name="vdm_secure_window" msgid="161700398158812314">"درحین جاریسازی، نمیتوانید به آن دسترسی داشته باشید. دسترسی به آن را در تلفنتان امتحان کنید."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"هنگام جاریسازی نمیتوان تصویر در تصویر را مشاهده کرد"</string> <string name="system_locale_title" msgid="711882686834677268">"پیشفرض سیستم"</string> <string name="default_card_name" msgid="9198284935962911468">"کارت <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"اجازه نمایه «ساعت همراه» برای مدیریت ساعتها"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index b88abb08bd73..319193d820f5 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> ei pääse puhelimen kameraan"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> ei pääse tabletin kameraan"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Sisältöön ei saa pääsyä striimauksen aikana. Kokeile striimausta puhelimella."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Pikkuruutua ei voi nähdä striimauksen aikana"</string> <string name="system_locale_title" msgid="711882686834677268">"Järjestelmän oletusarvo"</string> <string name="default_card_name" msgid="9198284935962911468">"Kortti: <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Kumppanin kelloprofiilin hallintalupa"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 02bc75ca2aec..ea51b7f9957f 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Impossible d\'accéder à l\'appareil photo du téléphone à partir de votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Impossible d\'accéder à l\'appareil photo de la tablette à partir de votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Vous ne pouvez pas y accéder lorsque vous utilisez la diffusion en continu. Essayez sur votre téléphone à la place."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Impossible d\'afficher des incrustations d\'image pendant une diffusion en continu"</string> <string name="system_locale_title" msgid="711882686834677268">"Paramètre système par défaut"</string> <string name="default_card_name" msgid="9198284935962911468">"CARTE <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Autorisation du profil de la montre de l\'application compagnon pour gérer les montres"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index e922931a0de3..a1d43836bf53 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Impossible d\'accéder à l\'appareil photo du téléphone depuis votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Impossible d\'accéder à l\'appareil photo de la tablette depuis votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Impossible d\'accéder à cela pendant le streaming. Essayez plutôt sur votre téléphone."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Impossible d\'afficher Picture-in-picture pendant le streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Paramètre système par défaut"</string> <string name="default_card_name" msgid="9198284935962911468">"CARTE <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Autorisation du profil de la montre associée pour gérer des montres"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index d8689755ac75..73527bf488df 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Non se puido acceder á cámara do teléfono desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>)"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Non se puido acceder á cámara da tableta desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>)"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Non se puido acceder a este contido durante a reprodución en tempo real. Téntao desde o teléfono."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Non se pode ver un vídeo en pantalla superposta mentres se reproduce en tempo real"</string> <string name="system_locale_title" msgid="711882686834677268">"Opción predeterminada do sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"TARXETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permiso de perfil de Companion Watch para xestionar reloxos"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 9531ff82f48e..692b70ddf7e1 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"તમારા <xliff:g id="DEVICE">%1$s</xliff:g> પરથી ફોનના કૅમેરાનો ઍક્સેસ કરી શકતાં નથી"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"તમારા <xliff:g id="DEVICE">%1$s</xliff:g> પરથી ટૅબ્લેટના કૅમેરાનો ઍક્સેસ કરી શકતાં નથી"</string> <string name="vdm_secure_window" msgid="161700398158812314">"સ્ટ્રીમ કરતી વખતે આ ઍક્સેસ કરી શકાતું નથી. તેના બદલે તમારા ફોન પર પ્રયાસ કરો."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"સ્ટ્રીમ કરતી વખતે ચિત્ર-માં-ચિત્ર જોઈ શકતા નથી"</string> <string name="system_locale_title" msgid="711882686834677268">"સિસ્ટમ ડિફૉલ્ટ"</string> <string name="default_card_name" msgid="9198284935962911468">"કાર્ડ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"વૉચ મેનેજ કરવા માટે સાથી વૉચ પ્રોફાઇલની પરવાનગી"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index bed4cfaabeff..8d41c04e6f59 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"आपके <xliff:g id="DEVICE">%1$s</xliff:g> से फ़ोन के कैमरे को ऐक्सेस नहीं किया जा सकता"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"आपके <xliff:g id="DEVICE">%1$s</xliff:g> से टैबलेट के कैमरे को ऐक्सेस नहीं किया जा सकता"</string> <string name="vdm_secure_window" msgid="161700398158812314">"स्ट्रीमिंग के दौरान, इसे ऐक्सेस नहीं किया जा सकता. इसके बजाय, अपने फ़ोन पर ऐक्सेस करके देखें."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"स्ट्रीमिंग करते समय, \'पिक्चर में पिक्चर\' सुविधा इस्तेमाल नहीं की जा सकती"</string> <string name="system_locale_title" msgid="711882686834677268">"सिस्टम डिफ़ॉल्ट"</string> <string name="default_card_name" msgid="9198284935962911468">"कार्ड <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"स्मार्टवॉच मैनेज करने के लिए, स्मार्टवॉच के साथ काम करने वाले साथी ऐप्लिकेशन पर प्रोफ़ाइल से जुड़ी अनुमति"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index b4c5cd4c42e1..817efed0900e 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"S vašeg uređaja <xliff:g id="DEVICE">%1$s</xliff:g> nije moguće pristupiti fotoaparatu telefona"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"S vašeg uređaja <xliff:g id="DEVICE">%1$s</xliff:g> nije moguće pristupiti fotoaparatu tableta"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Sadržaju nije moguće pristupiti tijekom streaminga. Pokušajte mu pristupiti na telefonu."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Slika u slici ne može se prikazivati tijekom streaminga"</string> <string name="system_locale_title" msgid="711882686834677268">"Zadane postavke sustava"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTICA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Dopuštenje profila popratne aplikacije sata za upravljanje satovima"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 0f364edb63d2..1916a52cd5b6 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nem lehet hozzáférni a telefon kamerájához a következő eszközön: <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nem lehet hozzáférni a táblagép kamerájához a következő eszközön: <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Ehhez a tartalomhoz nem lehet hozzáférni streamelés közben. Próbálja újra a telefonján."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Streamelés közben nem lehetséges a kép a képben módban való lejátszás"</string> <string name="system_locale_title" msgid="711882686834677268">"Rendszerbeállítás"</string> <string name="default_card_name" msgid="9198284935962911468">"KÁRTYA: <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Társóra-profilra vonatkozó engedély az órák kezeléséhez"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 33a90c251e7a..5491f3446900 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Հնարավոր չէ օգտագործել հեռախոսի տեսախցիկը ձեր <xliff:g id="DEVICE">%1$s</xliff:g> սարքից"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Հնարավոր չէ օգտագործել պլանշետի տեսախցիկը ձեր <xliff:g id="DEVICE">%1$s</xliff:g> սարքից"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Այս բովանդակությունը հասանելի չէ հեռարձակման ընթացքում։ Օգտագործեք ձեր հեռախոսը։"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Հեռարձակման ժամանակ հնարավոր չէ դիտել նկարը նկարի մեջ"</string> <string name="system_locale_title" msgid="711882686834677268">"Կանխադրված"</string> <string name="default_card_name" msgid="9198284935962911468">"ՔԱՐՏ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Ժամացույցները կառավարելու թույլտվություն ուղեկցող հավելվածի համար"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index d834470d79b2..0ff21cbe9a93 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Tidak dapat mengakses kamera ponsel dari <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Tidak dapat mengakses kamera tablet dari <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Konten ini tidak dapat diakses saat melakukan streaming. Coba di ponsel."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Tidak dapat menampilkan picture-in-picture saat streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Default sistem"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTU <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Izin profil Smartwatch Pendamping untuk mengelola smartwatch"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index b3c802b42f7b..3ba712075284 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ekki er hægt að opna myndavél símans úr <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ekki er hægt að opna myndavél spjaldtölvunnar úr <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Ekki er hægt að opna þetta á meðan streymi stendur yfir. Prófaðu það í símanum í staðinn."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Ekki er hægt að horfa á mynd í mynd á meðan streymi er í gangi"</string> <string name="system_locale_title" msgid="711882686834677268">"Sjálfgildi kerfis"</string> <string name="default_card_name" msgid="9198284935962911468">"KORT <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Fylgiforrit úrs – prófílheimild til að stjórna úrum"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index d18015494ace..39076f71980a 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Impossibile accedere alla fotocamera del telefono dal tuo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Impossibile accedere alla fotocamera del tablet dal tuo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Impossibile accedere a questi contenuti durante lo streaming. Prova a usare il telefono."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Impossibile visualizzare Picture in picture durante lo streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Predefinita di sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"SCHEDA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Autorizzazione per il profilo degli smartwatch complementari per gestire gli smartwatch"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 96a3618c5798..1dac7052c8a4 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"לא ניתן לגשת למצלמה של הטלפון מה‑<xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"לא ניתן לגשת למצלמה של הטאבלט מה‑<xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"אי אפשר לגשת לתוכן המאובטח הזה בזמן סטרימינג. במקום זאת, אפשר לנסות בטלפון."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"אי אפשר להציג תמונה בתוך תמונה בזמן סטרימינג"</string> <string name="system_locale_title" msgid="711882686834677268">"ברירת המחדל של המערכת"</string> <string name="default_card_name" msgid="9198284935962911468">"כרטיס <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"הרשאת פרופיל שעון לאפליקציה נלווית כדי לנהל שעונים"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 5acb6c8989b7..7f7104eb453d 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> からスマートフォンのカメラにアクセスできません"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> からタブレットのカメラにアクセスできません"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ストリーミング中はアクセスできません。スマートフォンでのアクセスをお試しください。"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ストリーミング中はピクチャー イン ピクチャーを表示できません"</string> <string name="system_locale_title" msgid="711882686834677268">"システムのデフォルト"</string> <string name="default_card_name" msgid="9198284935962911468">"カード <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ウォッチを管理できるコンパニオン ウォッチ プロファイル権限"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 01bf9b0469c2..8db1325a0a75 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ტელეფონის კამერაზე წვდომა ვერ მოხერხდა თქვენი <xliff:g id="DEVICE">%1$s</xliff:g>-დან"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ტაბლეტის კამერაზე წვდომა ვერ მოხერხდა თქვენი <xliff:g id="DEVICE">%1$s</xliff:g>-დან"</string> <string name="vdm_secure_window" msgid="161700398158812314">"მასზე წვდომის მიᲦება შეუძლებელია სტრიმინგის დროს. ცადეთ ტელეფონიდან."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"სტრიმინგის დროს ეკრანის ეკრანში ნახვა შეუძლებელია"</string> <string name="system_locale_title" msgid="711882686834677268">"სისტემის ნაგულისხმევი"</string> <string name="default_card_name" msgid="9198284935962911468">"ბარათი <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"კომპანიონი საათის პროფილის ნებართვა საათების მართვაზე"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 06594088d789..df69e4ced740 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> құрылғысынан телефон камерасын пайдалану мүмкін емес."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> құрылғысынан планшет камерасын пайдалану мүмкін емес."</string> <string name="vdm_secure_window" msgid="161700398158812314">"Трансляция кезінде контентті көру мүмкін емес. Оның орнына телефоннан көріңіз."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Трансляция кезінде суреттегі суретті көру мүмкін емес."</string> <string name="system_locale_title" msgid="711882686834677268">"Жүйенің әдепкі параметрі"</string> <string name="default_card_name" msgid="9198284935962911468">"<xliff:g id="CARDNUMBER">%d</xliff:g>-КАРТА"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Сағаттарды басқаруға арналған қосымша сағат профилінің рұқсаты"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 549dce845e51..5328e5858297 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"មិនអាចចូលប្រើកាមេរ៉ាទូរសព្ទពី <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបានទេ"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"មិនអាចចូលប្រើកាមេរ៉ាថេប្លេតពី <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបានទេ"</string> <string name="vdm_secure_window" msgid="161700398158812314">"មិនអាចចូលប្រើប្រាស់ខ្លឹមសារនេះបានទេ ពេលផ្សាយ។ សូមសាកល្បងប្រើនៅលើទូរសព្ទរបស់អ្នកជំនួសវិញ។"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"មិនអាចមើលរូបក្នុងរូបខណៈពេលកំពុងផ្សាយបានទេ"</string> <string name="system_locale_title" msgid="711882686834677268">"លំនាំដើមប្រព័ន្ធ"</string> <string name="default_card_name" msgid="9198284935962911468">"កាត <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ការអនុញ្ញាតពីកម្រងព័ត៌មាននាឡិកាដៃគូ ដើម្បីគ្រប់គ្រងនាឡិកា"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index a103f086f765..2ce4b8cb8226 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ನಿಮ್ಮ <xliff:g id="DEVICE">%1$s</xliff:g> ಮೂಲಕ ಫೋನ್ನ ಕ್ಯಾಮರಾವನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ನಿಮ್ಮ <xliff:g id="DEVICE">%1$s</xliff:g> ಮೂಲಕ ಟ್ಯಾಬ್ಲೆಟ್ನ ಕ್ಯಾಮರಾವನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ಸ್ಟ್ರೀಮ್ ಮಾಡುವಾಗ ಇದನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅದರ ಬದಲು ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ಸ್ಟ್ರೀಮ್ ಮಾಡುವಾಗ ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರವನ್ನು ವೀಕ್ಷಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="system_locale_title" msgid="711882686834677268">"ಸಿಸ್ಟಂ ಡೀಫಾಲ್ಟ್"</string> <string name="default_card_name" msgid="9198284935962911468">"ಕಾರ್ಡ್ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ವಾಚ್ಗಳನ್ನು ನಿರ್ವಹಿಸುವುದಕ್ಕಾಗಿ ಕಂಪ್ಯಾನಿಯನ್ ವಾಚ್ ಪ್ರೊಫೈಲ್ ಅನುಮತಿ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index c15240fa7b0d..3485cc505b60 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"사용자의 <xliff:g id="DEVICE">%1$s</xliff:g>에서 휴대전화 카메라에 액세스할 수 없습니다."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"사용자의 <xliff:g id="DEVICE">%1$s</xliff:g>에서 태블릿 카메라에 액세스할 수 없습니다."</string> <string name="vdm_secure_window" msgid="161700398158812314">"스트리밍 중에는 액세스할 수 없습니다. 대신 휴대전화에서 시도해 보세요."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"스트리밍 중에는 PIP 모드를 볼 수 없습니다."</string> <string name="system_locale_title" msgid="711882686834677268">"시스템 기본값"</string> <string name="default_card_name" msgid="9198284935962911468">"<xliff:g id="CARDNUMBER">%d</xliff:g> 카드"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"시계 관리를 위한 호환 시계 프로필 권한"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 99c2ead7b0c6..3a572294f1a6 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> түзмөгүңүздөн телефондун камерасына мүмкүнчүлүк жок"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> түзмөгүңүздөн планшетиңиздин камерасына мүмкүнчүлүк жок"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Муну алып ойнотуу учурунда көрүүгө болбойт. Анын ордуна телефондон кирип көрүңүз."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Алып ойнотуп жатканда сүрөттөгү сүрөт көрүнбөйт"</string> <string name="system_locale_title" msgid="711882686834677268">"Системанын демейки параметрлери"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Сааттын көмөкчү профилинин сааттарды тескөө уруксаты"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 3a06f72d1cd1..00216777bc86 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ບໍ່ສາມາດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບຂອງໂທລະສັບຈາກ <xliff:g id="DEVICE">%1$s</xliff:g> ຂອງທ່ານໄດ້"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ບໍ່ສາມາດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບຂອງແທັບເລັດຈາກ <xliff:g id="DEVICE">%1$s</xliff:g> ຂອງທ່ານໄດ້"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ບໍ່ສາມາດເຂົ້າເຖິງເນື້ອຫານີ້ໄດ້ໃນຂະນະທີ່ຍັງສະຕຣີມຢູ່. ກະລຸນາລອງຢູ່ໂທລະສັບຂອງທ່ານແທນ."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ບໍ່ສາມາດເບິ່ງການສະແດງຜົນຊ້ອນກັນໃນຂະນະທີ່ສະຕຣີມໄດ້"</string> <string name="system_locale_title" msgid="711882686834677268">"ຄ່າເລີ່ມຕົ້ນຂອງລະບົບ"</string> <string name="default_card_name" msgid="9198284935962911468">"ບັດ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ສິດການອະນຸຍາດສຳລັບໂປຣໄຟລ໌ໃນໂມງຊ່ວຍເຫຼືອເພື່ອຈັດການໂມງ"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index f5530fe54097..8e43d8a3a47a 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -2313,7 +2313,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nepavyko pasiekti telefono fotoaparato iš „<xliff:g id="DEVICE">%1$s</xliff:g>“"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nepavyko pasiekti planšetinio kompiuterio fotoaparato iš „<xliff:g id="DEVICE">%1$s</xliff:g>“"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Nepavyksta pasiekti perduodant srautu. Pabandykite naudoti telefoną."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Negalima peržiūrėti vaizdo vaizde perduodant srautu"</string> <string name="system_locale_title" msgid="711882686834677268">"Numatytoji sistemos vertė"</string> <string name="default_card_name" msgid="9198284935962911468">"KORTELĖ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Papildomos programos laikrodžio profilio leidimas valdyti laikrodžius"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 08865b3ece7b..2257c25453f9 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nevar piekļūt tālruņa kamerai no jūsu ierīces (<xliff:g id="DEVICE">%1$s</xliff:g>)."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nevar piekļūt planšetdatora kamerai no jūsu ierīces (<xliff:g id="DEVICE">%1$s</xliff:g>)."</string> <string name="vdm_secure_window" msgid="161700398158812314">"Straumēšanas laikā nevar piekļūt šim saturam. Mēģiniet tam piekļūt savā tālrunī."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Straumēšanas laikā nevar skatīt attēlu attēlā"</string> <string name="system_locale_title" msgid="711882686834677268">"Sistēmas noklusējums"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTE <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Palīglietotnes pulksteņa profila atļauja pārvaldīt pulksteņus"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index b574380ce823..21950c01cb2e 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не може да се пристапи до камерата на вашиот телефон од <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не може да се пристапи до камерата на вашиот таблет од <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"До ова не може да се пристапи при стриминг. Наместо тоа, пробајте на вашиот телефон."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Не може да се прикажува слика во слика при стримување"</string> <string name="system_locale_title" msgid="711882686834677268">"Стандарден за системот"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТИЧКА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Дозвола за профилот на придружен часовник за управување со часовници"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 7856654553d6..2dac3e4b5cdd 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"നിങ്ങളുടെ <xliff:g id="DEVICE">%1$s</xliff:g> എന്നതിൽ നിന്ന് ഫോണിന്റെ ക്യാമറ ആക്സസ് ചെയ്യാനാകില്ല"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"നിങ്ങളുടെ <xliff:g id="DEVICE">%1$s</xliff:g> എന്നതിൽ നിന്ന് ടാബ്ലെറ്റിന്റെ ക്യാമറ ആക്സസ് ചെയ്യാനാകില്ല"</string> <string name="vdm_secure_window" msgid="161700398158812314">"സ്ട്രീം ചെയ്യുമ്പോൾ ഇത് ആക്സസ് ചെയ്യാനാകില്ല. പകരം നിങ്ങളുടെ ഫോണിൽ ശ്രമിച്ച് നോക്കൂ."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"സ്ട്രീമിംഗിനിടെ ചിത്രത്തിനുള്ളിൽ ചിത്രം കാണാനാകില്ല"</string> <string name="system_locale_title" msgid="711882686834677268">"സിസ്റ്റം ഡിഫോൾട്ട്"</string> <string name="default_card_name" msgid="9198284935962911468">"കാർഡ് <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"വാച്ചുകൾ മാനേജ് ചെയ്യുന്നതിന് സഹകാരി ആപ്പിനുള്ള വാച്ച് പ്രൊഫൈൽ അനുമതി"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index ef4d07d0ce07..81835779632d 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Таны <xliff:g id="DEVICE">%1$s</xliff:g>-с утасны камерт хандах боломжгүй"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Таны <xliff:g id="DEVICE">%1$s</xliff:g>-с таблетын камерт хандах боломжгүй"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Стримингийн үед үүнд хандах боломжгүй. Оронд нь утас дээрээ туршиж үзнэ үү."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Дамжуулах явцад дэлгэц доторх дэлгэцийг үзэх боломжгүй"</string> <string name="system_locale_title" msgid="711882686834677268">"Системийн өгөгдмөл"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Дэмжигч цагны профайлын цаг удирдах зөвшөөрөл"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 26108cc4938b..214403555d04 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"तुमच्या <xliff:g id="DEVICE">%1$s</xliff:g> वरून फोनचा कॅमेरा अॅक्सेस करू शकत नाही"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"तुमच्या <xliff:g id="DEVICE">%1$s</xliff:g> वरून टॅबलेटचा कॅमेरा अॅक्सेस करू शकत नाही"</string> <string name="vdm_secure_window" msgid="161700398158812314">"स्ट्रीम करताना हे अॅक्सेस केले जाऊ शकत नाही. त्याऐवजी तुमच्या फोनवर अॅक्सेस करून पहा."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"स्ट्रीम होत असताना चित्रात-चित्र पाहू शकत नाही"</string> <string name="system_locale_title" msgid="711882686834677268">"सिस्टीम डीफॉल्ट"</string> <string name="default_card_name" msgid="9198284935962911468">"कार्ड <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"वॉच व्यवस्थापित करण्यासाठी सहयोगी वॉच प्रोफाइलची परवानगी"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 12846f9d19ad..c800be95bc28 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Tidak dapat mengakses kamera telefon daripada <xliff:g id="DEVICE">%1$s</xliff:g> anda"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Tidak dapat mengakses kamera tablet daripada <xliff:g id="DEVICE">%1$s</xliff:g> anda"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Kandungan ini tidak boleh diakses semasa penstriman. Cuba pada telefon anda."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Tidak dapat melihat gambar dalam gambar semasa penstriman"</string> <string name="system_locale_title" msgid="711882686834677268">"Lalai sistem"</string> <string name="default_card_name" msgid="9198284935962911468">"KAD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Kebenaran profil Jam Tangan rakan untuk mengurus jam tangan"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 0ba4c1f0a314..d2f7ef750135 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"သင်၏ <xliff:g id="DEVICE">%1$s</xliff:g> မှ ဖုန်းကင်မရာကို သုံး၍မရပါ"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"သင်၏ <xliff:g id="DEVICE">%1$s</xliff:g> မှ တက်ဘလက်ကင်မရာကို သုံး၍မရပါ"</string> <string name="vdm_secure_window" msgid="161700398158812314">"တိုက်ရိုက်လွှင့်နေစဉ် ၎င်းကို မသုံးနိုင်ပါ။ ၎င်းအစား ဖုန်းတွင် စမ်းကြည့်ပါ။"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"တိုက်ရိုက်လွှင့်စဉ် နှစ်ခုထပ်၍ မကြည့်နိုင်ပါ"</string> <string name="system_locale_title" msgid="711882686834677268">"စနစ်မူရင်း"</string> <string name="default_card_name" msgid="9198284935962911468">"ကတ် <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"လက်ပတ်နာရီများစီမံရန် ‘တွဲဖက်နာရီ’ ပရိုဖိုင်ခွင့်ပြုချက်"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 1aa7b719005a..f2c5c7759bef 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Det er ikke mulig å få tilgang til telefonkameraet fra <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Det er ikke mulig å få tilgang til kameraet på nettbrettet fra <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Dette er ikke tilgjengelig under strømming. Prøv på telefonen i stedet."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Kan ikke se bilde-i-bilde under strømming"</string> <string name="system_locale_title" msgid="711882686834677268">"Systemstandard"</string> <string name="default_card_name" msgid="9198284935962911468">"KORT <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Klokkeprofil-tillatelse for følgeapp for å administrere klokker"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 438fc7ac6a9e..13c3c12c35ac 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"तपाईंको <xliff:g id="DEVICE">%1$s</xliff:g> मार्फत फोनको क्यामेरा प्रयोग गर्न मिल्दैन"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"तपाईंको <xliff:g id="DEVICE">%1$s</xliff:g> मार्फत ट्याब्लेटको क्यामेरा प्रयोग गर्न मिल्दैन"</string> <string name="vdm_secure_window" msgid="161700398158812314">"स्ट्रिम गरिरहेका बेला यो सामग्री हेर्न तथा प्रयोग गर्न मिल्दैन। बरु आफ्नो फोनमार्फत सो सामग्री हेर्ने तथा प्रयोग गर्ने प्रयास गर्नुहोस्।"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"स्ट्रिम गरिरहेका बेला picture-in-picture मोड प्रयोग गर्न मिल्दैन"</string> <string name="system_locale_title" msgid="711882686834677268">"सिस्टम डिफल्ट"</string> <string name="default_card_name" msgid="9198284935962911468">"कार्ड <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"स्मार्टवाचहरू व्यवस्थापन गर्ने सहयोगी वाच प्रोफाइलसम्बन्धी अनुमति"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 6ab375015fe0..a9d654f3a388 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Kan geen toegang tot de camera van de telefoon krijgen vanaf je <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Kan geen toegang tot de camera van de tablet krijgen vanaf je <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Je hebt hier geen toegang toe tijdens streaming. Probeer het in plaats daarvan op je telefoon."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Kan scherm-in-scherm niet bekijken tijdens het streamen"</string> <string name="system_locale_title" msgid="711882686834677268">"Systeemstandaard"</string> <string name="default_card_name" msgid="9198284935962911468">"KAART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Smartwatchprofiel voor bijbehorende app om smartwatches te beheren"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 2dfe51720c6e..6215213b3637 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ଆପଣଙ୍କ <xliff:g id="DEVICE">%1$s</xliff:g>ରୁ ଫୋନର କ୍ୟାମେରାକୁ ଆକ୍ସେସ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ଆପଣଙ୍କ <xliff:g id="DEVICE">%1$s</xliff:g>ରୁ ଟାବଲେଟର କ୍ୟାମେରାକୁ ଆକ୍ସେସ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ଷ୍ଟ୍ରିମ କରିବା ସମୟରେ ଏହାକୁ ଆକ୍ସେସ କରାଯାଇପାରିବ ନାହିଁ। ଏହା ପରିବର୍ତ୍ତେ ଆପଣଙ୍କ ଫୋନରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ଷ୍ଟ୍ରିମ କରିବା ସମୟରେ ପିକଚର-ଇନ-ପିକଚର ଦେଖାଯାଇପାରିବ ନାହିଁ"</string> <string name="system_locale_title" msgid="711882686834677268">"ସିଷ୍ଟମ ଡିଫଲ୍ଟ"</string> <string name="default_card_name" msgid="9198284935962911468">"କାର୍ଡ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ୱାଚଗୁଡ଼ିକୁ ପରିଚାଳନା କରିବା ପାଇଁ ସହଯୋଗୀ ୱାଚ ପ୍ରୋଫାଇଲ ଅନୁମତି"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 2d16afc93117..dc53918fca34 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ਤੁਹਾਡੇ <xliff:g id="DEVICE">%1$s</xliff:g> ਤੋਂ ਫ਼ੋਨ ਦੇ ਕੈਮਰੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ਤੁਹਾਡੇ <xliff:g id="DEVICE">%1$s</xliff:g> ਤੋਂ ਟੈਬਲੈੱਟ ਦੇ ਕੈਮਰੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ਸਟ੍ਰੀਮਿੰਗ ਦੌਰਾਨ ਇਸ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਇਸਦੀ ਬਜਾਏ ਆਪਣੇ ਫ਼ੋਨ \'ਤੇ ਵਰਤ ਕੇ ਦੇਖੋ।"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ਸਟ੍ਰੀਮਿੰਗ ਦੌਰਾਨ ਤਸਵੀਰ-ਵਿੱਚ-ਤਸਵੀਰ ਨਹੀਂ ਦੇਖੀ ਜਾ ਸਕਦੀ"</string> <string name="system_locale_title" msgid="711882686834677268">"ਸਿਸਟਮ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string> <string name="default_card_name" msgid="9198284935962911468">"ਕਾਰਡ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ਘੜੀਆਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਸੰਬੰਧੀ ਘੜੀ ਪ੍ਰੋਫਾਈਲ ਇਜਾਜ਼ਤ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 52869c4c6a0c..4e3fe0d5fcd7 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -2313,7 +2313,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nie można korzystać z aparatu telefonu na urządzeniu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nie można korzystać z aparatu tabletu na urządzeniu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Nie można z tego skorzystać podczas strumieniowania. Użyj telefonu."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Podczas strumieniowania nie można wyświetlać obrazu w obrazie"</string> <string name="system_locale_title" msgid="711882686834677268">"Ustawienie domyślne systemu"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Uprawnienie profilu zegarka towarzyszącego do zarządzania zegarkami"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index ccd10c4f844a..47115ba69072 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Não é possível acessar a câmera do smartphone pelo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Não é possível acessar a câmera do tablet pelo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Não é possível acessar esse conteúdo durante o streaming. Tente pelo smartphone."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Não é possível usar o modo picture-in-picture durante o streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Padrão do sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"CHIP <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permissão do perfil do relógio complementar para gerenciar relógios"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 7335d2b6270f..86e84ba46339 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Não é possível aceder à câmara do telemóvel a partir do dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Não é possível aceder à câmara do tablet a partir do dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Não é possível aceder a isto durante o streaming. Em alternativa, experimente no telemóvel."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Não é possível ver o ecrã no ecrã durante o streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Predefinição do sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"CARTÃO <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Autorização do perfil de relógio associado para gerir relógios"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index ccd10c4f844a..47115ba69072 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Não é possível acessar a câmera do smartphone pelo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Não é possível acessar a câmera do tablet pelo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Não é possível acessar esse conteúdo durante o streaming. Tente pelo smartphone."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Não é possível usar o modo picture-in-picture durante o streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Padrão do sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"CHIP <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permissão do perfil do relógio complementar para gerenciar relógios"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index e31d465c8bd2..14b5b14958d7 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nu se poate accesa camera foto a telefonului de pe <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nu se poate accesa camera foto a tabletei de pe <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Nu se poate accesa în timpul streamingului. Încearcă pe telefon."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Nu se poate viziona picture-in-picture în timpul streamingului"</string> <string name="system_locale_title" msgid="711882686834677268">"Prestabilită de sistem"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permisiunea pentru gestionarea ceasurilor din profilul ceasului însoțitor"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 9ad5dd48d84d..da1f520211fd 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -2313,7 +2313,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"У устройства <xliff:g id="DEVICE">%1$s</xliff:g> нет доступа к камере телефона."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"У устройства \"<xliff:g id="DEVICE">%1$s</xliff:g>\" нет доступа к камере планшета."</string> <string name="vdm_secure_window" msgid="161700398158812314">"Этот контент недоступен во время трансляции. Используйте телефон."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Нельзя запустить режим \"Картинка в картинке\" во время потоковой передачи"</string> <string name="system_locale_title" msgid="711882686834677268">"Системные настройки по умолчанию"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Разрешение для сопутствующего приложения управлять часами"</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index c47634206d72..c09d7f515b9d 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ඔබගේ <xliff:g id="DEVICE">%1$s</xliff:g> වෙතින් දුරකථනයේ කැමරාවට ප්රවේශ විය නොහැකිය"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ඔබගේ <xliff:g id="DEVICE">%1$s</xliff:g> වෙතින් ටැබ්ලටයේ කැමරාවට ප්රවේශ විය නොහැකිය"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ප්රවාහය කරන අතරේ මෙයට ප්රවේශ විය නොහැක. ඒ වෙනුවට ඔබේ දුරකථනයෙහි උත්සාහ කරන්න."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ප්රවාහය අතරේ පින්තූරයේ-පින්තූරය බැලිය නොහැක"</string> <string name="system_locale_title" msgid="711882686834677268">"පද්ධති පෙරනිමිය"</string> <string name="default_card_name" msgid="9198284935962911468">"කාඩ්පත <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ඔරලෝසු කළමනාකරණය කිරීමට සහායක ඔරලෝසු පැතිකඩ අවසරය"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 16fa3ccd723f..50e519d12446 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -2313,7 +2313,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> nemáte prístup ku kamere telefónu"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> nemáte prístup ku kamere tabletu"</string> <string name="vdm_secure_window" msgid="161700398158812314">"K tomuto obsahu nie je počas streamovania prístup. Skúste použiť telefón."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Počas streamingu sa obraz v obraze nedá zobraziť"</string> <string name="system_locale_title" msgid="711882686834677268">"Predvolené systémom"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Povolenie profilu hodiniek pre sprievodnú aplikáciu umožňujúce spravovať hodinky"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index bc6af03a0fcb..9a57ccf49f36 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -2313,7 +2313,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ni mogoče dostopati do fotoaparata telefona prek naprave <xliff:g id="DEVICE">%1$s</xliff:g>."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ni mogoče dostopati do fotoaparata tabličnega računalnika prek naprave <xliff:g id="DEVICE">%1$s</xliff:g>."</string> <string name="vdm_secure_window" msgid="161700398158812314">"Do te vsebine ni mogoče dostopati med pretočnim predvajanjem. Poskusite s telefonom."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Slike v sliki ni mogoče prikazati med pretočnim predvajanjem."</string> <string name="system_locale_title" msgid="711882686834677268">"Sistemsko privzeto"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTICA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Dovoljenje za upravljanje ur v profilu ure v spremljevalni aplikaciji"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 5ce4aa99e31c..ac6668d91d9c 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nuk mund të qasesh në kamerën e telefonit tënd nga <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nuk mund të qasesh në kamerën e tabletit tënd nga <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Nuk mund të kesh qasje në të gjatë transmetimit. Provoje në telefon më mirë."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Figura brenda figurës nuk mund të shikohet gjatë transmetimit"</string> <string name="system_locale_title" msgid="711882686834677268">"Parazgjedhja e sistemit"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Leje për profilin e \"Orës shoqëruese\" për të menaxhuar orët"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 84713fdb684b..2b6f90034d2d 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -2312,7 +2312,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не може да се приступи камери телефона са <xliff:g id="DEVICE">%1$s</xliff:g> уређаја"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не може да се приступи камери таблета са <xliff:g id="DEVICE">%1$s</xliff:g> уређаја"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Овом не можете да приступате током стримовања. Пробајте на телефону."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Не можете да гледате слику у слици при стримовању"</string> <string name="system_locale_title" msgid="711882686834677268">"Подразумевани системски"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТИЦА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Дозвола за профил пратећег сата за управљање сатовима"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 99dc2dad6d2a..37015712637c 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Telefonens kamera kan inte användas från <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Surfplattans kamera kan inte användas från <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Det går inte att komma åt innehållet när du streamar. Testa med telefonen i stället."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Det går inte att visa bild-i-bild när du streamar"</string> <string name="system_locale_title" msgid="711882686834677268">"Systemets standardinställning"</string> <string name="default_card_name" msgid="9198284935962911468">"KORT <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Behörighet för den tillhörande klockprofilen att hantera klockor"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index aeb0d2ba434d..5d948be5d1fc 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Huwezi kufikia kamera ya simu kutoka kwenye <xliff:g id="DEVICE">%1$s</xliff:g> yako"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Haiwezi kufikia kamera ya kompyuta kibao kutoka kwenye <xliff:g id="DEVICE">%1$s</xliff:g> yako"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Huwezi kufikia maudhui haya unapotiririsha. Badala yake jaribu kwenye simu yako."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Huwezi kuona picha iliyopachikwa ndani ya picha nyingine unapotiririsha"</string> <string name="system_locale_title" msgid="711882686834677268">"Chaguomsingi la mfumo"</string> <string name="default_card_name" msgid="9198284935962911468">"SIM KADI <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Ruhusa ya wasifu oanifu wa Saa ili kudhibiti saa"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index f0e90eaa14a8..702fb851e214 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"உங்கள் <xliff:g id="DEVICE">%1$s</xliff:g> சாதனத்திலிருந்து மொபைலின் கேமராவை அணுக முடியாது"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"உங்கள் <xliff:g id="DEVICE">%1$s</xliff:g> சாதனத்திலிருந்து டேப்லெட்டின் கேமராவை அணுக முடியாது"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ஸ்ட்ரீமின்போது இதை அணுக முடியாது. அதற்குப் பதிலாக உங்கள் மொபைலில் பயன்படுத்திப் பார்க்கவும்."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ஸ்ட்ரீம் செய்யும்போது பிக்ச்சர்-இன்-பிக்ச்சர் அம்சத்தைப் பயன்படுத்த முடியாது"</string> <string name="system_locale_title" msgid="711882686834677268">"சிஸ்டத்தின் இயல்பு"</string> <string name="default_card_name" msgid="9198284935962911468">"கார்டு <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"வாட்ச்சுகளை நிர்வகிக்க, துணைத் தயாரிப்பு ஆப்ஸில் வாட்ச் சுயவிவரத்தை அனுமதித்தல்"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 5a972859a86d..63a9b217a20d 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"మీ <xliff:g id="DEVICE">%1$s</xliff:g> నుండి ఫోన్ కెమెరాను యాక్సెస్ చేయడం సాధ్యపడదు"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"మీ <xliff:g id="DEVICE">%1$s</xliff:g> నుండి టాబ్లెట్ కెమెరాను యాక్సెస్ చేయడం సాధ్యపడదు"</string> <string name="vdm_secure_window" msgid="161700398158812314">"స్ట్రీమింగ్ చేస్తున్నప్పుడు దీన్ని యాక్సెస్ చేయడం సాధ్యపడదు. బదులుగా మీ ఫోన్లో ట్రై చేయండి."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"స్ట్రీమింగ్ చేస్తున్నప్పుడు పిక్చర్-ఇన్-పిక్చర్ చూడలేరు"</string> <string name="system_locale_title" msgid="711882686834677268">"సిస్టమ్ ఆటోమేటిక్ సెట్టింగ్"</string> <string name="default_card_name" msgid="9198284935962911468">"కార్డ్ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"వాచ్లను మేనేజ్ చేయడానికి సహాయక వాచ్ ప్రొఫైల్ అనుమతి"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 8aab6fa5ed02..9398393a9aea 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"เข้าถึงกล้องของโทรศัพท์จาก <xliff:g id="DEVICE">%1$s</xliff:g> ไม่ได้"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"เข้าถึงกล้องของแท็บเล็ตจาก <xliff:g id="DEVICE">%1$s</xliff:g> ไม่ได้"</string> <string name="vdm_secure_window" msgid="161700398158812314">"เข้าถึงเนื้อหานี้ไม่ได้ขณะที่สตรีมมิง โปรดลองเข้าถึงในโทรศัพท์แทน"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ดูการแสดงภาพซ้อนภาพขณะสตรีมไม่ได้"</string> <string name="system_locale_title" msgid="711882686834677268">"ค่าเริ่มต้นของระบบ"</string> <string name="default_card_name" msgid="9198284935962911468">"ซิมการ์ด <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"สิทธิ์สำหรับโปรไฟล์ในนาฬิกาที่ใช้ร่วมกันเพื่อจัดการนาฬิกา"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index beb0c0d511d1..3b2e78814884 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Hindi ma-access ang camera ng telepono mula sa iyong <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Hindi ma-access ang camera ng tablet mula sa iyong <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Hindi ito puwedeng i-access habang nagsi-stream. Subukan na lang sa iyong telepono."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Hindi matingnan nang picture-in-picture habang nagsi-stream"</string> <string name="system_locale_title" msgid="711882686834677268">"Default ng system"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Pahintulot sa profile ng Relo ng kasamang app na pamahalaan ang mga relo"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index c002f0f4d87e..22899c740894 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan telefonun kamerasına erişilemiyor"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan tabletin kamerasına erişilemiyor"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Canlı oynatılırken bu içeriğe erişilemez. Bunun yerine telefonunuzu kullanmayı deneyin."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Yayın sırasında pencere içinde pencere görüntülenemez"</string> <string name="system_locale_title" msgid="711882686834677268">"Sistem varsayılanı"</string> <string name="default_card_name" msgid="9198284935962911468">"KART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Kol saatlerinin yönetimi için Tamamlayıcı Kol Saati Profili İzni"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index d782f2d50e9f..e7b1d66b5e82 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -2313,7 +2313,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не вдається отримати доступ до камери телефона з пристрою <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не вдається отримати доступ до камери планшета з пристрою <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Цей контент недоступний під час потокового передавання. Спробуйте натомість скористатися телефоном."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Ви не можете переглядати картинку в картинці під час трансляції"</string> <string name="system_locale_title" msgid="711882686834677268">"Налаштування системи за умовчанням"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТКА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Дозвіл профілю годинника для супутнього додатка на керування годинниками"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 91b3e95b17a5..95122e9f3586 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"آپ کے <xliff:g id="DEVICE">%1$s</xliff:g> سے فون کے کیمرا تک رسائی حاصل نہیں کی جا سکتی"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"آپ کے <xliff:g id="DEVICE">%1$s</xliff:g> سے ٹیبلیٹ کے کیمرا تک رسائی حاصل نہیں کی جا سکتی"</string> <string name="vdm_secure_window" msgid="161700398158812314">"سلسلہ بندی کے دوران اس تک رسائی حاصل نہیں کی جا سکتی۔ اس کے بجائے اپنے فون پر کوشش کریں۔"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"سلسلہ بندی کے دوران تصویر میں تصویر نہیں دیکھ سکتے"</string> <string name="system_locale_title" msgid="711882686834677268">"سسٹم ڈیفالٹ"</string> <string name="default_card_name" msgid="9198284935962911468">"کارڈ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"گھڑیوں کا نظم کرنے کے لیے ساتھی ایپ کی گھڑی کی پروفائل کی اجازت"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 284abb2a611f..1080918b045b 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> qurilmasidan telefonning kamerasiga kirish imkonsiz"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> qurilmasidan planshetning kamerasiga kirish imkonsiz"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Bu kontent striming vaqtida ochilmaydi. Telefon orqali urininb koʻring."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Striming vaqtida tasvir ustida tasvir rejimida koʻrib boʻlmaydi"</string> <string name="system_locale_title" msgid="711882686834677268">"Tizim standarti"</string> <string name="default_card_name" msgid="9198284935962911468">"SIM KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Soatlarni boshqarish uchun hamroh Soat profiliga ruxsat"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 621eaddbe066..3101facfe4ba 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Không truy cập được vào máy ảnh trên điện thoại từ <xliff:g id="DEVICE">%1$s</xliff:g> của bạn"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Không truy cập được vào máy ảnh trên máy tính bảng từ <xliff:g id="DEVICE">%1$s</xliff:g> của bạn"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Bạn không thể truy cập vào nội dung này trong khi phát trực tuyến. Hãy thử trên điện thoại."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Không thể xem video ở chế độ hình trong hình khi đang truyền trực tuyến"</string> <string name="system_locale_title" msgid="711882686834677268">"Theo chế độ mặc định của hệ thống"</string> <string name="default_card_name" msgid="9198284935962911468">"THẺ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Quyền sử dụng hồ sơ Đồng hồ của ứng dụng đồng hành để quản lý các đồng hồ"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 5152c374d9f8..54221e4da2f1 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"无法从<xliff:g id="DEVICE">%1$s</xliff:g>上访问手机的摄像头"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"无法从<xliff:g id="DEVICE">%1$s</xliff:g>上访问平板电脑的摄像头"</string> <string name="vdm_secure_window" msgid="161700398158812314">"流式传输时无法访问此内容。您可以尝试在手机上访问。"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"在线播放时无法查看画中画"</string> <string name="system_locale_title" msgid="711882686834677268">"系统默认设置"</string> <string name="default_card_name" msgid="9198284935962911468">"SIM 卡 <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"用于管理手表的配套手表个人资料权限"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index e7f24ada474d..7e1089375d9e 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"無法從 <xliff:g id="DEVICE">%1$s</xliff:g> 存取手機的相機"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"無法從 <xliff:g id="DEVICE">%1$s</xliff:g> 存取平板電腦的相機"</string> <string name="vdm_secure_window" msgid="161700398158812314">"串流播放時無法使用,請改用手機。"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"串流期間無法查看畫中畫"</string> <string name="system_locale_title" msgid="711882686834677268">"系統預設"</string> <string name="default_card_name" msgid="9198284935962911468">"SIM 卡 <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"用於管理手錶的隨附手錶設定檔權限"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 41f29300a128..5f6bc2074bd4 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"無法從 <xliff:g id="DEVICE">%1$s</xliff:g> 存取手機的相機"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"無法從 <xliff:g id="DEVICE">%1$s</xliff:g> 存取平板電腦的相機"</string> <string name="vdm_secure_window" msgid="161700398158812314">"串流播放時無法存取這項內容,請改用手機。"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"串流播放時無法查看子母畫面"</string> <string name="system_locale_title" msgid="711882686834677268">"系統預設"</string> <string name="default_card_name" msgid="9198284935962911468">"SIM 卡 <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"用於管理智慧手錶的配對智慧手錶設定檔權限"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 7f8502081ab0..43c0ada224db 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -2311,7 +2311,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ayikwazi ukufinyelela ikhamera yefoni kusuka ku-<xliff:g id="DEVICE">%1$s</xliff:g> yakho"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ayikwazi ukufinyelela ikhamera yethebulethi kusuka ku-<xliff:g id="DEVICE">%1$s</xliff:g> yakho"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Lokhu akukwazi ukufinyelelwa ngenkathi usakaza. Zama efonini yakho kunalokho."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Ayikwazi ukubuka isithombe esiphakathi kwesithombe ngenkathi isakaza"</string> <string name="system_locale_title" msgid="711882686834677268">"Okuzenzakalelayo kwesistimu"</string> <string name="default_card_name" msgid="9198284935962911468">"IKHADI <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Imvume yephrofayela ye-Companion Watch yokuphatha amawashi"</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 34332a5c5363..59306a3a61ce 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4946,7 +4946,6 @@ <!-- For VirtualDeviceManager --> <java-symbol type="string" name="vdm_camera_access_denied" /> <java-symbol type="string" name="vdm_secure_window" /> - <java-symbol type="string" name="vdm_pip_blocked" /> <java-symbol type="color" name="camera_privacy_light_day"/> <java-symbol type="color" name="camera_privacy_light_night"/> diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java index 0525443ecf82..0c0747060f48 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; import android.util.Property; import android.view.View; +import androidx.annotation.NonNull; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import androidx.test.rule.ActivityTestRule; @@ -36,6 +37,8 @@ import org.junit.Rule; import org.junit.Test; import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; @SmallTest public class AnimatorSetActivityTest { @@ -613,6 +616,68 @@ public class AnimatorSetActivityTest { }); } + @Test + public void initAfterStartNotification() throws Throwable { + Property<int[], Integer> property = new Property<>(Integer.class, "firstValue") { + @Override + public Integer get(int[] target) { + throw new IllegalStateException("Shouldn't be called"); + } + + @Override + public void set(int[] target, Integer value) { + target[0] = value; + } + }; + int[] target = new int[1]; + ObjectAnimator animator1 = ObjectAnimator.ofInt(target, property, 0, 100); + ObjectAnimator animator2 = ObjectAnimator.ofInt(target, property, 0, 100); + ObjectAnimator animator3 = ObjectAnimator.ofInt(target, property, 0, 100); + animator1.setDuration(10); + animator2.setDuration(10); + animator3.setDuration(10); + AnimatorSet set = new AnimatorSet(); + set.playSequentially(animator1, animator2, animator3); + final int[] values = new int[4]; + animator2.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(@NonNull Animator animation, boolean isReverse) { + values[0] = target[0]; + animator2.setIntValues(target[0], target[0] + 100); + } + + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + values[1] = target[0]; + } + }); + animator3.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(@NonNull Animator animation, boolean isReverse) { + values[2] = target[0]; + animator3.setIntValues(target[0], target[0] + 100); + } + + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + values[3] = target[0]; + } + }); + final CountDownLatch latch = new CountDownLatch(1); + set.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + latch.countDown(); + } + }); + mActivityRule.runOnUiThread(() -> set.start()); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + assertEquals(100, values[0]); + assertEquals(200, values[1]); + assertEquals(200, values[2]); + assertEquals(300, values[3]); + } + /** * Check that the animator list contains exactly the given animators and nothing else. */ diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index b73a87c8f0d9..48577416b3d0 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -884,12 +884,13 @@ public class ActivityThreadTest { mConfig.setTo(config); ++mNumOfConfigChanges; - if (mConfigLatch != null) { + final CountDownLatch configLatch = mConfigLatch; + if (configLatch != null) { if (mTestLatch != null) { mTestLatch.countDown(); } try { - mConfigLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); + configLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); } catch (InterruptedException e) { throw new IllegalStateException(e); } diff --git a/core/tests/coretests/src/android/flags/FeatureFlagsTest.java b/core/tests/coretests/src/android/flags/FeatureFlagsTest.java deleted file mode 100644 index 3fc94394d12c..000000000000 --- a/core/tests/coretests/src/android/flags/FeatureFlagsTest.java +++ /dev/null @@ -1,155 +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 android.flags; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; - -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - - -@SmallTest -@Presubmit -public class FeatureFlagsTest { - - IFeatureFlagsFake mIFeatureFlagsFake = new IFeatureFlagsFake(); - FeatureFlags mFeatureFlags = new FeatureFlags(mIFeatureFlagsFake); - - @Before - public void setup() { - FeatureFlags.setInstance(mFeatureFlags); - } - - @Test - public void testFusedOff_Disabled() { - FusedOffFlag flag = FeatureFlags.fusedOffFlag("test", "a"); - assertThat(mFeatureFlags.isEnabled(flag)).isFalse(); - } - - @Test - public void testFusedOn_Enabled() { - FusedOnFlag flag = FeatureFlags.fusedOnFlag("test", "a"); - assertThat(mFeatureFlags.isEnabled(flag)).isTrue(); - } - - @Test - public void testBooleanFlag_DefaultDisabled() { - BooleanFlag flag = FeatureFlags.booleanFlag("test", "a", false); - assertThat(mFeatureFlags.isEnabled(flag)).isFalse(); - } - - @Test - public void testBooleanFlag_DefaultEnabled() { - BooleanFlag flag = FeatureFlags.booleanFlag("test", "a", true); - assertThat(mFeatureFlags.isEnabled(flag)).isTrue(); - } - - @Test - public void testDynamicBooleanFlag_DefaultDisabled() { - DynamicBooleanFlag flag = FeatureFlags.dynamicBooleanFlag("test", "a", false); - assertThat(mFeatureFlags.isCurrentlyEnabled(flag)).isFalse(); - } - - @Test - public void testDynamicBooleanFlag_DefaultEnabled() { - DynamicBooleanFlag flag = FeatureFlags.dynamicBooleanFlag("test", "a", true); - assertThat(mFeatureFlags.isCurrentlyEnabled(flag)).isTrue(); - } - - @Test - public void testBooleanFlag_OverrideBeforeRead() { - BooleanFlag flag = FeatureFlags.booleanFlag("test", "a", false); - SyncableFlag syncableFlag = new SyncableFlag( - flag.getNamespace(), flag.getName(), "true", false); - - mIFeatureFlagsFake.setFlagOverrides(List.of(syncableFlag)); - - assertThat(mFeatureFlags.isEnabled(flag)).isTrue(); - } - - @Test - public void testFusedOffFlag_OverrideHasNoEffect() { - FusedOffFlag flag = FeatureFlags.fusedOffFlag("test", "a"); - SyncableFlag syncableFlag = new SyncableFlag( - flag.getNamespace(), flag.getName(), "true", false); - - mIFeatureFlagsFake.setFlagOverrides(List.of(syncableFlag)); - - assertThat(mFeatureFlags.isEnabled(flag)).isFalse(); - } - - @Test - public void testFusedOnFlag_OverrideHasNoEffect() { - FusedOnFlag flag = FeatureFlags.fusedOnFlag("test", "a"); - SyncableFlag syncableFlag = new SyncableFlag( - flag.getNamespace(), flag.getName(), "false", false); - - mIFeatureFlagsFake.setFlagOverrides(List.of(syncableFlag)); - - assertThat(mFeatureFlags.isEnabled(flag)).isTrue(); - } - - @Test - public void testDynamicFlag_OverrideBeforeRead() { - DynamicBooleanFlag flag = FeatureFlags.dynamicBooleanFlag("test", "a", false); - SyncableFlag syncableFlag = new SyncableFlag( - flag.getNamespace(), flag.getName(), "true", true); - - mIFeatureFlagsFake.setFlagOverrides(List.of(syncableFlag)); - - // Changes to true - assertThat(mFeatureFlags.isCurrentlyEnabled(flag)).isTrue(); - } - - @Test - public void testDynamicFlag_OverrideAfterRead() { - DynamicBooleanFlag flag = FeatureFlags.dynamicBooleanFlag("test", "a", false); - SyncableFlag syncableFlag = new SyncableFlag( - flag.getNamespace(), flag.getName(), "true", true); - - // Starts false - assertThat(mFeatureFlags.isCurrentlyEnabled(flag)).isFalse(); - - mIFeatureFlagsFake.setFlagOverrides(List.of(syncableFlag)); - - // Changes to true - assertThat(mFeatureFlags.isCurrentlyEnabled(flag)).isTrue(); - } - - @Test - public void testDynamicFlag_FiresListener() { - DynamicBooleanFlag flag = FeatureFlags.dynamicBooleanFlag("test", "a", false); - AtomicBoolean called = new AtomicBoolean(false); - FeatureFlags.ChangeListener listener = flag1 -> called.set(true); - - mFeatureFlags.addChangeListener(listener); - - SyncableFlag syncableFlag = new SyncableFlag( - flag.getNamespace(), flag.getName(), flag.getDefault().toString(), true); - - mIFeatureFlagsFake.setFlagOverrides(List.of(syncableFlag)); - - // Fires listener. - assertThat(called.get()).isTrue(); - } -} diff --git a/core/tests/coretests/src/android/flags/IFeatureFlagsFake.java b/core/tests/coretests/src/android/flags/IFeatureFlagsFake.java deleted file mode 100644 index bc5d8aa3ac73..000000000000 --- a/core/tests/coretests/src/android/flags/IFeatureFlagsFake.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.flags; - -import android.os.IBinder; -import android.os.RemoteException; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -class IFeatureFlagsFake implements IFeatureFlags { - - private final Set<IFeatureFlagsCallback> mCallbacks = new HashSet<>(); - - List<SyncableFlag> mOverrides; - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public List<SyncableFlag> syncFlags(List<SyncableFlag> flagList) { - return mOverrides == null ? flagList : mOverrides; - } - - @Override - public List<SyncableFlag> queryFlags(List<SyncableFlag> flagList) { - return mOverrides == null ? flagList : mOverrides; } - - @Override - public void overrideFlag(SyncableFlag syncableFlag) { - SyncableFlag match = findFlag(syncableFlag); - if (match != null) { - mOverrides.remove(match); - } - - mOverrides.add(syncableFlag); - - for (IFeatureFlagsCallback cb : mCallbacks) { - try { - cb.onFlagChange(syncableFlag); - } catch (RemoteException e) { - // does not happen in fakes. - } - } - } - - @Override - public void resetFlag(SyncableFlag syncableFlag) { - SyncableFlag match = findFlag(syncableFlag); - if (match != null) { - mOverrides.remove(match); - } - - for (IFeatureFlagsCallback cb : mCallbacks) { - try { - cb.onFlagChange(syncableFlag); - } catch (RemoteException e) { - // does not happen in fakes. - } - } - } - - private SyncableFlag findFlag(SyncableFlag syncableFlag) { - SyncableFlag match = null; - for (SyncableFlag sf : mOverrides) { - if (sf.getName().equals(syncableFlag.getName()) - && sf.getNamespace().equals(syncableFlag.getNamespace())) { - match = sf; - break; - } - } - - return match; - } - @Override - public void registerCallback(IFeatureFlagsCallback callback) { - mCallbacks.add(callback); - } - - @Override - public void unregisterCallback(IFeatureFlagsCallback callback) { - mCallbacks.remove(callback); - } - - public void setFlagOverrides(List<SyncableFlag> flagList) { - mOverrides = flagList; - for (SyncableFlag sf : flagList) { - for (IFeatureFlagsCallback cb : mCallbacks) { - try { - cb.onFlagChange(sf); - } catch (RemoteException e) { - // does not happen in fakes. - } - } - } - } -} diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java index 184b9eac24f3..4f722cefcf9f 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java @@ -353,6 +353,23 @@ public class RemoteViewsAdapterTest { public boolean isCreated() { return false; } + + @Override + public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() { + RemoteViews.RemoteCollectionItems.Builder itemsBuilder = + new RemoteViews.RemoteCollectionItems.Builder(); + itemsBuilder.setHasStableIds(hasStableIds()) + .setViewTypeCount(getViewTypeCount()); + try { + for (int i = 0; i < mCount; i++) { + itemsBuilder.addItem(getItemId(i), getViewAt(i)); + } + } catch (RemoteException e) { + // No-op + } + + return itemsBuilder.build(); + } } private static class DistinctIntent extends Intent { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 76e0e1eb7a95..55eabb039c01 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -48,7 +48,7 @@ public class WindowExtensionsImpl implements WindowExtensions { // TODO(b/241126279) Introduce constants to better version functionality @Override public int getVendorApiLevel() { - return 3; + return 4; } @NonNull diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 18497ad249ee..381e9d472f0f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -32,7 +32,7 @@ import androidx.window.extensions.core.util.function.Function; */ class SplitContainer { @NonNull - private final TaskFragmentContainer mPrimaryContainer; + private TaskFragmentContainer mPrimaryContainer; @NonNull private final TaskFragmentContainer mSecondaryContainer; @NonNull @@ -46,17 +46,35 @@ class SplitContainer { @NonNull private final IBinder mToken; + /** + * Whether the selection of which container is primary can be changed at runtime. Runtime + * updates is currently possible only for {@link SplitPinContainer} + * + * @see SplitPinContainer + */ + private final boolean mIsPrimaryContainerMutable; + SplitContainer(@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes) { + this(primaryContainer, primaryActivity, secondaryContainer, splitRule, splitAttributes, + false /* isPrimaryContainerMutable */); + } + + SplitContainer(@NonNull TaskFragmentContainer primaryContainer, + @NonNull Activity primaryActivity, + @NonNull TaskFragmentContainer secondaryContainer, + @NonNull SplitRule splitRule, + @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) { mPrimaryContainer = primaryContainer; mSecondaryContainer = secondaryContainer; mSplitRule = splitRule; mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes(); mCurrentSplitAttributes = splitAttributes; mToken = new Binder("SplitContainer"); + mIsPrimaryContainerMutable = isPrimaryContainerMutable; if (shouldFinishPrimaryWithSecondary(splitRule)) { if (mPrimaryContainer.getRunningActivityCount() == 1 @@ -74,6 +92,13 @@ class SplitContainer { } } + void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) { + if (!mIsPrimaryContainerMutable) { + throw new IllegalStateException("Cannot update primary TaskFragmentContainer"); + } + mPrimaryContainer = primaryContainer; + } + @NonNull TaskFragmentContainer getPrimaryContainer() { return mPrimaryContainer; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 4cedd41e2d9a..a2f75e099465 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -213,6 +213,56 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } @Override + public boolean pinTopActivityStack(int taskId, @NonNull SplitPinRule splitPinRule) { + synchronized (mLock) { + final TaskContainer task = getTaskContainer(taskId); + if (task == null) { + Log.e(TAG, "Cannot find the task for id: " + taskId); + return false; + } + + final TaskFragmentContainer topContainer = + task.getTopNonFinishingTaskFragmentContainer(); + // Cannot pin the TaskFragment if no other TaskFragment behind it. + if (topContainer == null || task.indexOf(topContainer) <= 0) { + Log.w(TAG, "Cannot find an ActivityStack to pin or split"); + return false; + } + // Abort if the top container is already pinned. + if (task.getSplitPinContainer() != null) { + Log.w(TAG, "There is already a pinned ActivityStack."); + return false; + } + + // Find a valid adjacent TaskFragmentContainer + final TaskFragmentContainer primaryContainer = + task.getNonFinishingTaskFragmentContainerBelow(topContainer); + if (primaryContainer == null) { + Log.w(TAG, "Cannot find another ActivityStack to split"); + return false; + } + + // Registers a Split + final SplitPinContainer splitPinContainer = new SplitPinContainer(primaryContainer, + topContainer, splitPinRule, splitPinRule.getDefaultSplitAttributes()); + task.addSplitContainer(splitPinContainer); + + // Updates the Split + final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction(); + final WindowContainerTransaction wct = transactionRecord.getTransaction(); + mPresenter.updateSplitContainer(splitPinContainer, wct); + transactionRecord.apply(false /* shouldApplyIndependently */); + updateCallbackIfNecessary(); + return true; + } + } + + @Override + public void unpinTopActivityStack(int taskId){ + // TODO + } + + @Override public void setSplitAttributesCalculator( @NonNull Function<SplitAttributesCalculatorParams, SplitAttributes> calculator) { synchronized (mLock) { @@ -672,7 +722,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (targetContainer == null) { // When there is no embedding rule matched, try to place it in the top container // like a normal launch. - targetContainer = taskContainer.getTopTaskFragmentContainer(); + targetContainer = taskContainer.getTopNonFinishingTaskFragmentContainer(); } if (targetContainer == null) { return; @@ -791,7 +841,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen final TaskFragmentContainer container = getContainerWithActivity(activity); if (!isOnReparent && container != null - && container.getTaskContainer().getTopTaskFragmentContainer() != container) { + && container.getTaskContainer().getTopNonFinishingTaskFragmentContainer() + != container) { // Do not resolve if the launched activity is not the top-most container in the Task. return true; } @@ -888,7 +939,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (taskContainer == null) { return; } - final TaskFragmentContainer targetContainer = taskContainer.getTopTaskFragmentContainer(); + final TaskFragmentContainer targetContainer = + taskContainer.getTopNonFinishingTaskFragmentContainer(); if (targetContainer == null) { return; } @@ -1213,11 +1265,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // 3. Whether the top activity (if any) should be split with the new activity intent. final TaskContainer taskContainer = getTaskContainer(taskId); - if (taskContainer == null || taskContainer.getTopTaskFragmentContainer() == null) { + if (taskContainer == null + || taskContainer.getTopNonFinishingTaskFragmentContainer() == null) { // There is no other activity in the Task to check split with. return null; } - final TaskFragmentContainer topContainer = taskContainer.getTopTaskFragmentContainer(); + final TaskFragmentContainer topContainer = + taskContainer.getTopNonFinishingTaskFragmentContainer(); final Activity topActivity = topContainer.getTopNonFinishingActivity(); if (topActivity != null && topActivity != launchingActivity) { final TaskFragmentContainer container = getSecondaryContainerForSplitIfAny(wct, @@ -1567,6 +1621,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // background. return; } + final SplitContainer splitContainer = getActiveSplitForContainer(container); + if (splitContainer instanceof SplitPinContainer + && updateSplitContainerIfNeeded(splitContainer, wct, null /* splitAttributes */)) { + // A SplitPinContainer exists and is updated. + return; + } if (launchPlaceholderIfNecessary(wct, container)) { // Placeholder was launched, the positions will be updated when the activity is added // to the secondary container. @@ -1579,7 +1639,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // If the info is not available yet the task fragment will be expanded when it's ready return; } - SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer == null) { return; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java new file mode 100644 index 000000000000..03c77a089012 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.window.extensions.embedding; + +import androidx.annotation.NonNull; + +/** + * Client-side descriptor of a split that holds two containers while the secondary + * container is pinned on top of the Task and the primary container is the container that is + * currently below the secondary container. The primary container could be updated to + * another container whenever the existing primary container is removed or no longer + * be the container that's right behind the secondary container. + */ +class SplitPinContainer extends SplitContainer { + + SplitPinContainer(@NonNull TaskFragmentContainer primaryContainer, + @NonNull TaskFragmentContainer secondaryContainer, + @NonNull SplitPinRule splitPinRule, + @NonNull SplitAttributes splitAttributes) { + super(primaryContainer, primaryContainer.getTopNonFinishingActivity(), secondaryContainer, + splitPinRule, splitAttributes, true /* isPrimaryContainerMutable */); + } + + @Override + public String toString() { + return "SplitPinContainer{" + + " primaryContainer=" + getPrimaryContainer() + + " secondaryContainer=" + getSecondaryContainer() + + " splitPinRule=" + getSplitRule() + + " splitAttributes" + getCurrentSplitAttributes() + + "}"; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 53d39d9fa28e..4dafbd17f379 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -17,6 +17,7 @@ package androidx.window.extensions.embedding; import static android.content.pm.PackageManager.MATCH_ALL; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import android.app.Activity; import android.app.ActivityThread; @@ -39,6 +40,7 @@ import android.view.WindowInsets; import android.view.WindowMetrics; import android.window.TaskFragmentAnimationParams; import android.window.TaskFragmentCreationParams; +import android.window.TaskFragmentOperation; import android.window.WindowContainerTransaction; import androidx.annotation.IntDef; @@ -336,10 +338,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { // value. final SplitRule rule = splitContainer.getSplitRule(); final TaskFragmentContainer primaryContainer = splitContainer.getPrimaryContainer(); - final Activity activity = primaryContainer.getTopNonFinishingActivity(); - if (activity == null) { - return; - } final TaskContainer taskContainer = splitContainer.getTaskContainer(); final TaskProperties taskProperties = taskContainer.getTaskProperties(); final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes(); @@ -424,6 +422,16 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { container.setLastRequestedBounds(fragmentOptions.getInitialRelativeBounds()); container.setLastRequestedWindowingMode(fragmentOptions.getWindowingMode()); super.createTaskFragment(wct, fragmentOptions); + + // Reorders the pinned TaskFragment to front to ensure it is the front-most TaskFragment. + final SplitPinContainer pinnedContainer = + container.getTaskContainer().getSplitPinContainer(); + if (pinnedContainer != null) { + final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( + OP_TYPE_REORDER_TO_FRONT).build(); + wct.addTaskFragmentOperation( + pinnedContainer.getSecondaryContainer().getTaskFragmentToken(), operation); + } } @Override diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 4580c9836168..969e3ed5b9b6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -57,6 +57,10 @@ class TaskContainer { @NonNull private final List<SplitContainer> mSplitContainers = new ArrayList<>(); + /** Active pin split pair in this Task. */ + @Nullable + private SplitPinContainer mSplitPinContainer; + @NonNull private final Configuration mConfiguration; @@ -174,11 +178,28 @@ class TaskContainer { } @Nullable - TaskFragmentContainer getTopTaskFragmentContainer() { - if (mContainers.isEmpty()) { - return null; + TaskFragmentContainer getTopNonFinishingTaskFragmentContainer() { + for (int i = mContainers.size() - 1; i >= 0; i--) { + final TaskFragmentContainer container = mContainers.get(i); + if (!container.isFinished()) { + return container; + } + } + return null; + } + + /** Gets a non-finishing container below the given one. */ + @Nullable + TaskFragmentContainer getNonFinishingTaskFragmentContainerBelow( + @NonNull TaskFragmentContainer current) { + final int index = mContainers.indexOf(current); + for (int i = index - 1; i >= 0; i--) { + final TaskFragmentContainer container = mContainers.get(i); + if (!container.isFinished()) { + return container; + } } - return mContainers.get(mContainers.size() - 1); + return null; } @Nullable @@ -217,31 +238,57 @@ class TaskContainer { } void addSplitContainer(@NonNull SplitContainer splitContainer) { + if (splitContainer instanceof SplitPinContainer) { + mSplitPinContainer = (SplitPinContainer) splitContainer; + mSplitContainers.add(splitContainer); + return; + } + + // Keeps the SplitPinContainer on the top of the list. + mSplitContainers.remove(mSplitPinContainer); mSplitContainers.add(splitContainer); + if (mSplitPinContainer != null) { + mSplitContainers.add(mSplitPinContainer); + } } void removeSplitContainers(@NonNull List<SplitContainer> containers) { mSplitContainers.removeAll(containers); } + void removeSplitPinContainer() { + mSplitContainers.remove(mSplitPinContainer); + mSplitPinContainer = null; + } + + @Nullable + SplitPinContainer getSplitPinContainer() { + return mSplitPinContainer; + } + void addTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.add(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void addTaskFragmentContainer(int index, @NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.add(index, taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void removeTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.remove(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void removeTaskFragmentContainers(@NonNull List<TaskFragmentContainer> taskFragmentContainer) { mContainers.removeAll(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void clearTaskFragmentContainer() { mContainers.clear(); + onTaskFragmentContainerUpdated(); } /** @@ -254,6 +301,34 @@ class TaskContainer { return mContainers; } + private void onTaskFragmentContainerUpdated() { + if (mSplitPinContainer == null) { + return; + } + + final TaskFragmentContainer pinnedContainer = mSplitPinContainer.getSecondaryContainer(); + final int pinnedContainerIndex = mContainers.indexOf(pinnedContainer); + if (pinnedContainerIndex <= 0) { + removeSplitPinContainer(); + return; + } + + // Ensure the pinned container is top-most. + if (pinnedContainerIndex != mContainers.size() - 1) { + mContainers.remove(pinnedContainer); + mContainers.add(pinnedContainer); + } + + // Update the primary container adjacent to the pinned container if needed. + final TaskFragmentContainer adjacentContainer = + getNonFinishingTaskFragmentContainerBelow(pinnedContainer); + if (adjacentContainer == null) { + removeSplitPinContainer(); + } else if (mSplitPinContainer.getPrimaryContainer() != adjacentContainer) { + mSplitPinContainer.setPrimaryContainer(adjacentContainer); + } + } + /** Adds the descriptors of split states in this Task to {@code outSplitStates}. */ void getSplitStates(@NonNull List<SplitInfo> outSplitStates) { for (SplitContainer container : mSplitContainers) { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 9e264726a65a..9af1fe916279 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -1462,6 +1462,51 @@ public class SplitControllerTest { verify(testRecord).apply(eq(false)); } + @Test + public void testPinTopActivityStack() { + // Create two activities. + final Activity primaryActivity = createMockActivity(); + final Activity secondaryActivity = createMockActivity(); + + // Unable to pin if not being embedded. + SplitPinRule splitPinRule = new SplitPinRule.Builder(new SplitAttributes.Builder().build(), + parentWindowMetrics -> true /* parentWindowMetricsPredicate */).build(); + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Split the two activities. + addSplitTaskFragments(primaryActivity, secondaryActivity); + final TaskFragmentContainer primaryContainer = + mSplitController.getContainerWithActivity(primaryActivity); + spyOn(primaryContainer); + + // Unable to pin if no valid TaskFragment. + doReturn(true).when(primaryContainer).isFinished(); + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Otherwise, should pin successfully. + doReturn(false).when(primaryContainer).isFinished(); + assertTrue(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Unable to pin if there is already a pinned TaskFragment + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Unable to pin on an unknown Task. + assertFalse(mSplitController.pinTopActivityStack(TASK_ID + 1, splitPinRule)); + + // Gets the current size of all the SplitContainers. + final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID); + final int splitContainerCount = taskContainer.getSplitContainers().size(); + + // Create another activity and split with primary activity. + final Activity thirdActivity = createMockActivity(); + addSplitTaskFragments(primaryActivity, thirdActivity); + + // Ensure another SplitContainer is added and the pinned TaskFragment still on top + assertTrue(taskContainer.getSplitContainers().size() == splitContainerCount + +1); + assertTrue(mSplitController.getTopActiveContainer(TASK_ID).getTopNonFinishingActivity() + == secondaryActivity); + } + /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { return createMockActivity(TASK_ID); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index 11af1d1f20e1..000c65a75c81 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -135,15 +135,15 @@ public class TaskContainerTest { @Test public void testGetTopTaskFragmentContainer() { final TaskContainer taskContainer = createTestTaskContainer(); - assertNull(taskContainer.getTopTaskFragmentContainer()); + assertNull(taskContainer.getTopNonFinishingTaskFragmentContainer()); final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */, new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */); - assertEquals(tf0, taskContainer.getTopTaskFragmentContainer()); + assertEquals(tf0, taskContainer.getTopNonFinishingTaskFragmentContainer()); final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */); - assertEquals(tf1, taskContainer.getTopTaskFragmentContainer()); + assertEquals(tf1, taskContainer.getTopNonFinishingTaskFragmentContainer()); } @Test diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 6385fd3fd811..8def8ff1ab01 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1075,9 +1075,9 @@ public class BubbleController implements ConfigurationChangeListener, * <p>This is used by external callers (launcher). */ @VisibleForTesting - public void expandStackAndSelectBubbleFromLauncher(String key, int bubbleBarXCoordinate, - int bubbleBarYCoordinate) { - mBubblePositioner.setBubbleBarPosition(bubbleBarXCoordinate, bubbleBarYCoordinate); + public void expandStackAndSelectBubbleFromLauncher(String key, int bubbleBarOffsetX, + int bubbleBarOffsetY) { + mBubblePositioner.setBubbleBarPosition(bubbleBarOffsetX, bubbleBarOffsetY); if (BubbleOverflow.KEY.equals(key)) { mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow()); @@ -2088,10 +2088,10 @@ public class BubbleController implements ConfigurationChangeListener, } @Override - public void showBubble(String key, int bubbleBarXCoordinate, int bubbleBarYCoordinate) { + public void showBubble(String key, int bubbleBarOffsetX, int bubbleBarOffsetY) { mMainExecutor.execute( () -> mController.expandStackAndSelectBubbleFromLauncher( - key, bubbleBarXCoordinate, bubbleBarYCoordinate)); + key, bubbleBarOffsetX, bubbleBarOffsetY)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 6a5e3104fed5..ee6996d3d23d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -718,9 +718,16 @@ public class BubblePositioner { mShowingInBubbleBar = showingInBubbleBar; } - /** Sets the position of the bubble bar in screen coordinates. */ - public void setBubbleBarPosition(int x, int y) { - mBubbleBarPosition.set(x, y); + /** + * Sets the position of the bubble bar in screen coordinates. + * + * @param offsetX the offset of the bubble bar from the edge of the screen on the X axis + * @param offsetY the offset of the bubble bar from the edge of the screen on the Y axis + */ + public void setBubbleBarPosition(int offsetX, int offsetY) { + mBubbleBarPosition.set( + getAvailableRect().width() - offsetX, + getAvailableRect().height() + mInsets.top + mInsets.bottom - offsetY); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl index 59332f4be627..351319f5fb5e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl @@ -29,8 +29,7 @@ interface IBubbles { oneway void unregisterBubbleListener(in IBubblesListener listener) = 2; - oneway void showBubble(in String key, in int bubbleBarXCoordinate, - in int bubbleBarYCoordinate) = 3; + oneway void showBubble(in String key, in int bubbleBarOffsetX, in int bubbleBarOffsetY) = 3; oneway void removeBubble(in String key, in int reason) = 4; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index b14c3c10846b..08da4857a0b0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -1927,6 +1927,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, pw.println(innerPrefix + "mLeash=" + mLeash); pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState()); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); + mPipTransitionController.dump(pw, innerPrefix); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 73eb62ae47e9..e3d53fc415db 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -72,6 +72,7 @@ import com.android.wm.shell.transition.CounterRotatorHelper; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.TransitionUtil; +import java.io.PrintWriter; import java.util.Optional; /** @@ -451,6 +452,9 @@ public class PipTransition extends PipTransitionController { @Override public void forceFinishTransition() { + // mFinishCallback might be null with an outdated mCurrentPipTaskToken + // for example, when app crashes while in PiP and exit transition has not started + mCurrentPipTaskToken = null; if (mFinishCallback == null) return; mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */); mFinishCallback = null; @@ -1137,4 +1141,12 @@ public class PipTransition extends PipTransitionController { PipMenuController.ALPHA_NO_CHANGE); mPipMenuController.updateMenuBounds(destinationBounds); } + + @Override + public void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + TAG); + pw.println(innerPrefix + "mCurrentPipTaskToken=" + mCurrentPipTaskToken); + pw.println(innerPrefix + "mFinishCallback=" + mFinishCallback); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index e1bcd70c256b..63627938ec87 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -42,6 +42,7 @@ import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -283,4 +284,9 @@ public abstract class PipTransitionController implements Transitions.TransitionH */ void onPipTransitionCanceled(int direction); } + + /** + * Dumps internal states. + */ + public void dump(PrintWriter pw, String prefix) {} } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java index 5f54f58557d1..56c0d0e67cab 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java @@ -38,8 +38,6 @@ public class ShellSharedConstants { public static final String KEY_EXTRA_SHELL_RECENT_TASKS = "extra_shell_recent_tasks"; // See IBackAnimation.aidl public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation"; - // See IFloatingTasks.aidl - public static final String KEY_EXTRA_SHELL_FLOATING_TASKS = "extra_shell_floating_tasks"; // See IDesktopMode.aidl public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode"; // See IDragAndDrop.aidl diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt index 96bfb78eff0d..d1dd8df81a3e 100644 --- a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt @@ -23,6 +23,7 @@ import android.content.ComponentName import android.util.Log import com.android.dream.lowlight.dagger.LowLightDreamModule import com.android.dream.lowlight.dagger.qualifiers.Application +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.TimeoutCancellationException @@ -103,6 +104,11 @@ class LowLightDreamManager @Inject constructor( ) } catch (ex: TimeoutCancellationException) { Log.e(TAG, "timed out while waiting for low light animation", ex) + } catch (ex: CancellationException) { + Log.w(TAG, "low light transition animation cancelled") + // Catch the cancellation so that we still set the system dream component if the + // animation is cancelled, such as by a user tapping to wake as the transition to + // low light happens. } dreamManager.setSystemDreamComponent( if (shouldEnterLowLight) lowLightDreamComponent else null diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt index 473603002b21..de1aee598667 100644 --- a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt @@ -110,15 +110,5 @@ class LowLightTransitionCoordinator @Inject constructor() { } } animator.addListener(listener) - continuation.invokeOnCancellation { - try { - animator.removeListener(listener) - animator.cancel() - } catch (exception: IndexOutOfBoundsException) { - // TODO(b/285666217): remove this try/catch once a proper fix is implemented. - // Cancelling the animator can cause an exception since we may be removing a - // listener during the cancellation. See b/285666217 for more details. - } - } } } diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt new file mode 100644 index 000000000000..f69c84dafbb2 --- /dev/null +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dream.lowlight.util + +import android.view.animation.Interpolator + +/** + * Interpolator wrapper that shortens another interpolator from its original duration to a portion + * of that duration. + * + * For example, an `originalDuration` of 1000 and a `newDuration` of 200 results in an animation + * that when played for 200ms is the exact same as the first 200ms of a 1000ms animation if using + * the original interpolator. + * + * This is useful for the transition between the user dream and the low light clock as some + * animations are defined in the spec to be longer than the total duration of the animation. For + * example, the low light clock exit translation animation is defined to last >1s while the actual + * fade out of the low light clock is only 250ms, meaning the clock isn't visible anymore after + * 250ms. + * + * Since the dream framework currently only allows one dream to be visible and running, we use this + * interpolator to play just the first 250ms of the translation animation. Simply reducing the + * duration of the animation would result in the text exiting much faster than intended, so a custom + * interpolator is needed. + */ +class TruncatedInterpolator( + private val baseInterpolator: Interpolator, + originalDuration: Float, + newDuration: Float +) : Interpolator { + private val scaleFactor: Float + + init { + scaleFactor = newDuration / originalDuration + } + + override fun getInterpolation(input: Float): Float { + return baseInterpolator.getInterpolation(input * scaleFactor) + } +} diff --git a/libs/dream/lowlight/tests/Android.bp b/libs/dream/lowlight/tests/Android.bp index 2d79090cd7d4..64b53cbb5c5a 100644 --- a/libs/dream/lowlight/tests/Android.bp +++ b/libs/dream/lowlight/tests/Android.bp @@ -27,6 +27,7 @@ android_test { "androidx.test.runner", "androidx.test.rules", "androidx.test.ext.junit", + "animationlib", "frameworks-base-testutils", "junit", "kotlinx_coroutines_test", diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt index 2a886bc31788..de84adb2e5c2 100644 --- a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt +++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt @@ -152,6 +152,21 @@ class LowLightDreamManagerTest { verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) } + @Test + fun setAmbientLightMode_animationCancelled_SetsSystemDream() = testScope.runTest { + mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT) + runCurrent() + cancelEnterAnimations() + runCurrent() + // Animation never finishes, but we should still set the system dream + verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) + } + + private fun cancelEnterAnimations() { + val listener = withArgCaptor { verify(mEnterAnimator).addListener(capture()) } + listener.onAnimationCancel(mEnterAnimator) + } + private fun completeEnterAnimations() { val listener = withArgCaptor { verify(mEnterAnimator).addListener(capture()) } listener.onAnimationEnd(mEnterAnimator) diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt index 4c526a6ac69d..9ae304f9763a 100644 --- a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt +++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt @@ -158,26 +158,6 @@ class LowLightTransitionCoordinatorTest { assertThat(job.isCancelled).isTrue() } - @Test - fun shouldCancelAnimatorWhenJobCancelled() = testScope.runTest { - whenever(mEnterListener.onBeforeEnterLowLight()).thenReturn(mAnimator) - val coordinator = LowLightTransitionCoordinator() - coordinator.setLowLightEnterListener(mEnterListener) - val job = launch { - coordinator.waitForLowLightTransitionAnimation(timeout = TIMEOUT, entering = true) - } - runCurrent() - // Animator listener is added and the runnable is not run yet. - verify(mAnimator).addListener(mAnimatorListenerCaptor.capture()) - verify(mAnimator, never()).cancel() - assertThat(job.isCompleted).isFalse() - - job.cancel() - // We should have removed the listener and cancelled the animator - verify(mAnimator).removeListener(mAnimatorListenerCaptor.value) - verify(mAnimator).cancel() - } - companion object { private val TIMEOUT = 1.toDuration(DurationUnit.SECONDS) } diff --git a/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt b/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt new file mode 100644 index 000000000000..190f02e97136 --- /dev/null +++ b/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.dream.lowlight.util + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.app.animation.Interpolators +import com.google.common.truth.Truth +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class TruncatedInterpolatorTest { + @Test + fun truncatedInterpolator_matchesRegularInterpolator() { + val originalInterpolator = Interpolators.EMPHASIZED + val truncatedInterpolator = + TruncatedInterpolator(originalInterpolator, ORIGINAL_DURATION_MS, NEW_DURATION_MS) + + // Both interpolators should start at the same value. + var animationPercent = 0f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent)) + + animationPercent = 1f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent * DURATION_RATIO)) + + animationPercent = 0.25f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent * DURATION_RATIO)) + } + + companion object { + private const val ORIGINAL_DURATION_MS: Float = 1000f + private const val NEW_DURATION_MS: Float = 200f + private const val DURATION_RATIO: Float = NEW_DURATION_MS / ORIGINAL_DURATION_MS + } +} diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index 01e6bf00a664..bb8002a18c11 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -53,6 +53,20 @@ ] }, { + "name": "SystemUIGoogleScreenshotTests", + "options": [ + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.Postsubmit" + } + ] + }, + { // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+) "name": "SystemUIGoogleBiometricsScreenshotTests", "options": [ @@ -131,5 +145,21 @@ } ] } + ], + "postsubmit": [ + { + "name": "SystemUIGoogleScreenshotTests", + "options": [ + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + { + "include-annotation": "android.platform.test.annotations.Postsubmit" + } + ] + } ] } diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group.xml b/packages/SystemUI/res/layout/status_bar_wifi_group.xml deleted file mode 100644 index 6cb6993bb762..000000000000 --- a/packages/SystemUI/res/layout/status_bar_wifi_group.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2018, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<com.android.systemui.statusbar.StatusBarWifiView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/wifi_combo" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:gravity="center_vertical" > - - <include layout="@layout/status_bar_wifi_group_inner" /> - -</com.android.systemui.statusbar.StatusBarWifiView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml index ab52465be63c..3baae3376bd0 100644 --- a/packages/SystemUI/res/layout/zen_mode_condition.xml +++ b/packages/SystemUI/res/layout/zen_mode_condition.xml @@ -15,6 +15,7 @@ limitations under the License. --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:theme="@style/Theme.SystemUI.QuickSettings" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipChildren="false" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt index 0fbeb1a054a7..f3296f0632bb 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt @@ -32,7 +32,7 @@ open class RegionSampler @JvmOverloads constructor( val sampledView: View, - mainExecutor: Executor?, + val mainExecutor: Executor?, val bgExecutor: Executor?, val regionSamplingEnabled: Boolean, val isLockscreen: Boolean = false, @@ -166,7 +166,7 @@ constructor( if (isLockscreen) WallpaperManager.FLAG_LOCK else WallpaperManager.FLAG_SYSTEM ) - onColorsChanged(sampledRegionWithOffset, initialSampling) + mainExecutor?.execute { onColorsChanged(sampledRegionWithOffset, initialSampling) } } ) } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index ca064efd4f76..4b14d3cff718 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -42,8 +42,6 @@ public class QuickStepContract { "com.google.android.apps.nexuslauncher.NexusLauncherActivity"; public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy"; - public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius"; - public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners"; public static final String KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER = "extra_unfold_animation"; // See ISysuiUnlockAnimationController.aidl public static final String KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER = "unlock_animation"; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java deleted file mode 100644 index 74c325dea15c..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ /dev/null @@ -1,541 +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.systemui.shared.system; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; -import static android.view.WindowManager.TRANSIT_SLEEP; - -import android.annotation.SuppressLint; -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.app.IApplicationThread; -import android.graphics.Rect; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.ArrayMap; -import android.util.Log; -import android.view.IRecentsAnimationController; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.window.IRemoteTransition; -import android.window.IRemoteTransitionFinishedCallback; -import android.window.PictureInPictureSurfaceTransaction; -import android.window.RemoteTransition; -import android.window.TaskSnapshot; -import android.window.TransitionInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.wm.shell.util.TransitionUtil; - -import java.util.ArrayList; -import java.util.HashMap; - -/** - * Helper class to build {@link RemoteTransition} objects - */ -public class RemoteTransitionCompat { - private static final String TAG = "RemoteTransitionCompat"; - - /** Constructor specifically for recents animation */ - public static RemoteTransition newRemoteTransition(RecentsAnimationListener recents, - IApplicationThread appThread) { - IRemoteTransition remote = new IRemoteTransition.Stub() { - final RecentsControllerWrap mRecentsSession = new RecentsControllerWrap(); - IBinder mToken = null; - - @Override - public void startAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, - IRemoteTransitionFinishedCallback finishedCallback) { - // TODO(b/177438007): Move this set-up logic into launcher's animation impl. - mToken = transition; - mRecentsSession.start(recents, mToken, info, t, finishedCallback); - } - - @Override - public void mergeAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, IBinder mergeTarget, - IRemoteTransitionFinishedCallback finishedCallback) { - if (mergeTarget.equals(mToken) && mRecentsSession.merge(info, t)) { - try { - finishedCallback.onTransitionFinished(null /* wct */, null /* sct */); - } catch (RemoteException e) { - Log.e(TAG, "Error merging transition.", e); - } - // commit taskAppeared after merge transition finished. - mRecentsSession.commitTasksAppearedIfNeeded(); - } else { - t.close(); - info.releaseAllSurfaces(); - } - } - }; - return new RemoteTransition(remote, appThread, "Recents"); - } - - /** - * Wrapper to hook up parts of recents animation to shell transition. - * TODO(b/177438007): Remove this once Launcher handles shell transitions directly. - */ - @VisibleForTesting - static class RecentsControllerWrap extends IRecentsAnimationController.Default { - private RecentsAnimationListener mListener = null; - private IRemoteTransitionFinishedCallback mFinishCB = null; - - /** - * List of tasks that we are switching away from via this transition. Upon finish, these - * pausing tasks will become invisible. - * These need to be ordered since the order must be restored if there is no task-switch. - */ - private ArrayList<TaskState> mPausingTasks = null; - - /** - * List of tasks that we are switching to. Upon finish, these will remain visible and - * on top. - */ - private ArrayList<TaskState> mOpeningTasks = null; - - private WindowContainerToken mPipTask = null; - private WindowContainerToken mRecentsTask = null; - private int mRecentsTaskId = 0; - private TransitionInfo mInfo = null; - private boolean mOpeningSeparateHome = false; - private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null; - private PictureInPictureSurfaceTransaction mPipTransaction = null; - private IBinder mTransition = null; - private boolean mKeyguardLocked = false; - private RemoteAnimationTarget[] mAppearedTargets; - private boolean mWillFinishToHome = false; - - /** The animation is idle, waiting for the user to choose a task to switch to. */ - private static final int STATE_NORMAL = 0; - - /** The user chose a new task to switch to and the animation is animating to it. */ - private static final int STATE_NEW_TASK = 1; - - /** The latest state that the recents animation is operating in. */ - private int mState = STATE_NORMAL; - - void start(RecentsAnimationListener listener, - IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, - IRemoteTransitionFinishedCallback finishedCallback) { - if (mInfo != null) { - throw new IllegalStateException("Trying to run a new recents animation while" - + " recents is already active."); - } - mListener = listener; - mInfo = info; - mFinishCB = finishedCallback; - mPausingTasks = new ArrayList<>(); - mOpeningTasks = new ArrayList<>(); - mPipTask = null; - mRecentsTask = null; - mRecentsTaskId = -1; - mLeashMap = new ArrayMap<>(); - mTransition = transition; - mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0; - mState = STATE_NORMAL; - - final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>(); - final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>(); - TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter(); - // About layering: we divide up the "layer space" into 3 regions (each the size of - // the change count). This lets us categorize things into above/below/between - // while maintaining their relative ordering. - for (int i = 0; i < info.getChanges().size(); ++i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (TransitionUtil.isWallpaper(change)) { - final RemoteAnimationTarget target = TransitionUtil.newTarget(change, - // wallpapers go into the "below" layer space - info.getChanges().size() - i, info, t, mLeashMap); - wallpapers.add(target); - // Make all the wallpapers opaque since we want them visible from the start - t.setAlpha(target.leash, 1); - } else if (leafTaskFilter.test(change)) { - // start by putting everything into the "below" layer space. - final RemoteAnimationTarget target = TransitionUtil.newTarget(change, - info.getChanges().size() - i, info, t, mLeashMap); - apps.add(target); - if (TransitionUtil.isClosingType(change.getMode())) { - // raise closing (pausing) task to "above" layer so it isn't covered - t.setLayer(target.leash, info.getChanges().size() * 3 - i); - mPausingTasks.add(new TaskState(change, target.leash)); - if (taskInfo.pictureInPictureParams != null - && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) { - mPipTask = taskInfo.token; - } - } else if (taskInfo != null - && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) { - // There's a 3p launcher, so make sure recents goes above that. - t.setLayer(target.leash, info.getChanges().size() * 3 - i); - mRecentsTask = taskInfo.token; - mRecentsTaskId = taskInfo.taskId; - } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - mRecentsTask = taskInfo.token; - mRecentsTaskId = taskInfo.taskId; - } else if (TransitionUtil.isOpeningType(change.getMode())) { - mOpeningTasks.add(new TaskState(change, target.leash)); - } - } - } - t.apply(); - mListener.onAnimationStart(new RecentsAnimationControllerCompat(this), - apps.toArray(new RemoteAnimationTarget[apps.size()]), - wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]), - new Rect(0, 0, 0, 0), new Rect()); - } - - @SuppressLint("NewApi") - boolean merge(TransitionInfo info, SurfaceControl.Transaction t) { - if (info.getType() == TRANSIT_SLEEP) { - // A sleep event means we need to stop animations immediately, so cancel here. - mListener.onAnimationCanceled(new HashMap<>()); - finish(mWillFinishToHome, false /* userLeaveHint */); - return false; - } - ArrayList<TransitionInfo.Change> openingTasks = null; - ArrayList<TransitionInfo.Change> closingTasks = null; - mAppearedTargets = null; - mOpeningSeparateHome = false; - TransitionInfo.Change recentsOpening = null; - boolean foundRecentsClosing = false; - boolean hasChangingApp = false; - final TransitionUtil.LeafTaskFilter leafTaskFilter = - new TransitionUtil.LeafTaskFilter(); - for (int i = 0; i < info.getChanges().size(); ++i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - final boolean isLeafTask = leafTaskFilter.test(change); - if (TransitionUtil.isOpeningType(change.getMode())) { - if (mRecentsTask.equals(change.getContainer())) { - recentsOpening = change; - } else if (isLeafTask) { - if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - // This is usually a 3p launcher - mOpeningSeparateHome = true; - } - if (openingTasks == null) { - openingTasks = new ArrayList<>(); - } - openingTasks.add(change); - } - } else if (TransitionUtil.isClosingType(change.getMode())) { - if (mRecentsTask.equals(change.getContainer())) { - foundRecentsClosing = true; - } else if (isLeafTask) { - if (closingTasks == null) { - closingTasks = new ArrayList<>(); - } - closingTasks.add(change); - } - } else if (change.getMode() == TRANSIT_CHANGE) { - // Finish recents animation if the display is changed, so the default - // transition handler can play the animation such as rotation effect. - if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)) { - mListener.onSwitchToScreenshot(() -> finish(false /* toHome */, - false /* userLeaveHint */)); - return false; - } - hasChangingApp = true; - } - } - if (hasChangingApp && foundRecentsClosing) { - // This happens when a visible app is expanding (usually PiP). In this case, - // that transition probably has a special-purpose animation, so finish recents - // now and let it do its animation (since recents is going to be occluded). - if (!mListener.onSwitchToScreenshot( - () -> finish(true /* toHome */, false /* userLeaveHint */))) { - Log.w(TAG, "Recents callback doesn't support support switching to screenshot" - + ", there might be a flicker."); - finish(true /* toHome */, false /* userLeaveHint */); - } - return false; - } - if (recentsOpening != null) { - // the recents task re-appeared. This happens if the user gestures before the - // task-switch (NEW_TASK) animation finishes. - if (mState == STATE_NORMAL) { - Log.e(TAG, "Returning to recents while recents is already idle."); - } - if (closingTasks == null || closingTasks.size() == 0) { - Log.e(TAG, "Returning to recents without closing any opening tasks."); - } - // Setup may hide it initially since it doesn't know that overview was still active. - t.show(recentsOpening.getLeash()); - t.setAlpha(recentsOpening.getLeash(), 1.f); - mState = STATE_NORMAL; - } - boolean didMergeThings = false; - if (closingTasks != null) { - // Cancelling a task-switch. Move the tasks back to mPausing from mOpening - for (int i = 0; i < closingTasks.size(); ++i) { - final TransitionInfo.Change change = closingTasks.get(i); - int openingIdx = TaskState.indexOf(mOpeningTasks, change); - if (openingIdx < 0) { - Log.e(TAG, "Back to existing recents animation from an unrecognized " - + "task: " + change.getTaskInfo().taskId); - continue; - } - mPausingTasks.add(mOpeningTasks.remove(openingIdx)); - didMergeThings = true; - } - } - if (openingTasks != null && openingTasks.size() > 0) { - // Switching to some new tasks, add to mOpening and remove from mPausing. Also, - // enter NEW_TASK state since this will start the switch-to animation. - final int layer = mInfo.getChanges().size() * 3; - final RemoteAnimationTarget[] targets = - new RemoteAnimationTarget[openingTasks.size()]; - for (int i = 0; i < openingTasks.size(); ++i) { - final TransitionInfo.Change change = openingTasks.get(i); - int pausingIdx = TaskState.indexOf(mPausingTasks, change); - if (pausingIdx >= 0) { - // Something is showing/opening a previously-pausing app. - targets[i] = TransitionUtil.newTarget(change, layer, - mPausingTasks.get(pausingIdx).mLeash); - mOpeningTasks.add(mPausingTasks.remove(pausingIdx)); - // Setup hides opening tasks initially, so make it visible again (since we - // are already showing it). - t.show(change.getLeash()); - t.setAlpha(change.getLeash(), 1.f); - } else { - // We are receiving new opening tasks, so convert to onTasksAppeared. - targets[i] = TransitionUtil.newTarget(change, layer, info, t, mLeashMap); - // reparent into the original `mInfo` since that's where we are animating. - final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo); - t.reparent(targets[i].leash, mInfo.getRoot(rootIdx).getLeash()); - t.setLayer(targets[i].leash, layer); - mOpeningTasks.add(new TaskState(change, targets[i].leash)); - } - } - didMergeThings = true; - mState = STATE_NEW_TASK; - mAppearedTargets = targets; - } - if (!didMergeThings) { - // Didn't recognize anything in incoming transition so don't merge it. - Log.w(TAG, "Don't know how to merge this transition."); - return false; - } - t.apply(); - // not using the incoming anim-only surfaces - info.releaseAnimSurfaces(); - return true; - } - - private void commitTasksAppearedIfNeeded() { - if (mAppearedTargets != null) { - mListener.onTasksAppeared(mAppearedTargets); - mAppearedTargets = null; - } - } - - @Override public TaskSnapshot screenshotTask(int taskId) { - try { - return ActivityTaskManager.getService().takeTaskSnapshot(taskId, - true /* updateCache */); - } catch (RemoteException e) { - Log.e(TAG, "Failed to screenshot task", e); - } - return null; - } - - @Override public void setInputConsumerEnabled(boolean enabled) { - if (!enabled) return; - // transient launches don't receive focus automatically. Since we are taking over - // the gesture now, take focus explicitly. - // This also moves recents back to top if the user gestured before a switch - // animation finished. - try { - ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId); - } catch (RemoteException e) { - Log.e(TAG, "Failed to set focused task", e); - } - } - - @Override public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) { - } - - @Override public void setFinishTaskTransaction(int taskId, - PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) { - mPipTransaction = finishTransaction; - } - - @Override - @SuppressLint("NewApi") - public void finish(boolean toHome, boolean sendUserLeaveHint) { - if (mFinishCB == null) { - Log.e(TAG, "Duplicate call to finish", new RuntimeException()); - return; - } - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - final WindowContainerTransaction wct = new WindowContainerTransaction(); - - if (mKeyguardLocked && mRecentsTask != null) { - if (toHome) wct.reorder(mRecentsTask, true /* toTop */); - else wct.restoreTransientOrder(mRecentsTask); - } - if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) { - // The gesture is returning to the pausing-task(s) rather than continuing with - // recents, so end the transition by moving the app back to the top (and also - // re-showing it's task). - for (int i = mPausingTasks.size() - 1; i >= 0; --i) { - // reverse order so that index 0 ends up on top - wct.reorder(mPausingTasks.get(i).mToken, true /* onTop */); - t.show(mPausingTasks.get(i).mTaskSurface); - } - if (!mKeyguardLocked && mRecentsTask != null) { - wct.restoreTransientOrder(mRecentsTask); - } - } else if (toHome && mOpeningSeparateHome && mPausingTasks != null) { - // Special situation where 3p launcher was changed during recents (this happens - // during tapltests...). Here we get both "return to home" AND "home opening". - // This is basically going home, but we have to restore the recents and home order. - for (int i = 0; i < mOpeningTasks.size(); ++i) { - final TaskState state = mOpeningTasks.get(i); - if (state.mTaskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - // Make sure it is on top. - wct.reorder(state.mToken, true /* onTop */); - } - t.show(state.mTaskSurface); - } - for (int i = mPausingTasks.size() - 1; i >= 0; --i) { - t.hide(mPausingTasks.get(i).mTaskSurface); - } - if (!mKeyguardLocked && mRecentsTask != null) { - wct.restoreTransientOrder(mRecentsTask); - } - } else { - // The general case: committing to recents, going home, or switching tasks. - for (int i = 0; i < mOpeningTasks.size(); ++i) { - t.show(mOpeningTasks.get(i).mTaskSurface); - } - for (int i = 0; i < mPausingTasks.size(); ++i) { - if (!sendUserLeaveHint) { - // This means recents is not *actually* finishing, so of course we gotta - // do special stuff in WMCore to accommodate. - wct.setDoNotPip(mPausingTasks.get(i).mToken); - } - // Since we will reparent out of the leashes, pre-emptively hide the child - // surface to match the leash. Otherwise, there will be a flicker before the - // visibility gets committed in Core when using split-screen (in splitscreen, - // the leaf-tasks are not "independent" so aren't hidden by normal setup). - t.hide(mPausingTasks.get(i).mTaskSurface); - } - if (mPipTask != null && mPipTransaction != null && sendUserLeaveHint) { - t.show(mInfo.getChange(mPipTask).getLeash()); - PictureInPictureSurfaceTransaction.apply(mPipTransaction, - mInfo.getChange(mPipTask).getLeash(), t); - mPipTask = null; - mPipTransaction = null; - } - } - try { - mFinishCB.onTransitionFinished(wct.isEmpty() ? null : wct, t); - } catch (RemoteException e) { - Log.e(TAG, "Failed to call animation finish callback", e); - t.apply(); - } - // Only release the non-local created surface references. The animator is responsible - // for releasing the leashes created by local. - mInfo.releaseAllSurfaces(); - // Reset all members. - mListener = null; - mFinishCB = null; - mPausingTasks = null; - mOpeningTasks = null; - mAppearedTargets = null; - mInfo = null; - mOpeningSeparateHome = false; - mLeashMap = null; - mTransition = null; - mState = STATE_NORMAL; - } - - @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { - } - - @Override public void cleanupScreenshot() { - } - - @Override public void setWillFinishToHome(boolean willFinishToHome) { - mWillFinishToHome = willFinishToHome; - } - - /** - * @see IRecentsAnimationController#removeTask - */ - @Override public boolean removeTask(int taskId) { - return false; - } - - /** - * @see IRecentsAnimationController#detachNavigationBarFromApp - */ - @Override public void detachNavigationBarFromApp(boolean moveHomeToTop) { - try { - ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition); - } catch (RemoteException e) { - Log.e(TAG, "Failed to detach the navigation bar from app", e); - } - } - - /** - * @see IRecentsAnimationController#animateNavigationBarToApp(long) - */ - @Override public void animateNavigationBarToApp(long duration) { - } - } - - /** Utility class to track the state of a task as-seen by recents. */ - private static class TaskState { - WindowContainerToken mToken; - ActivityManager.RunningTaskInfo mTaskInfo; - - /** The surface/leash of the task provided by Core. */ - SurfaceControl mTaskSurface; - - /** The (local) animation-leash created for this task. */ - SurfaceControl mLeash; - - TaskState(TransitionInfo.Change change, SurfaceControl leash) { - mToken = change.getContainer(); - mTaskInfo = change.getTaskInfo(); - mTaskSurface = change.getLeash(); - mLeash = leash; - } - - static int indexOf(ArrayList<TaskState> list, TransitionInfo.Change change) { - for (int i = list.size() - 1; i >= 0; --i) { - if (list.get(i).mToken.equals(change.getContainer())) { - return i; - } - } - return -1; - } - - public String toString() { - return "" + mToken + " : " + mLeash; - } - } -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java deleted file mode 100644 index 98212e1d91b6..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2019 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.shared.tracing; - -import android.os.Trace; -import android.util.Log; -import android.view.Choreographer; - -import com.android.internal.util.TraceBuffer; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Queue; -import java.util.function.Consumer; - -/** - * A proto tracer implementation that can be updated directly (upon state change), or on the next - * scheduled frame. - * - * @param <P> The class type of the proto provider - * @param <S> The proto class type of the encapsulating proto - * @param <T> The proto class type of the individual proto entries in the buffer - * @param <R> The proto class type of the entry root proto in the buffer - */ -public class FrameProtoTracer<P, S extends P, T extends P, R> - implements Choreographer.FrameCallback { - - private static final String TAG = "FrameProtoTracer"; - private static final int BUFFER_CAPACITY = 1024 * 1024; - - private final Object mLock = new Object(); - private final TraceBuffer<P, S, T> mBuffer; - private final File mTraceFile; - private final ProtoTraceParams<P, S, T, R> mParams; - private Choreographer mChoreographer; - private final Queue<T> mPool = new ArrayDeque<>(); - private final ArrayList<ProtoTraceable<R>> mTraceables = new ArrayList<>(); - private final ArrayList<ProtoTraceable<R>> mTmpTraceables = new ArrayList<>(); - - private volatile boolean mEnabled; - private boolean mFrameScheduled; - - private final TraceBuffer.ProtoProvider<P, S, T> mProvider = - new TraceBuffer.ProtoProvider<P, S, T>() { - @Override - public int getItemSize(P proto) { - return mParams.getProtoSize(proto); - } - - @Override - public byte[] getBytes(P proto) { - return mParams.getProtoBytes(proto); - } - - @Override - public void write(S encapsulatingProto, Queue<T> buffer, OutputStream os) - throws IOException { - os.write(mParams.serializeEncapsulatingProto(encapsulatingProto, buffer)); - } - }; - - public interface ProtoTraceParams<P, S, T, R> { - File getTraceFile(); - S getEncapsulatingTraceProto(); - T updateBufferProto(T reuseObj, ArrayList<ProtoTraceable<R>> traceables); - byte[] serializeEncapsulatingProto(S encapsulatingProto, Queue<T> buffer); - byte[] getProtoBytes(P proto); - int getProtoSize(P proto); - } - - public FrameProtoTracer(ProtoTraceParams<P, S, T, R> params) { - mParams = params; - mBuffer = new TraceBuffer<>(BUFFER_CAPACITY, mProvider, new Consumer<T>() { - @Override - public void accept(T t) { - onProtoDequeued(t); - } - }); - mTraceFile = params.getTraceFile(); - } - - public void start() { - synchronized (mLock) { - if (mEnabled) { - return; - } - mBuffer.resetBuffer(); - mEnabled = true; - } - logState(); - } - - public void stop() { - synchronized (mLock) { - if (!mEnabled) { - return; - } - mEnabled = false; - } - writeToFile(); - } - - public boolean isEnabled() { - return mEnabled; - } - - public void add(ProtoTraceable<R> traceable) { - synchronized (mLock) { - mTraceables.add(traceable); - } - } - - public void remove(ProtoTraceable<R> traceable) { - synchronized (mLock) { - mTraceables.remove(traceable); - } - } - - public void scheduleFrameUpdate() { - if (!mEnabled || mFrameScheduled) { - return; - } - - // Schedule an update on the next frame - if (mChoreographer == null) { - mChoreographer = Choreographer.getMainThreadInstance(); - } - mChoreographer.postFrameCallback(this); - mFrameScheduled = true; - } - - public void update() { - if (!mEnabled) { - return; - } - - logState(); - } - - public float getBufferUsagePct() { - return (float) mBuffer.getBufferSize() / BUFFER_CAPACITY; - } - - @Override - public void doFrame(long frameTimeNanos) { - logState(); - } - - private void onProtoDequeued(T proto) { - mPool.add(proto); - } - - private void logState() { - synchronized (mLock) { - mTmpTraceables.addAll(mTraceables); - } - - mBuffer.add(mParams.updateBufferProto(mPool.poll(), mTmpTraceables)); - mTmpTraceables.clear(); - mFrameScheduled = false; - } - - private void writeToFile() { - try { - Trace.beginSection("ProtoTracer.writeToFile"); - mBuffer.writeTraceToFile(mTraceFile, mParams.getEncapsulatingTraceProto()); - } catch (IOException e) { - Log.e(TAG, "Unable to write buffer to file", e); - } finally { - Trace.endSection(); - } - } -} - - diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java deleted file mode 100644 index e05b0b074449..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.systemui.shared.tracing; - -/** - * @see FrameProtoTracer - */ -public interface ProtoTraceable<T> { - - /** - * NOTE: Implementations should update all fields in this proto. - */ - void writeToProto(T proto); -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt index 635f0fa44234..50e5466d0325 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt @@ -12,6 +12,10 @@ class KeyguardClockFrame( ) : FrameLayout(context, attrs) { private var drawAlpha: Int = 255 + init { + setLayerType(View.LAYER_TYPE_SOFTWARE, null) + } + protected override fun onSetAlpha(alpha: Int): Boolean { drawAlpha = alpha return true diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java index 794e6941c2bb..b3e08c0bc69f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java @@ -165,15 +165,18 @@ public class KeyguardPinViewController * Responsible for identifying if PIN hinting is to be enabled or not */ private boolean isPinHinting() { - return mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser()) - == DEFAULT_PIN_LENGTH; + return mPinLength == DEFAULT_PIN_LENGTH; } /** - * Responsible for identifying if auto confirm is enabled or not in Settings + * Responsible for identifying if auto confirm is enabled or not in Settings and + * a valid PIN_LENGTH is stored on the device (though the latter check is only to make it more + * robust since we only allow enabling PIN confirmation if the user has a valid PIN length + * saved on device) */ private boolean isAutoPinConfirmEnabledInSettings() { //Checks if user has enabled the auto confirm in Settings - return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser()); + return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser()) + && mPinLength != LockPatternUtils.PIN_LENGTH_UNAVAILABLE; } } diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java index a7d4455b43c2..87627698597f 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java @@ -20,6 +20,7 @@ import com.android.keyguard.CarrierText; import com.android.systemui.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import dagger.Module; @@ -44,6 +45,13 @@ public abstract class KeyguardStatusBarViewModule { /** */ @Provides @KeyguardStatusBarViewScope + static StatusBarLocation getStatusBarLocation() { + return StatusBarLocation.KEYGUARD; + } + + /** */ + @Provides + @KeyguardStatusBarViewScope static StatusBarUserSwitcherContainer getUserSwitcherContainer(KeyguardStatusBarView view) { return view.findViewById(R.id.user_switcher_container); } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 1f1b154ef1c8..04acd0b91173 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -123,7 +123,6 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.telephony.TelephonyListenerManager; -import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tuner.TunablePadding.TunablePaddingService; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.DeviceConfigProxy; @@ -335,7 +334,6 @@ public class Dependency { @Inject Lazy<IWallpaperManager> mWallpaperManager; @Inject Lazy<CommandQueue> mCommandQueue; @Inject Lazy<RecordingController> mRecordingController; - @Inject Lazy<ProtoTracer> mProtoTracer; @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory; @Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy; @Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager; @@ -528,7 +526,6 @@ public class Dependency { mProviders.put(DozeParameters.class, mDozeParameters::get); mProviders.put(IWallpaperManager.class, mWallpaperManager::get); mProviders.put(CommandQueue.class, mCommandQueue::get); - mProviders.put(ProtoTracer.class, mProtoTracer::get); mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get); mProviders.put(TelephonyListenerManager.class, mTelephonyListenerManager::get); diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt index 444491f53629..70b43713599b 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt @@ -71,10 +71,23 @@ interface AuthenticationRepository { */ val isBypassEnabled: StateFlow<Boolean> - /** Whether the auto confirm feature is enabled for the currently-selected user. */ + /** + * Whether the auto confirm feature is enabled for the currently-selected user. + * + * Note that the length of the PIN is also important to take into consideration, please see + * [hintedPinLength]. + */ val isAutoConfirmEnabled: StateFlow<Boolean> - /** The length of the PIN for which we should show a hint. */ + /** + * The exact length a PIN should be for us to enable PIN length hinting. + * + * A PIN that's shorter or longer than this is not eligible for the UI to render hints showing + * how many digits the current PIN is, even if [isAutoConfirmEnabled] is enabled. + * + * Note that PIN length hinting is only available if the PIN auto confirmation feature is + * available. + */ val hintedPinLength: Int /** Whether the pattern should be visible for the currently-selected user. */ @@ -166,10 +179,10 @@ constructor( .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = true, + initialValue = false, ) - override val hintedPinLength: Int = LockPatternUtils.MIN_AUTO_PIN_REQUIREMENT_LENGTH + override val hintedPinLength: Int = 6 override val isPatternVisible: StateFlow<Boolean> = userRepository.selectedUserInfo diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt index 82674bff6433..3283e406ddb0 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt @@ -37,7 +37,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -103,10 +102,11 @@ constructor( /** The length of the hinted PIN, or `null` if pin length hint should not be shown. */ val hintedPinLength: StateFlow<Int?> = - flow { emit(repository.getPinLength()) } - .map { currentPinLength -> - // Hinting is enabled for 6-digit codes only - currentPinLength.takeIf { repository.hintedPinLength == it } + repository.isAutoConfirmEnabled + .map { isAutoConfirmEnabled -> + repository.getPinLength().takeIf { + isAutoConfirmEnabled && it == repository.hintedPinLength + } } .stateIn( scope = applicationScope, diff --git a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt index b52ddc1dbc42..b34f1b45d763 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt @@ -87,6 +87,10 @@ class AccessorizedBatteryDrawable( } var displayShield: Boolean = false + set(value) { + field = value + postInvalidate() + } private fun updateSizes() { val b = bounds @@ -204,4 +208,11 @@ class AccessorizedBatteryDrawable( val shieldPathString = context.resources.getString(R.string.config_batterymeterShieldPath) shieldPath.set(PathParser.createPathFromPathData(shieldPathString)) } + + private val invalidateRunnable: () -> Unit = { invalidateSelf() } + + private fun postInvalidate() { + unscheduleSelf(invalidateRunnable) + scheduleSelf(invalidateRunnable, 0) + } } diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index c1238d911ecf..4e8383c3e39e 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -464,9 +464,11 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { public void dump(PrintWriter pw, String[] args) { String powerSave = mDrawable == null ? null : mDrawable.getPowerSaveEnabled() + ""; + String displayShield = mDrawable == null ? null : mDrawable.getDisplayShield() + ""; CharSequence percent = mBatteryPercentView == null ? null : mBatteryPercentView.getText(); pw.println(" BatteryMeterView:"); pw.println(" mDrawable.getPowerSave: " + powerSave); + pw.println(" mDrawable.getDisplayShield: " + displayShield); pw.println(" mBatteryPercentView.getText(): " + percent); pw.println(" mTextColor: #" + Integer.toHexString(mTextColor)); pw.println(" mBatteryStateUnknown: " + mBatteryStateUnknown); diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java index f6a10bd4af4a..6a5749cc5571 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java @@ -30,16 +30,18 @@ import android.view.View; import androidx.annotation.NonNull; +import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.ViewController; +import java.io.PrintWriter; + import javax.inject.Inject; /** Controller for {@link BatteryMeterView}. **/ @@ -53,6 +55,7 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> private final String mSlotBattery; private final SettingObserver mSettingObserver; private final UserTracker mUserTracker; + private final StatusBarLocation mLocation; private final ConfigurationController.ConfigurationListener mConfigurationListener = new ConfigurationController.ConfigurationListener() { @@ -94,6 +97,13 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> public void onIsBatteryDefenderChanged(boolean isBatteryDefender) { mView.onIsBatteryDefenderChanged(isBatteryDefender); } + + @Override + public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + pw.print(super.toString()); + pw.println(" location=" + mLocation); + mView.dump(pw, args); + } }; private final UserTracker.Callback mUserChangedCallback = @@ -113,14 +123,15 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> @Inject public BatteryMeterViewController( BatteryMeterView view, + StatusBarLocation location, UserTracker userTracker, ConfigurationController configurationController, TunerService tunerService, @Main Handler mainHandler, ContentResolver contentResolver, - FeatureFlags featureFlags, BatteryController batteryController) { super(view); + mLocation = location; mUserTracker = userTracker; mConfigurationController = configurationController; mTunerService = tunerService; @@ -129,7 +140,8 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> mBatteryController = batteryController; mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString); - mView.setDisplayShieldEnabled(featureFlags.isEnabled(Flags.BATTERY_SHIELD_ICON)); + mView.setDisplayShieldEnabled( + getContext().getResources().getBoolean(R.bool.flag_battery_shield_icon)); mSlotBattery = getResources().getString(com.android.internal.R.string.status_bar_battery); mSettingObserver = new SettingObserver(mMainHandler); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index 37ce44488346..083e21fbdfba 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -17,12 +17,7 @@ package com.android.systemui.biometrics import android.app.ActivityTaskManager import android.content.Context -import android.content.res.Configuration -import android.graphics.Color import android.graphics.PixelFormat -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter -import android.graphics.Rect import android.hardware.biometrics.BiometricOverlayConstants import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS @@ -33,27 +28,23 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import android.hardware.fingerprint.ISidefpsController import android.os.Handler import android.util.Log -import android.util.RotationUtils import android.view.Display import android.view.DisplayInfo import android.view.Gravity import android.view.LayoutInflater import android.view.Surface import android.view.View -import android.view.View.AccessibilityDelegate import android.view.ViewPropertyAnimator import android.view.WindowManager import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY -import android.view.accessibility.AccessibilityEvent -import androidx.annotation.RawRes import com.airbnb.lottie.LottieAnimationView -import com.airbnb.lottie.LottieProperty -import com.airbnb.lottie.model.KeyPath import com.android.internal.annotations.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor +import com.android.systemui.biometrics.ui.binder.SideFpsOverlayViewBinder +import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -64,6 +55,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.traceSection import java.io.PrintWriter import javax.inject.Inject +import javax.inject.Provider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -86,6 +78,7 @@ constructor( @Main private val mainExecutor: DelayableExecutor, @Main private val handler: Handler, private val alternateBouncerInteractor: AlternateBouncerInteractor, + private val sideFpsOverlayViewModelFactory: Provider<SideFpsOverlayViewModel>, @Application private val scope: CoroutineScope, dumpManager: DumpManager ) : Dumpable { @@ -250,105 +243,15 @@ constructor( private fun createOverlayForDisplay(@BiometricOverlayConstants.ShowReason reason: Int) { val view = layoutInflater.inflate(R.layout.sidefps_view, null, false) overlayView = view - val display = context.display!! - // b/284098873 `context.display.rotation` may not up-to-date, we use displayInfo.rotation - display.getDisplayInfo(displayInfo) - val offsets = - sensorProps.getLocation(display.uniqueId).let { location -> - if (location == null) { - Log.w(TAG, "No location specified for display: ${display.uniqueId}") - } - location ?: sensorProps.location - } - overlayOffsets = offsets - - val lottie = view.findViewById(R.id.sidefps_animation) as LottieAnimationView - view.rotation = - display.asSideFpsAnimationRotation( - offsets.isYAligned(), - getRotationFromDefault(displayInfo.rotation) - ) - lottie.setAnimation( - display.asSideFpsAnimation( - offsets.isYAligned(), - getRotationFromDefault(displayInfo.rotation) - ) + SideFpsOverlayViewBinder.bind( + view = view, + viewModel = sideFpsOverlayViewModelFactory.get(), + overlayViewParams = overlayViewParams, + reason = reason, + context = context, ) - lottie.addLottieOnCompositionLoadedListener { - // Check that view is not stale, and that overlayView has not been hidden/removed - if (overlayView != null && overlayView == view) { - updateOverlayParams(display, it.bounds) - } - } orientationReasonListener.reason = reason - lottie.addOverlayDynamicColor(context, reason) - - /** - * Intercepts TYPE_WINDOW_STATE_CHANGED accessibility event, preventing Talkback from - * speaking @string/accessibility_fingerprint_label twice when sensor location indicator is - * in focus - */ - view.setAccessibilityDelegate( - object : AccessibilityDelegate() { - override fun dispatchPopulateAccessibilityEvent( - host: View, - event: AccessibilityEvent - ): Boolean { - return if ( - event.getEventType() === AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED - ) { - true - } else { - super.dispatchPopulateAccessibilityEvent(host, event) - } - } - } - ) } - - @VisibleForTesting - fun updateOverlayParams(display: Display, bounds: Rect) { - val isNaturalOrientation = display.isNaturalOrientation() - val isDefaultOrientation = - if (isReverseDefaultRotation) !isNaturalOrientation else isNaturalOrientation - val size = windowManager.maximumWindowMetrics.bounds - - val displayWidth = if (isDefaultOrientation) size.width() else size.height() - val displayHeight = if (isDefaultOrientation) size.height() else size.width() - val boundsWidth = if (isDefaultOrientation) bounds.width() else bounds.height() - val boundsHeight = if (isDefaultOrientation) bounds.height() else bounds.width() - - val sensorBounds = - if (overlayOffsets.isYAligned()) { - Rect( - displayWidth - boundsWidth, - overlayOffsets.sensorLocationY, - displayWidth, - overlayOffsets.sensorLocationY + boundsHeight - ) - } else { - Rect( - overlayOffsets.sensorLocationX, - 0, - overlayOffsets.sensorLocationX + boundsWidth, - boundsHeight - ) - } - - RotationUtils.rotateBounds( - sensorBounds, - Rect(0, 0, displayWidth, displayHeight), - getRotationFromDefault(display.rotation) - ) - - overlayViewParams.x = sensorBounds.left - overlayViewParams.y = sensorBounds.top - - windowManager.updateViewLayout(overlayView, overlayViewParams) - } - - private fun getRotationFromDefault(rotation: Int): Int = - if (isReverseDefaultRotation) (rotation + 1) % 4 else rotation } private val FingerprintManager?.sideFpsSensorProperties: FingerprintSensorPropertiesInternal? @@ -373,89 +276,12 @@ private fun Int.isReasonToAutoShow(activityTaskManager: ActivityTaskManager): Bo private fun ActivityTaskManager.topClass(): String = getTasks(1).firstOrNull()?.topActivity?.className ?: "" -@RawRes -private fun Display.asSideFpsAnimation(yAligned: Boolean, rotationFromDefault: Int): Int = - when (rotationFromDefault) { - Surface.ROTATION_0 -> if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape - Surface.ROTATION_180 -> if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape - else -> if (yAligned) R.raw.sfps_pulse_landscape else R.raw.sfps_pulse - } - -private fun Display.asSideFpsAnimationRotation(yAligned: Boolean, rotationFromDefault: Int): Float = - when (rotationFromDefault) { - Surface.ROTATION_90 -> if (yAligned) 0f else 180f - Surface.ROTATION_180 -> 180f - Surface.ROTATION_270 -> if (yAligned) 180f else 0f - else -> 0f - } - private fun SensorLocationInternal.isYAligned(): Boolean = sensorLocationY != 0 private fun Display.isNaturalOrientation(): Boolean = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 -private fun LottieAnimationView.addOverlayDynamicColor( - context: Context, - @BiometricOverlayConstants.ShowReason reason: Int -) { - fun update() { - val isKeyguard = reason == REASON_AUTH_KEYGUARD - if (isKeyguard) { - val color = - com.android.settingslib.Utils.getColorAttrDefaultColor( - context, - com.android.internal.R.attr.materialColorPrimaryFixed - ) - val outerRimColor = - com.android.settingslib.Utils.getColorAttrDefaultColor( - context, - com.android.internal.R.attr.materialColorPrimaryFixedDim - ) - val chevronFill = - com.android.settingslib.Utils.getColorAttrDefaultColor( - context, - com.android.internal.R.attr.materialColorOnPrimaryFixed - ) - addValueCallback(KeyPath(".blue600", "**"), LottieProperty.COLOR_FILTER) { - PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP) - } - addValueCallback(KeyPath(".blue400", "**"), LottieProperty.COLOR_FILTER) { - PorterDuffColorFilter(outerRimColor, PorterDuff.Mode.SRC_ATOP) - } - addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) { - PorterDuffColorFilter(chevronFill, PorterDuff.Mode.SRC_ATOP) - } - } else { - if (!isDarkMode(context)) { - addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) { - PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP) - } - } - for (key in listOf(".blue600", ".blue400")) { - addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) { - PorterDuffColorFilter( - context.getColor(R.color.settingslib_color_blue400), - PorterDuff.Mode.SRC_ATOP - ) - } - } - } - } - - if (composition != null) { - update() - } else { - addLottieOnCompositionLoadedListener { update() } - } -} - -private fun isDarkMode(context: Context): Boolean { - val darkMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK - return darkMode == Configuration.UI_MODE_NIGHT_YES -} - -@VisibleForTesting -class OrientationReasonListener( +public class OrientationReasonListener( context: Context, displayManager: DisplayManager, handler: Handler, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt index c43722f2a0bb..efbde4c5985b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt @@ -16,8 +16,11 @@ package com.android.systemui.biometrics.data.repository +import android.hardware.biometrics.ComponentInfoInternal import android.hardware.biometrics.SensorLocationInternal +import android.hardware.biometrics.SensorProperties import android.hardware.fingerprint.FingerprintManager +import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback import com.android.systemui.biometrics.shared.model.FingerprintSensorType @@ -30,10 +33,8 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn /** @@ -43,22 +44,17 @@ import kotlinx.coroutines.flow.shareIn */ interface FingerprintPropertyRepository { - /** - * If the repository is initialized or not. Other properties are defaults until this is true. - */ - val isInitialized: Flow<Boolean> - /** The id of fingerprint sensor. */ - val sensorId: StateFlow<Int> + val sensorId: Flow<Int> /** The security strength of sensor (convenience, weak, strong). */ - val strength: StateFlow<SensorStrength> + val strength: Flow<SensorStrength> /** The types of fingerprint sensor (rear, ultrasonic, optical, etc.). */ - val sensorType: StateFlow<FingerprintSensorType> + val sensorType: Flow<FingerprintSensorType> /** The sensor location relative to each physical display. */ - val sensorLocations: StateFlow<Map<String, SensorLocationInternal>> + val sensorLocations: Flow<Map<String, SensorLocationInternal>> } @SysUISingleton @@ -66,10 +62,10 @@ class FingerprintPropertyRepositoryImpl @Inject constructor( @Application private val applicationScope: CoroutineScope, - private val fingerprintManager: FingerprintManager + private val fingerprintManager: FingerprintManager? ) : FingerprintPropertyRepository { - override val isInitialized: Flow<Boolean> = + private val props: Flow<FingerprintSensorPropertiesInternal> = conflatedCallbackFlow { val callback = object : IFingerprintAuthenticatorsRegisteredCallback.Stub() { @@ -77,45 +73,47 @@ constructor( sensors: List<FingerprintSensorPropertiesInternal> ) { if (sensors.isNotEmpty()) { - setProperties(sensors[0]) - trySendWithFailureLogging(true, TAG, "initialize properties") + trySendWithFailureLogging(sensors[0], TAG, "initialize properties") + } else { + trySendWithFailureLogging( + DEFAULT_PROPS, + TAG, + "initialize with default properties" + ) } } } - fingerprintManager.addAuthenticatorsRegisteredCallback(callback) - trySendWithFailureLogging(false, TAG, "initial value defaulting to false") + fingerprintManager?.addAuthenticatorsRegisteredCallback(callback) + trySendWithFailureLogging(DEFAULT_PROPS, TAG, "initialize with default properties") awaitClose {} } .shareIn(scope = applicationScope, started = SharingStarted.Eagerly, replay = 1) - private val _sensorId: MutableStateFlow<Int> = MutableStateFlow(-1) - override val sensorId: StateFlow<Int> = _sensorId.asStateFlow() - - private val _strength: MutableStateFlow<SensorStrength> = - MutableStateFlow(SensorStrength.CONVENIENCE) - override val strength = _strength.asStateFlow() - - private val _sensorType: MutableStateFlow<FingerprintSensorType> = - MutableStateFlow(FingerprintSensorType.UNKNOWN) - override val sensorType = _sensorType.asStateFlow() - - private val _sensorLocations: MutableStateFlow<Map<String, SensorLocationInternal>> = - MutableStateFlow(mapOf("" to SensorLocationInternal.DEFAULT)) - override val sensorLocations: StateFlow<Map<String, SensorLocationInternal>> = - _sensorLocations.asStateFlow() - - private fun setProperties(prop: FingerprintSensorPropertiesInternal) { - _sensorId.value = prop.sensorId - _strength.value = sensorStrengthIntToObject(prop.sensorStrength) - _sensorType.value = sensorTypeIntToObject(prop.sensorType) - _sensorLocations.value = - prop.allLocations.associateBy { sensorLocationInternal -> + override val sensorId: Flow<Int> = props.map { it.sensorId } + override val strength: Flow<SensorStrength> = + props.map { sensorStrengthIntToObject(it.sensorStrength) } + override val sensorType: Flow<FingerprintSensorType> = + props.map { sensorTypeIntToObject(it.sensorType) } + override val sensorLocations: Flow<Map<String, SensorLocationInternal>> = + props.map { + it.allLocations.associateBy { sensorLocationInternal -> sensorLocationInternal.displayId } - } + } companion object { private const val TAG = "FingerprintPropertyRepositoryImpl" + private val DEFAULT_PROPS = + FingerprintSensorPropertiesInternal( + -1 /* sensorId */, + SensorProperties.STRENGTH_CONVENIENCE, + 0 /* maxEnrollmentsPerUser */, + listOf<ComponentInfoInternal>(), + FingerprintSensorProperties.TYPE_UNKNOWN, + false /* halControlsIllumination */, + true /* resetLockoutRequiresHardwareAuthToken */, + listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT) + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt index aa85e5f3b21a..37f39cb5fe0e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt @@ -17,16 +17,24 @@ package com.android.systemui.biometrics.domain.interactor import android.hardware.biometrics.SensorLocationInternal -import android.util.Log import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine /** Business logic for SideFps overlay offsets. */ interface SideFpsOverlayInteractor { + /** The displayId of the current display. */ + val displayId: Flow<String> - /** Get the corresponding offsets based on different displayId. */ - fun getOverlayOffsets(displayId: String): SensorLocationInternal + /** The corresponding offsets based on different displayId. */ + val overlayOffsets: Flow<SensorLocationInternal> + + /** Update the displayId. */ + fun changeDisplay(displayId: String?) } @SysUISingleton @@ -35,14 +43,16 @@ class SideFpsOverlayInteractorImpl constructor(private val fingerprintPropertyRepository: FingerprintPropertyRepository) : SideFpsOverlayInteractor { - override fun getOverlayOffsets(displayId: String): SensorLocationInternal { - val offsets = fingerprintPropertyRepository.sensorLocations.value - return if (offsets.containsKey(displayId)) { - offsets[displayId]!! - } else { - Log.w(TAG, "No location specified for display: $displayId") - offsets[""]!! + private val _displayId: MutableStateFlow<String> = MutableStateFlow("") + override val displayId: Flow<String> = _displayId.asStateFlow() + + override val overlayOffsets: Flow<SensorLocationInternal> = + combine(displayId, fingerprintPropertyRepository.sensorLocations) { displayId, offsets -> + offsets[displayId] ?: SensorLocationInternal.DEFAULT } + + override fun changeDisplay(displayId: String?) { + _displayId.value = displayId ?: "" } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt new file mode 100644 index 000000000000..0409519c9816 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics.ui.binder + +import android.content.Context +import android.content.res.Configuration +import android.graphics.Color +import android.graphics.PorterDuff +import android.graphics.PorterDuffColorFilter +import android.hardware.biometrics.BiometricOverlayConstants +import android.view.View +import android.view.WindowManager +import android.view.accessibility.AccessibilityEvent +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.airbnb.lottie.LottieAnimationView +import com.airbnb.lottie.LottieProperty +import com.airbnb.lottie.model.KeyPath +import com.android.systemui.R +import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.lifecycle.repeatWhenAttached +import kotlinx.coroutines.launch + +/** Sub-binder for SideFpsOverlayView. */ +object SideFpsOverlayViewBinder { + + /** Bind the view. */ + @JvmStatic + fun bind( + view: View, + viewModel: SideFpsOverlayViewModel, + overlayViewParams: WindowManager.LayoutParams, + @BiometricOverlayConstants.ShowReason reason: Int, + @Application context: Context + ) { + val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + + val lottie = view.findViewById(R.id.sidefps_animation) as LottieAnimationView + + viewModel.changeDisplay() + + view.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { + viewModel.sideFpsAnimationRotation.collect { rotation -> + view.rotation = rotation + } + } + + launch { + // TODO(b/221037350, wenhuiy): Create a separate ViewBinder for sideFpsAnimation + // in order to add scuba tests in the future. + viewModel.sideFpsAnimation.collect { animation -> + lottie.setAnimation(animation) + } + } + + launch { + viewModel.sensorBounds.collect { sensorBounds -> + overlayViewParams.x = sensorBounds.left + overlayViewParams.y = sensorBounds.top + + windowManager.updateViewLayout(view, overlayViewParams) + } + } + + launch { + viewModel.overlayOffsets.collect { overlayOffsets -> + lottie.addLottieOnCompositionLoadedListener { + viewModel.updateSensorBounds( + it.bounds, + windowManager.maximumWindowMetrics.bounds, + overlayOffsets + ) + } + } + } + } + } + + lottie.addOverlayDynamicColor(context, reason) + + /** + * Intercepts TYPE_WINDOW_STATE_CHANGED accessibility event, preventing Talkback from + * speaking @string/accessibility_fingerprint_label twice when sensor location indicator is + * in focus + */ + view.accessibilityDelegate = + object : View.AccessibilityDelegate() { + override fun dispatchPopulateAccessibilityEvent( + host: View, + event: AccessibilityEvent + ): Boolean { + return if ( + event.getEventType() === AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED + ) { + true + } else { + super.dispatchPopulateAccessibilityEvent(host, event) + } + } + } + } +} + +private fun LottieAnimationView.addOverlayDynamicColor( + context: Context, + @BiometricOverlayConstants.ShowReason reason: Int +) { + fun update() { + val isKeyguard = reason == BiometricOverlayConstants.REASON_AUTH_KEYGUARD + if (isKeyguard) { + val color = context.getColor(R.color.numpad_key_color_secondary) // match bouncer color + val chevronFill = + com.android.settingslib.Utils.getColorAttrDefaultColor( + context, + android.R.attr.textColorPrimaryInverse + ) + for (key in listOf(".blue600", ".blue400")) { + addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) { + PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP) + } + } + addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) { + PorterDuffColorFilter(chevronFill, PorterDuff.Mode.SRC_ATOP) + } + } else if (!isDarkMode(context)) { + addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) { + PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP) + } + } else if (isDarkMode(context)) { + for (key in listOf(".blue600", ".blue400")) { + addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) { + PorterDuffColorFilter( + context.getColor(R.color.settingslib_color_blue400), + PorterDuff.Mode.SRC_ATOP + ) + } + } + } + } + + if (composition != null) { + update() + } else { + addLottieOnCompositionLoadedListener { update() } + } +} + +private fun isDarkMode(context: Context): Boolean { + val darkMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + return darkMode == Configuration.UI_MODE_NIGHT_YES +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt new file mode 100644 index 000000000000..e938b4efb68c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics.ui.viewmodel + +import android.content.Context +import android.graphics.Rect +import android.hardware.biometrics.SensorLocationInternal +import android.util.RotationUtils +import android.view.Display +import android.view.DisplayInfo +import android.view.Surface +import androidx.annotation.RawRes +import com.android.systemui.R +import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor +import com.android.systemui.dagger.qualifiers.Application +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.map + +/** View-model for SideFpsOverlayView. */ +class SideFpsOverlayViewModel +@Inject +constructor( + @Application private val context: Context, + private val sideFpsOverlayInteractor: SideFpsOverlayInteractor, +) { + + private val isReverseDefaultRotation = + context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation) + + private val _sensorBounds: MutableStateFlow<Rect> = MutableStateFlow(Rect()) + val sensorBounds = _sensorBounds.asStateFlow() + + val overlayOffsets: Flow<SensorLocationInternal> = sideFpsOverlayInteractor.overlayOffsets + + /** Update the displayId. */ + fun changeDisplay() { + sideFpsOverlayInteractor.changeDisplay(context.display!!.uniqueId) + } + + /** Determine the rotation of the sideFps animation given the overlay offsets. */ + val sideFpsAnimationRotation: Flow<Float> = + overlayOffsets.map { overlayOffsets -> + val display = context.display!! + val displayInfo: DisplayInfo = DisplayInfo() + // b/284098873 `context.display.rotation` may not up-to-date, we use + // displayInfo.rotation + display.getDisplayInfo(displayInfo) + val yAligned: Boolean = overlayOffsets.isYAligned() + when (getRotationFromDefault(displayInfo.rotation)) { + Surface.ROTATION_90 -> if (yAligned) 0f else 180f + Surface.ROTATION_180 -> 180f + Surface.ROTATION_270 -> if (yAligned) 180f else 0f + else -> 0f + } + } + + /** Populate the sideFps animation from the overlay offsets. */ + @RawRes + val sideFpsAnimation: Flow<Int> = + overlayOffsets.map { overlayOffsets -> + val display = context.display!! + val displayInfo: DisplayInfo = DisplayInfo() + // b/284098873 `context.display.rotation` may not up-to-date, we use + // displayInfo.rotation + display.getDisplayInfo(displayInfo) + val yAligned: Boolean = overlayOffsets.isYAligned() + when (getRotationFromDefault(displayInfo.rotation)) { + Surface.ROTATION_0 -> if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape + Surface.ROTATION_180 -> + if (yAligned) R.raw.sfps_pulse else R.raw.sfps_pulse_landscape + else -> if (yAligned) R.raw.sfps_pulse_landscape else R.raw.sfps_pulse + } + } + + /** + * Calculate and update the bounds of the sensor based on the bounds of the overlay view, the + * maximum bounds of the window, and the offsets of the sensor location. + */ + fun updateSensorBounds( + bounds: Rect, + maximumWindowBounds: Rect, + offsets: SensorLocationInternal + ) { + val isNaturalOrientation = context.display!!.isNaturalOrientation() + val isDefaultOrientation = + if (isReverseDefaultRotation) !isNaturalOrientation else isNaturalOrientation + + val displayWidth = + if (isDefaultOrientation) maximumWindowBounds.width() else maximumWindowBounds.height() + val displayHeight = + if (isDefaultOrientation) maximumWindowBounds.height() else maximumWindowBounds.width() + val boundsWidth = if (isDefaultOrientation) bounds.width() else bounds.height() + val boundsHeight = if (isDefaultOrientation) bounds.height() else bounds.width() + + val sensorBounds = + if (offsets.isYAligned()) { + Rect( + displayWidth - boundsWidth, + offsets.sensorLocationY, + displayWidth, + offsets.sensorLocationY + boundsHeight + ) + } else { + Rect( + offsets.sensorLocationX, + 0, + offsets.sensorLocationX + boundsWidth, + boundsHeight + ) + } + + val displayInfo: DisplayInfo = DisplayInfo() + context.display!!.getDisplayInfo(displayInfo) + + RotationUtils.rotateBounds( + sensorBounds, + Rect(0, 0, displayWidth, displayHeight), + getRotationFromDefault(displayInfo.rotation) + ) + + _sensorBounds.value = sensorBounds + } + + private fun getRotationFromDefault(rotation: Int): Int = + if (isReverseDefaultRotation) (rotation + 1) % 4 else rotation +} + +private fun Display.isNaturalOrientation(): Boolean = + rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 + +private fun SensorLocationInternal.isYAligned(): Boolean = sensorLocationY != 0 diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 62a484d42dec..8e14237c0586 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -88,7 +88,7 @@ constructor( /** Whether the auto confirm feature is enabled for the currently-selected user. */ val isAutoConfirmEnabled: StateFlow<Boolean> = authenticationInteractor.isAutoConfirmEnabled - /** The length of the PIN for which we should show a hint. */ + /** The length of the hinted PIN, or `null`, if pin length hint should not be shown. */ val hintedPinLength: StateFlow<Int?> = authenticationInteractor.hintedPinLength /** Whether the pattern should be visible for the currently-selected user. */ diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java index a90980fddfb0..046ccf165d07 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java @@ -18,6 +18,7 @@ package com.android.systemui.dagger; import com.android.systemui.globalactions.ShutdownUiModule; import com.android.systemui.keyguard.CustomizationProvider; +import com.android.systemui.shade.ShadeModule; import com.android.systemui.statusbar.NotificationInsetsModule; import com.android.systemui.statusbar.QsFrameTranslateModule; @@ -32,6 +33,7 @@ import dagger.Subcomponent; DependencyProvider.class, NotificationInsetsModule.class, QsFrameTranslateModule.class, + ShadeModule.class, ShutdownUiModule.class, SystemUIBinder.class, SystemUIModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 3b897394c515..3c42a29451ca 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -82,7 +82,6 @@ import com.android.systemui.screenshot.dagger.ScreenshotModule; import com.android.systemui.security.data.repository.SecurityRepositoryModule; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.shade.ShadeController; -import com.android.systemui.shade.ShadeModule; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.shade.transition.LargeScreenShadeInterpolatorImpl; import com.android.systemui.shared.condition.Monitor; @@ -199,7 +198,6 @@ import javax.inject.Named; SecurityRepositoryModule.class, ScreenRecordModule.class, SettingsUtilModule.class, - ShadeModule.class, SmartRepliesInflationModule.class, SmartspaceModule.class, StatusBarPipelineModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt index ee046c27d72e..484bf3d51f36 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt @@ -26,6 +26,7 @@ import androidx.core.animation.doOnEnd import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators +import com.android.dream.lowlight.util.TruncatedInterpolator import com.android.systemui.R import com.android.systemui.complication.ComplicationHostViewController import com.android.systemui.complication.ComplicationLayoutParams @@ -204,31 +205,28 @@ constructor( translationYAnimator( from = 0f, to = -mDreamInTranslationYDistance.toFloat(), - durationMs = mDreamInTranslationYDurationMs, + durationMs = mDreamInComplicationsAnimDurationMs, delayMs = 0, - interpolator = Interpolators.EMPHASIZED + // Truncate the animation from the full duration to match the alpha + // animation so that the whole animation ends at the same time. + interpolator = + TruncatedInterpolator( + Interpolators.EMPHASIZED, + /*originalDuration=*/ mDreamInTranslationYDurationMs.toFloat(), + /*newDuration=*/ mDreamInComplicationsAnimDurationMs.toFloat() + ) ), alphaAnimator( - from = - mCurrentAlphaAtPosition.getOrDefault( - key = POSITION_BOTTOM, - defaultValue = 1f - ), - to = 0f, - durationMs = mDreamInComplicationsAnimDurationMs, - delayMs = 0, - positions = POSITION_BOTTOM - ) - .apply { - doOnEnd { - // The logical end of the animation is once the alpha and blur - // animations finish, end the animation so that any listeners are - // notified. The Y translation animation is much longer than all of - // the other animations due to how the spec is defined, but is not - // expected to run to completion. - mAnimator?.end() - } - }, + from = + mCurrentAlphaAtPosition.getOrDefault( + key = POSITION_BOTTOM, + defaultValue = 1f + ), + to = 0f, + durationMs = mDreamInComplicationsAnimDurationMs, + delayMs = 0, + positions = POSITION_BOTTOM + ), alphaAnimator( from = mCurrentAlphaAtPosition.getOrDefault( diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index a5a799565eaf..add323983928 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -61,10 +61,6 @@ object Flags { // TODO(b/254512538): Tracking Bug val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply") - // TODO(b/279735475): Tracking Bug - @JvmField - val NEW_LIGHT_BAR_LOGIC = releasedFlag(279735475, "new_light_bar_logic") - /** * This flag is server-controlled and should stay as [unreleasedFlag] since we never want to * enable it on release builds. @@ -72,9 +68,6 @@ object Flags { val NOTIFICATION_MEMORY_LOGGING_ENABLED = unreleasedFlag(119, "notification_memory_logging_enabled") - // TODO(b/257315550): Tracking Bug - val NO_HUN_FOR_OLD_WHEN = releasedFlag(118, "no_hun_for_old_when") - // TODO(b/260335638): Tracking Bug @JvmField val NOTIFICATION_INLINE_REPLY_ANIMATION = @@ -290,12 +283,17 @@ object Flags { /** Migrate the lock icon view to the new keyguard root view. */ // TODO(b/286552209): Tracking bug. @JvmField - val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon") + val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon", teamfood = true) // TODO(b/288276738): Tracking bug. @JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag(241, "widget_on_keyguard") + /** Migrate the NSSL to the a sibling to both the panel and keyguard root view. */ + // TODO(b/288074305): Tracking bug. + @JvmField + val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl") + // 300 - power menu // TODO(b/254512600): Tracking Bug @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite") @@ -367,22 +365,10 @@ object Flags { // TODO(b/256614753): Tracking Bug val NEW_STATUS_BAR_MOBILE_ICONS = releasedFlag(606, "new_status_bar_mobile_icons") - // TODO(b/256614210): Tracking Bug - val NEW_STATUS_BAR_WIFI_ICON = releasedFlag(607, "new_status_bar_wifi_icon") - // TODO(b/256614751): Tracking Bug val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND = unreleasedFlag(608, "new_status_bar_mobile_icons_backend", teamfood = true) - // TODO(b/256613548): Tracking Bug - val NEW_STATUS_BAR_WIFI_ICON_BACKEND = - unreleasedFlag(609, "new_status_bar_wifi_icon_backend", teamfood = true) - - // TODO(b/256623670): Tracking Bug - @JvmField - val BATTERY_SHIELD_ICON = - resourceBooleanFlag(610, R.bool.flag_battery_shield_icon, "battery_shield_icon") - // TODO(b/260881289): Tracking Bug val NEW_STATUS_BAR_ICONS_DEBUG_COLORING = unreleasedFlag(611, "new_status_bar_icons_debug_coloring") @@ -799,4 +785,11 @@ object Flags { // TODO(b/285174336): Tracking Bug @JvmField val USE_REPOS_FOR_BOUNCER_SHOWING = unreleasedFlag(2900, "use_repos_for_bouncer_showing") + + // 3100 - Haptic interactions + + // TODO(b/290213663): Tracking Bug + @JvmField + val ONE_WAY_HAPTICS_API_MIGRATION = + unreleasedFlag(3100, "oneway_haptics_api_migration") } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index a8af67a9fc97..b21b0017208c 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -88,11 +88,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListeners; -import com.android.systemui.shared.tracing.ProtoTraceable; import com.android.systemui.statusbar.phone.LightBarController; -import com.android.systemui.tracing.ProtoTracer; -import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto; -import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.systemui.util.Assert; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.desktopmode.DesktopMode; @@ -115,8 +111,7 @@ import javax.inject.Provider; /** * Utility class to handle edge swipes for back gesture */ -public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBackPlugin>, - ProtoTraceable<SystemUiTraceProto> { +public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBackPlugin> { private static final String TAG = "EdgeBackGestureHandler"; private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( @@ -192,7 +187,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private Consumer<Boolean> mButtonForcedVisibleCallback; private final PluginManager mPluginManager; - private final ProtoTracer mProtoTracer; private final NavigationModeController mNavigationModeController; private final BackPanelController.Factory mBackPanelControllerFactory; private final ViewConfiguration mViewConfiguration; @@ -402,7 +396,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, - ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, @@ -425,7 +418,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mOverviewProxyService = overviewProxyService; mSysUiState = sysUiState; mPluginManager = pluginManager; - mProtoTracer = protoTracer; mNavigationModeController = navigationModeController; mBackPanelControllerFactory = backPanelControllerFactory; mViewConfiguration = viewConfiguration; @@ -557,7 +549,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack */ public void onNavBarAttached() { mIsAttached = true; - mProtoTracer.add(this); mOverviewProxyService.addCallback(mQuickSwitchListener); mSysUiState.addCallback(mSysUiStateCallback); if (mIsTrackpadGestureFeaturesEnabled) { @@ -576,7 +567,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack */ public void onNavBarDetached() { mIsAttached = false; - mProtoTracer.remove(this); mOverviewProxyService.removeCallback(mQuickSwitchListener); mSysUiState.removeCallback(mSysUiStateCallback); mInputManager.unregisterInputDeviceListener(mInputDeviceListener); @@ -1135,8 +1125,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack dispatchToBackAnimation(ev); } } - - mProtoTracer.scheduleFrameUpdate(); } private boolean isButtonPressFromTrackpad(MotionEvent ev) { @@ -1285,14 +1273,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack return topActivity != null && mGestureBlockingActivities.contains(topActivity); } - @Override - public void writeToProto(SystemUiTraceProto proto) { - if (proto.edgeBackGestureHandler == null) { - proto.edgeBackGestureHandler = new EdgeBackGestureHandlerProto(); - } - proto.edgeBackGestureHandler.allowGesture = mAllowGesture; - } - public void setBackAnimation(BackAnimation backAnimation) { mBackAnimation = backAnimation; updateBackAnimationThresholds(); @@ -1319,7 +1299,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final Handler mHandler; private final Executor mBackgroundExecutor; private final UserTracker mUserTracker; - private final ProtoTracer mProtoTracer; private final NavigationModeController mNavigationModeController; private final BackPanelController.Factory mBackPanelControllerFactory; private final ViewConfiguration mViewConfiguration; @@ -1343,7 +1322,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, - ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, @@ -1365,7 +1343,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mHandler = handler; mBackgroundExecutor = backgroundExecutor; mUserTracker = userTracker; - mProtoTracer = protoTracer; mNavigationModeController = navigationModeController; mBackPanelControllerFactory = backPanelControllerFactory; mViewConfiguration = viewConfiguration; @@ -1392,7 +1369,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mHandler, mBackgroundExecutor, mUserTracker, - mProtoTracer, mNavigationModeController, mBackPanelControllerFactory, mViewConfiguration, diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt index c13476fbbe08..eb1ca66f6ca8 100644 --- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt @@ -20,6 +20,7 @@ package com.android.systemui.power.domain.interactor import android.os.PowerManager import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.PowerRepository import com.android.systemui.statusbar.phone.ScreenOffAnimationController @@ -32,6 +33,7 @@ class PowerInteractor @Inject constructor( private val repository: PowerRepository, + private val keyguardRepository: KeyguardRepository, private val falsingCollector: FalsingCollector, private val screenOffAnimationController: ScreenOffAnimationController, private val statusBarStateController: StatusBarStateController, @@ -54,4 +56,21 @@ constructor( falsingCollector.onScreenOnFromTouch() } } + + /** + * Wakes up the device if the device was dozing or going to sleep in order to display a + * full-screen intent. + */ + fun wakeUpForFullScreenIntent() { + if ( + keyguardRepository.wakefulness.value.isStartingToSleep() || + statusBarStateController.isDozing + ) { + repository.wakeUp(why = FSI_WAKE_WHY, wakeReason = PowerManager.WAKE_REASON_APPLICATION) + } + } + + companion object { + private const val FSI_WAKE_WHY = "full_screen_intent" + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index e7dde6617964..207cc1398279 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -25,11 +25,9 @@ import static android.view.MotionEvent.ACTION_UP; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_AWAKE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING; @@ -173,8 +171,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private boolean mInputFocusTransferStarted; private float mInputFocusTransferStartY; private long mInputFocusTransferStartMillis; - private float mWindowCornerRadius; - private boolean mSupportsRoundedCornersOnWindows; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; @VisibleForTesting @@ -454,8 +450,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis Bundle params = new Bundle(); params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder()); - params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius); - params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows); params.putBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER, mSysuiUnlockAnimationController.asBinder()); mUnfoldTransitionProgressForwarder.ifPresent( @@ -588,9 +582,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis com.android.internal.R.string.config_recentsComponentName)); mQuickStepIntent = new Intent(ACTION_QUICKSTEP) .setPackage(mRecentsComponentName.getPackageName()); - mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext); - mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils - .supportsRoundedCornersOnWindows(mContext.getResources()); mSysUiState = sysUiState; mSysUiState.addCallback(this::notifySystemUiStateFlags); mUiEventLogger = uiEventLogger; @@ -1084,8 +1075,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis pw.print(" mInputFocusTransferStarted="); pw.println(mInputFocusTransferStarted); pw.print(" mInputFocusTransferStartY="); pw.println(mInputFocusTransferStartY); pw.print(" mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis); - pw.print(" mWindowCornerRadius="); pw.println(mWindowCornerRadius); - pw.print(" mSupportsRoundedCornersOnWindows="); pw.println(mSupportsRoundedCornersOnWindows); pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion); pw.print(" mNavigationBarSurface="); pw.println(mNavigationBarSurface); pw.print(" mNavBarMode="); pw.println(mNavBarMode); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index d97db3b27c87..ea15035a6c6f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1076,6 +1076,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mTapAgainViewController.init(); mShadeHeaderController.init(); + mShadeHeaderController.setShadeCollapseAction( + () -> collapse(/* delayed= */ false , /* speedUpFactor= */ 1.0f)); mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); mNotificationPanelUnfoldAnimationController.ifPresent(controller -> controller.setup(mNotificationContainerParent)); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java index ebb98883c679..02f337a8752a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java @@ -18,6 +18,7 @@ package com.android.systemui.shade; import android.view.MotionEvent; +import com.android.systemui.CoreStartable; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; @@ -31,7 +32,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; * these are coordinated with {@link StatusBarKeyguardViewManager} via * {@link com.android.systemui.keyguard.KeyguardViewMediator} and others. */ -public interface ShadeController { +public interface ShadeController extends CoreStartable { /** Make our window larger and the shade expanded */ void instantExpandShade(); @@ -164,17 +165,14 @@ public interface ShadeController { void onLaunchAnimationEnd(boolean launchIsFullScreen); /** Sets the listener for when the visibility of the shade changes. */ - default void setVisibilityListener(ShadeVisibilityListener listener) {}; + default void setVisibilityListener(ShadeVisibilityListener listener) {} /** */ - default void setNotificationPresenter(NotificationPresenter presenter) {}; + default void setNotificationPresenter(NotificationPresenter presenter) {} /** */ default void setNotificationShadeWindowViewController( - NotificationShadeWindowViewController notificationShadeWindowViewController) {}; - - /** */ - default void setShadeViewController(ShadeViewController shadeViewController) {}; + NotificationShadeWindowViewController notificationShadeWindowViewController) {} /** Listens for shade visibility changes. */ interface ShadeVisibilityListener { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt index 4d0500786ca3..5f95bcae030e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.shade import android.view.MotionEvent @@ -7,6 +23,7 @@ import javax.inject.Inject /** Empty implementation of ShadeController for variants of Android without shades. */ @SysUISingleton open class ShadeControllerEmptyImpl @Inject constructor() : ShadeController { + override fun start() {} override fun instantExpandShade() {} override fun instantCollapseShade() {} override fun animateCollapseShade( diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java index af74a8d7dca1..22c638177a48 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java @@ -63,6 +63,7 @@ public final class ShadeControllerImpl implements ShadeController { private final StatusBarWindowController mStatusBarWindowController; private final DeviceProvisionedController mDeviceProvisionedController; + private final Lazy<ShadeViewController> mShadeViewControllerLazy; private final Lazy<AssistManager> mAssistManagerLazy; private final Lazy<NotificationGutsManager> mGutsManager; @@ -70,8 +71,6 @@ public final class ShadeControllerImpl implements ShadeController { private boolean mExpandedVisible; - // TODO(b/237661616): Rename this variable to mShadeViewController. - private ShadeViewController mNotificationPanelViewController; private NotificationPresenter mPresenter; private NotificationShadeWindowViewController mNotificationShadeWindowViewController; private ShadeVisibilityListener mShadeVisibilityListener; @@ -87,11 +86,13 @@ public final class ShadeControllerImpl implements ShadeController { DeviceProvisionedController deviceProvisionedController, NotificationShadeWindowController notificationShadeWindowController, WindowManager windowManager, + Lazy<ShadeViewController> shadeViewControllerLazy, Lazy<AssistManager> assistManagerLazy, Lazy<NotificationGutsManager> gutsManager ) { mCommandQueue = commandQueue; mMainExecutor = mainExecutor; + mShadeViewControllerLazy = shadeViewControllerLazy; mStatusBarStateController = statusBarStateController; mStatusBarWindowController = statusBarWindowController; mDeviceProvisionedController = deviceProvisionedController; @@ -107,7 +108,7 @@ public final class ShadeControllerImpl implements ShadeController { public void instantExpandShade() { // Make our window larger and the panel expanded. makeExpandedVisible(true /* force */); - mNotificationPanelViewController.expand(false /* animate */); + getShadeViewController().expand(false /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */); } @@ -123,13 +124,13 @@ public final class ShadeControllerImpl implements ShadeController { "animateCollapse(): mExpandedVisible=" + mExpandedVisible + "flags=" + flags); } if (getNotificationShadeWindowView() != null - && mNotificationPanelViewController.canBeCollapsed() + && getShadeViewController().canBeCollapsed() && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) { // release focus immediately to kick off focus change transition mNotificationShadeWindowController.setNotificationShadeFocusable(false); mNotificationShadeWindowViewController.cancelExpandHelper(); - mNotificationPanelViewController.collapse(true, delayed, speedUpFactor); + getShadeViewController().collapse(true, delayed, speedUpFactor); } } @@ -138,7 +139,7 @@ public final class ShadeControllerImpl implements ShadeController { if (!mCommandQueue.panelsEnabled()) { return; } - mNotificationPanelViewController.expandToNotifications(); + getShadeViewController().expandToNotifications(); } @Override @@ -149,12 +150,12 @@ public final class ShadeControllerImpl implements ShadeController { // Settings are not available in setup if (!mDeviceProvisionedController.isCurrentUserSetup()) return; - mNotificationPanelViewController.expandToQs(); + getShadeViewController().expandToQs(); } @Override public boolean closeShadeIfOpen() { - if (!mNotificationPanelViewController.isFullyCollapsed()) { + if (!getShadeViewController().isFullyCollapsed()) { mCommandQueue.animateCollapsePanels( CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); notifyVisibilityChanged(false); @@ -170,12 +171,12 @@ public final class ShadeControllerImpl implements ShadeController { @Override public boolean isShadeFullyOpen() { - return mNotificationPanelViewController.isShadeFullyExpanded(); + return getShadeViewController().isShadeFullyExpanded(); } @Override public boolean isExpandingOrCollapsing() { - return mNotificationPanelViewController.isExpandingOrCollapsing(); + return getShadeViewController().isExpandingOrCollapsing(); } @Override public void postAnimateCollapseShade() { @@ -194,13 +195,13 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void postOnShadeExpanded(Runnable executable) { - mNotificationPanelViewController.addOnGlobalLayoutListener( + getShadeViewController().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (getNotificationShadeWindowView().isVisibleToUser()) { - mNotificationPanelViewController.removeOnGlobalLayoutListener(this); - mNotificationPanelViewController.postToView(executable); + getShadeViewController().removeOnGlobalLayoutListener(this); + getShadeViewController().postToView(executable); } } }); @@ -224,7 +225,7 @@ public final class ShadeControllerImpl implements ShadeController { @Override public boolean collapseShade() { - if (!mNotificationPanelViewController.isFullyCollapsed()) { + if (!getShadeViewController().isFullyCollapsed()) { // close the shade if it was open animateCollapseShadeForcedDelayed(); notifyVisibilityChanged(false); @@ -252,10 +253,10 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void cancelExpansionAndCollapseShade() { - if (mNotificationPanelViewController.isTracking()) { + if (getShadeViewController().isTracking()) { mNotificationShadeWindowViewController.cancelCurrentTouch(); } - if (mNotificationPanelViewController.isPanelExpanded() + if (getShadeViewController().isPanelExpanded() && mStatusBarStateController.getState() == StatusBarState.SHADE) { animateCollapseShade(); } @@ -311,7 +312,7 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void instantCollapseShade() { - mNotificationPanelViewController.instantCollapse(); + getShadeViewController().instantCollapse(); runPostCollapseRunnables(); } @@ -342,7 +343,7 @@ public final class ShadeControllerImpl implements ShadeController { } // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) - mNotificationPanelViewController.collapse(false, false, 1.0f); + getShadeViewController().collapse(false, false, 1.0f); mExpandedVisible = false; notifyVisibilityChanged(false); @@ -364,7 +365,7 @@ public final class ShadeControllerImpl implements ShadeController { notifyExpandedVisibleChanged(false); mCommandQueue.recomputeDisableFlags( mDisplayId, - mNotificationPanelViewController.shouldHideStatusBarIconsWhenExpanded()); + getShadeViewController().shouldHideStatusBarIconsWhenExpanded()); // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in // the bouncer appear animation. @@ -406,11 +407,14 @@ public final class ShadeControllerImpl implements ShadeController { return mNotificationShadeWindowViewController.getView(); } + private ShadeViewController getShadeViewController() { + return mShadeViewControllerLazy.get(); + } + @Override - public void setShadeViewController(ShadeViewController shadeViewController) { - mNotificationPanelViewController = shadeViewController; - mNotificationPanelViewController.setTrackingStartedListener(this::runPostCollapseRunnables); - mNotificationPanelViewController.setOpenCloseListener( + public void start() { + getShadeViewController().setTrackingStartedListener(this::runPostCollapseRunnables); + getShadeViewController().setOpenCloseListener( new OpenCloseListener() { @Override public void onClosingFinished() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index 411f91f0cb07..8b89ff49f418 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -122,6 +122,8 @@ constructor( } } + var shadeCollapseAction: Runnable? = null + private lateinit var iconManager: StatusBarIconController.TintedIconManager private lateinit var carrierIconSlots: List<String> private lateinit var mShadeCarrierGroupController: ShadeCarrierGroupController @@ -469,9 +471,11 @@ constructor( if (largeScreenActive) { logInstantEvent("Large screen constraints set") header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID) + systemIcons.setOnClickListener { shadeCollapseAction?.run() } } else { logInstantEvent("Small screen constraints set") header.setTransition(HEADER_TRANSITION_ID) + systemIcons.setOnClickListener(null) } header.jumpToState(header.startState) updatePosition() diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt index 8ae9e5e1fb8c..8ec8d115de78 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt @@ -50,6 +50,7 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfC import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.phone.KeyguardBottomAreaView +import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.TapAgainView import com.android.systemui.statusbar.policy.BatteryController @@ -64,7 +65,7 @@ import javax.inject.Named import javax.inject.Provider /** Module for classes related to the notification shade. */ -@Module +@Module(includes = [StartShadeModule::class]) abstract class ShadeModule { @Binds @@ -281,17 +282,16 @@ abstract class ShadeModule { tunerService: TunerService, @Main mainHandler: Handler, contentResolver: ContentResolver, - featureFlags: FeatureFlags, batteryController: BatteryController, ): BatteryMeterViewController { return BatteryMeterViewController( batteryMeterView, + StatusBarLocation.QS, userTracker, configurationController, tunerService, mainHandler, contentResolver, - featureFlags, batteryController, ) } diff --git a/core/java/android/flags/DynamicFlag.java b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt index 68819c58c064..c50693c30533 100644 --- a/core/java/android/flags/DynamicFlag.java +++ b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt @@ -14,18 +14,18 @@ * limitations under the License. */ -package android.flags; +package com.android.systemui.shade -/** - * A flag for which the value may be different from one read to the next. - * - * @param <T> The type of value that this flag stores. E.g. Boolean or String. - * - * @hide - */ -public interface DynamicFlag<T> extends Flag<T> { - @Override - default boolean isDynamic() { - return true; - } +import com.android.systemui.CoreStartable +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +internal abstract class StartShadeModule { + @Binds + @IntoMap + @ClassKey(ShadeController::class) + abstract fun bind(shadeController: ShadeController): CoreStartable } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt index ce730baeed0d..5d06f8d083d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt @@ -21,8 +21,8 @@ import android.widget.FrameLayout /** * A temporary base class that's shared between our old status bar connectivity view implementations - * ([StatusBarWifiView], [StatusBarMobileView]) and our new status bar implementations ( - * [ModernStatusBarWifiView], [ModernStatusBarMobileView]). + * ([StatusBarMobileView]) and our new status bar implementations ([ModernStatusBarWifiView], + * [ModernStatusBarMobileView]). * * Once our refactor is over, we should be able to delete this go-between class and the old view * class. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index a532195c5b9f..92df78bac17f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -73,7 +73,6 @@ import com.android.systemui.settings.DisplayTracker; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.commandline.CommandRegistry; import com.android.systemui.statusbar.policy.CallbackController; -import com.android.systemui.tracing.ProtoTracer; import java.io.FileDescriptor; import java.io.FileOutputStream; @@ -190,7 +189,6 @@ public class CommandQueue extends IStatusBar.Stub implements */ private int mLastUpdatedImeDisplayId = INVALID_DISPLAY; private final DisplayTracker mDisplayTracker; - private ProtoTracer mProtoTracer; private final @Nullable CommandRegistry mRegistry; private final @Nullable DumpHandler mDumpHandler; @@ -504,18 +502,16 @@ public class CommandQueue extends IStatusBar.Stub implements @VisibleForTesting public CommandQueue(Context context, DisplayTracker displayTracker) { - this(context, displayTracker, null, null, null); + this(context, displayTracker, null, null); } public CommandQueue( Context context, DisplayTracker displayTracker, - ProtoTracer protoTracer, CommandRegistry registry, DumpHandler dumpHandler ) { mDisplayTracker = displayTracker; - mProtoTracer = protoTracer; mRegistry = registry; mDumpHandler = dumpHandler; mDisplayTracker.addDisplayChangeCallback(new DisplayTracker.Callback() { @@ -1160,9 +1156,6 @@ public class CommandQueue extends IStatusBar.Stub implements @Override public void startTracing() { synchronized (mLock) { - if (mProtoTracer != null) { - mProtoTracer.start(); - } mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, true).sendToTarget(); } } @@ -1170,9 +1163,6 @@ public class CommandQueue extends IStatusBar.Stub implements @Override public void stopTracing() { synchronized (mLock) { - if (mProtoTracer != null) { - mProtoTracer.stop(); - } mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, false).sendToTarget(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java deleted file mode 100644 index 8d7214d6bd75..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar; - -import static com.android.systemui.plugins.DarkIconDispatcher.getTint; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import com.android.systemui.R; -import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; - -import java.util.ArrayList; - -/** - * Start small: StatusBarWifiView will be able to layout from a WifiIconState - */ -public class StatusBarWifiView extends BaseStatusBarFrameLayout implements DarkReceiver { - private static final String TAG = "StatusBarWifiView"; - - /// Used to show etc dots - private StatusBarIconView mDotView; - /// Contains the main icon layout - private LinearLayout mWifiGroup; - private ImageView mWifiIcon; - private ImageView mIn; - private ImageView mOut; - private View mInoutContainer; - private View mSignalSpacer; - private View mAirplaneSpacer; - private WifiIconState mState; - private String mSlot; - @StatusBarIconView.VisibleState - private int mVisibleState = STATE_HIDDEN; - - public static StatusBarWifiView fromContext(Context context, String slot) { - LayoutInflater inflater = LayoutInflater.from(context); - StatusBarWifiView v = (StatusBarWifiView) inflater.inflate(R.layout.status_bar_wifi_group, null); - v.setSlot(slot); - v.init(); - v.setVisibleState(STATE_ICON); - return v; - } - - public StatusBarWifiView(Context context) { - super(context); - } - - public StatusBarWifiView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public void setSlot(String slot) { - mSlot = slot; - } - - @Override - public void setStaticDrawableColor(int color) { - ColorStateList list = ColorStateList.valueOf(color); - mWifiIcon.setImageTintList(list); - mIn.setImageTintList(list); - mOut.setImageTintList(list); - mDotView.setDecorColor(color); - } - - @Override - public void setDecorColor(int color) { - mDotView.setDecorColor(color); - } - - @Override - public String getSlot() { - return mSlot; - } - - @Override - public boolean isIconVisible() { - return mState != null && mState.visible; - } - - @Override - public void setVisibleState(@StatusBarIconView.VisibleState int state, boolean animate) { - if (state == mVisibleState) { - return; - } - mVisibleState = state; - - switch (state) { - case STATE_ICON: - mWifiGroup.setVisibility(View.VISIBLE); - mDotView.setVisibility(View.GONE); - break; - case STATE_DOT: - mWifiGroup.setVisibility(View.GONE); - mDotView.setVisibility(View.VISIBLE); - break; - case STATE_HIDDEN: - default: - mWifiGroup.setVisibility(View.GONE); - mDotView.setVisibility(View.GONE); - break; - } - } - - @Override - @StatusBarIconView.VisibleState - public int getVisibleState() { - return mVisibleState; - } - - @Override - public void getDrawingRect(Rect outRect) { - super.getDrawingRect(outRect); - float translationX = getTranslationX(); - float translationY = getTranslationY(); - outRect.left += translationX; - outRect.right += translationX; - outRect.top += translationY; - outRect.bottom += translationY; - } - - private void init() { - mWifiGroup = findViewById(R.id.wifi_group); - mWifiIcon = findViewById(R.id.wifi_signal); - mIn = findViewById(R.id.wifi_in); - mOut = findViewById(R.id.wifi_out); - mSignalSpacer = findViewById(R.id.wifi_signal_spacer); - mAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer); - mInoutContainer = findViewById(R.id.inout_container); - - initDotView(); - } - - private void initDotView() { - mDotView = new StatusBarIconView(mContext, mSlot, null); - mDotView.setVisibleState(STATE_DOT); - - int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size_sp); - LayoutParams lp = new LayoutParams(width, width); - lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START; - addView(mDotView, lp); - } - - public void applyWifiState(WifiIconState state) { - boolean requestLayout = false; - - if (state == null) { - requestLayout = getVisibility() != View.GONE; - setVisibility(View.GONE); - mState = null; - } else if (mState == null) { - requestLayout = true; - mState = state.copy(); - initViewState(); - } else if (!mState.equals(state)) { - requestLayout = updateState(state.copy()); - } - - if (requestLayout) { - requestLayout(); - } - } - - private boolean updateState(WifiIconState state) { - setContentDescription(state.contentDescription); - if (mState.resId != state.resId && state.resId >= 0) { - mWifiIcon.setImageDrawable(mContext.getDrawable(state.resId)); - } - - mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); - mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE); - mInoutContainer.setVisibility( - (state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE); - mAirplaneSpacer.setVisibility(state.airplaneSpacerVisible ? View.VISIBLE : View.GONE); - mSignalSpacer.setVisibility(state.signalSpacerVisible ? View.VISIBLE : View.GONE); - - boolean needsLayout = state.activityIn != mState.activityIn - ||state.activityOut != mState.activityOut; - - if (mState.visible != state.visible) { - needsLayout |= true; - setVisibility(state.visible ? View.VISIBLE : View.GONE); - } - - mState = state; - return needsLayout; - } - - private void initViewState() { - setContentDescription(mState.contentDescription); - if (mState.resId >= 0) { - mWifiIcon.setImageDrawable(mContext.getDrawable(mState.resId)); - } - - mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); - mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE); - mInoutContainer.setVisibility( - (mState.activityIn || mState.activityOut) ? View.VISIBLE : View.GONE); - mAirplaneSpacer.setVisibility(mState.airplaneSpacerVisible ? View.VISIBLE : View.GONE); - mSignalSpacer.setVisibility(mState.signalSpacerVisible ? View.VISIBLE : View.GONE); - setVisibility(mState.visible ? View.VISIBLE : View.GONE); - } - - @Override - public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) { - int areaTint = getTint(areas, this, tint); - ColorStateList color = ColorStateList.valueOf(areaTint); - mWifiIcon.setImageTintList(color); - mIn.setImageTintList(color); - mOut.setImageTintList(color); - mDotView.setDecorColor(areaTint); - mDotView.setIconColor(areaTint, false); - } - - - @Override - public String toString() { - return "StatusBarWifiView(slot=" + mSlot + " state=" + mState + ")"; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java index 73f181b8c734..9aa28c31cfd8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -18,10 +18,6 @@ package com.android.systemui.statusbar.connectivity; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.Nullable; @@ -557,10 +553,6 @@ public class NetworkControllerImpl extends BroadcastReceiver mBroadcastDispatcher.unregisterReceiver(this); } - public int getConnectedWifiLevel() { - return mWifiSignalController.getState().level; - } - @Override public AccessPointController getAccessPointController() { return mAccessPoints; @@ -654,14 +646,6 @@ public class NetworkControllerImpl extends BroadcastReceiver return mWifiSignalController.isCarrierMergedWifi(subId); } - boolean hasDefaultNetwork() { - return !mNoDefaultNetwork; - } - - boolean isNonCarrierWifiNetworkAvailable() { - return !mNoNetworksAvailable; - } - boolean isEthernetDefault() { return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); } @@ -1242,15 +1226,12 @@ public class NetworkControllerImpl extends BroadcastReceiver } private boolean mDemoInetCondition; - private WifiState mDemoWifiState; @Override public void onDemoModeStarted() { if (DEBUG) Log.d(TAG, "Entering demo mode"); unregisterListeners(); mDemoInetCondition = mInetCondition; - mDemoWifiState = mWifiSignalController.getState(); - mDemoWifiState.ssid = "DemoMode"; } @Override @@ -1300,41 +1281,6 @@ public class NetworkControllerImpl extends BroadcastReceiver controller.updateConnectivity(connected, connected); } } - String wifi = args.getString("wifi"); - if (wifi != null && !mStatusBarPipelineFlags.runNewWifiIconBackend()) { - boolean show = wifi.equals("show"); - String level = args.getString("level"); - if (level != null) { - mDemoWifiState.level = level.equals("null") ? -1 - : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); - mDemoWifiState.connected = mDemoWifiState.level >= 0; - } - String activity = args.getString("activity"); - if (activity != null) { - switch (activity) { - case "inout": - mWifiSignalController.setActivity(DATA_ACTIVITY_INOUT); - break; - case "in": - mWifiSignalController.setActivity(DATA_ACTIVITY_IN); - break; - case "out": - mWifiSignalController.setActivity(DATA_ACTIVITY_OUT); - break; - default: - mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); - break; - } - } else { - mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); - } - String ssid = args.getString("ssid"); - if (ssid != null) { - mDemoWifiState.ssid = ssid; - } - mDemoWifiState.enabled = show; - mWifiSignalController.notifyListeners(); - } String sims = args.getString("sims"); if (sims != null && !mStatusBarPipelineFlags.useNewMobileIcons()) { int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index 035fa0454bfc..e5ba3ce1fdae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -78,7 +78,6 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.window.StatusBarWindowController; -import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.time.SystemClock; @@ -195,11 +194,10 @@ public interface CentralSurfacesDependenciesModule { static CommandQueue provideCommandQueue( Context context, DisplayTracker displayTracker, - ProtoTracer protoTracer, CommandRegistry registry, DumpHandler dumpHandler ) { - return new CommandQueue(context, displayTracker, protoTracer, registry, dumpHandler); + return new CommandQueue(context, displayTracker, registry, dumpHandler); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt index 5f28ecb56b8c..577ad20cb5d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt @@ -16,27 +16,21 @@ package com.android.systemui.statusbar.notification -import android.content.Context import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.FlagResolver import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import javax.inject.Inject -class NotifPipelineFlags @Inject constructor( - val context: Context, - val featureFlags: FeatureFlags, - val sysPropFlags: FlagResolver, +class NotifPipelineFlags +@Inject +constructor( + private val featureFlags: FeatureFlags, + private val sysPropFlags: FlagResolver, ) { fun isDevLoggingEnabled(): Boolean = featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING) fun allowDismissOngoing(): Boolean = - sysPropFlags.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING) - - fun isOtpRedactionEnabled(): Boolean = - sysPropFlags.isEnabled(NotificationFlags.OTP_REDACTION) - - val isNoHunForOldWhenEnabled: Boolean - get() = featureFlags.isEnabled(Flags.NO_HUN_FOR_OLD_WHEN) + sysPropFlags.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt index 212f2c215989..1cf9c1e1f299 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt @@ -3,7 +3,10 @@ package com.android.systemui.statusbar.notification import android.util.FloatProperty import android.view.View import androidx.annotation.FloatRange +import com.android.systemui.Dependency import com.android.systemui.R +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.statusbar.notification.stack.AnimationProperties import com.android.systemui.statusbar.notification.stack.StackStateAnimator import kotlin.math.abs @@ -20,6 +23,8 @@ interface Roundable { /** Properties required for a Roundable */ val roundableState: RoundableState + val clipHeight: Int + /** Current top roundness */ @get:FloatRange(from = 0.0, to = 1.0) @JvmDefault @@ -40,12 +45,16 @@ interface Roundable { /** Current top corner in pixel, based on [topRoundness] and [maxRadius] */ @JvmDefault val topCornerRadius: Float - get() = topRoundness * maxRadius + get() = + if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.topCornerRadius + else topRoundness * maxRadius /** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */ @JvmDefault val bottomCornerRadius: Float - get() = bottomRoundness * maxRadius + get() = + if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.bottomCornerRadius + else bottomRoundness * maxRadius /** Get and update the current radii */ @JvmDefault @@ -320,14 +329,20 @@ interface Roundable { * @param roundable Target of the radius animation * @param maxRadius Max corner radius in pixels */ -class RoundableState( +class RoundableState +@JvmOverloads +constructor( internal val targetView: View, private val roundable: Roundable, maxRadius: Float, + private val featureFlags: FeatureFlags = Dependency.get(FeatureFlags::class.java) ) { internal var maxRadius = maxRadius private set + internal val newHeadsUpAnimFlagEnabled + get() = featureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS) + /** Animatable for top roundness */ private val topAnimatable = topAnimatable(roundable) @@ -344,6 +359,41 @@ class RoundableState( internal var bottomRoundness = 0f private set + internal val topCornerRadius: Float + get() { + val height = roundable.clipHeight + val topRadius = topRoundness * maxRadius + val bottomRadius = bottomRoundness * maxRadius + + if (height == 0) { + return 0f + } else if (topRadius + bottomRadius > height) { + // The sum of top and bottom corner radii should be at max the clipped height + val overShoot = topRadius + bottomRadius - height + return topRadius - (overShoot * topRoundness / (topRoundness + bottomRoundness)) + } + + return topRadius + } + + internal val bottomCornerRadius: Float + get() { + val height = roundable.clipHeight + val topRadius = topRoundness * maxRadius + val bottomRadius = bottomRoundness * maxRadius + + if (height == 0) { + return 0f + } else if (topRadius + bottomRadius > height) { + // The sum of top and bottom corner radii should be at max the clipped height + val overShoot = topRadius + bottomRadius - height + return bottomRadius - + (overShoot * bottomRoundness / (topRoundness + bottomRoundness)) + } + + return bottomRadius + } + /** Last requested top roundness associated by [SourceType] */ internal val topRoundnessMap = mutableMapOf<SourceType, Float>() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 609f9d47a983..0c43da066fdb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -582,10 +582,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter } private boolean shouldSuppressHeadsUpWhenAwakeForOldWhen(NotificationEntry entry, boolean log) { - if (!mFlags.isNoHunForOldWhenEnabled()) { - return false; - } - final Notification notification = entry.getSbn().getNotification(); if (notification == null) { return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 27510d47b5ab..908c11a1d076 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -566,12 +566,20 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView @Override public float getTopCornerRadius() { + if (isNewHeadsUpAnimFlagEnabled()) { + return super.getTopCornerRadius(); + } + float fraction = getInterpolatedAppearAnimationFraction(); return MathUtils.lerp(0, super.getTopCornerRadius(), fraction); } @Override public float getBottomCornerRadius() { + if (isNewHeadsUpAnimFlagEnabled()) { + return super.getBottomCornerRadius(); + } + float fraction = getInterpolatedAppearAnimationFraction(); return MathUtils.lerp(0, super.getBottomCornerRadius(), fraction); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index 9aa50e989ff1..7f23c1b89b51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -28,7 +28,10 @@ import android.util.IndentingPrintWriter; import android.view.View; import android.view.ViewOutlineProvider; +import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.notification.RoundableState; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; import com.android.systemui.util.DumpUtilsKt; @@ -47,12 +50,14 @@ public abstract class ExpandableOutlineView extends ExpandableView { private float mOutlineAlpha = -1f; private boolean mAlwaysRoundBothCorners; private Path mTmpPath = new Path(); + private final FeatureFlags mFeatureFlags; /** * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when * it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself. */ protected boolean mDismissUsingRowTranslationX = true; + private float[] mTmpCornerRadii = new float[8]; private final ViewOutlineProvider mProvider = new ViewOutlineProvider() { @@ -81,6 +86,15 @@ public abstract class ExpandableOutlineView extends ExpandableView { return mRoundableState; } + @Override + public int getClipHeight() { + if (mCustomOutline) { + return mOutlineRect.height(); + } + + return super.getClipHeight(); + } + protected Path getClipPath(boolean ignoreTranslation) { int left; int top; @@ -112,7 +126,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { return EMPTY_PATH; } float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius(); - if (topRadius + bottomRadius > height) { + if (!isNewHeadsUpAnimFlagEnabled() && (topRadius + bottomRadius > height)) { float overShoot = topRadius + bottomRadius - height; float currentTopRoundness = getTopRoundness(); float currentBottomRoundness = getBottomRoundness(); @@ -153,6 +167,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { super(context, attrs); setOutlineProvider(mProvider); initDimens(); + mFeatureFlags = Dependency.get(FeatureFlags.class); } @Override @@ -360,4 +375,9 @@ public abstract class ExpandableOutlineView extends ExpandableView { } }); } + + // TODO(b/290365128) replace with ViewRefactorFlag + protected boolean isNewHeadsUpAnimFlagEnabled() { + return mFeatureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index f98624409e56..c4c116ba0613 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -93,6 +93,12 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro return mRoundableState; } + @Override + public int getClipHeight() { + int clipHeight = Math.max(mActualHeight - mClipTopAmount - mClipBottomAmount, 0); + return Math.max(clipHeight, mMinimumHeightForClipping); + } + private void initDimens() { mContentShift = getResources().getDimensionPixelSize( R.dimen.shelf_transform_content_shift); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index ef5e86f06ef0..87205e2df9c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -120,6 +120,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple } @Override + public int getClipHeight() { + return mView.getHeight(); + } + + @Override public void applyRoundnessAndInvalidate() { if (mRoundnessChangedListener != null) { // We cannot apply the rounded corner to this View, so our parents (in drawChild()) will diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt index 04308b47abc9..a8d8a8e03ed5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt @@ -30,8 +30,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableView */ class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableView(context, attrs) { + override var clipHeight = 0 var cornerRadius = 0f - var clipHeight = 0 var clipRect = RectF() var clipPath = Path() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index dad806418f2d..626f851d9d11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -192,6 +192,11 @@ public class NotificationChildrenContainer extends ViewGroup } @Override + public int getClipHeight() { + return Math.max(mActualHeight - mClipBottomAmount, 0); + } + + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = Math.min(mAttachedChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 8063c8cfc6f3..c1ceb3ce5ab6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -3758,20 +3758,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable case MotionEvent.ACTION_UP: if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick && isBelowLastNotification(mInitialTouchX, mInitialTouchY)) { - debugLog("handleEmptySpaceClick: touch event propagated further"); + debugShadeLog("handleEmptySpaceClick: touch event propagated further"); mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY); } break; default: - debugLog("handleEmptySpaceClick: MotionEvent ignored"); + debugShadeLog("handleEmptySpaceClick: MotionEvent ignored"); } } - private void debugLog(@CompileTimeConstant final String s) { + private void debugShadeLog(@CompileTimeConstant final String s) { if (mLogger == null) { return; } - mLogger.d(s); + mLogger.logShadeDebugEvent(s); } private void logEmptySpaceClick(MotionEvent ev, boolean isTouchBelowLastNotification, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt index 2c38b8dfca04..3396306412bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt @@ -7,6 +7,7 @@ import com.android.systemui.log.core.LogLevel.INFO import com.android.systemui.log.core.LogLevel.ERROR import com.android.systemui.log.dagger.NotificationHeadsUpLog import com.android.systemui.log.dagger.NotificationRenderLog +import com.android.systemui.log.dagger.ShadeLog import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD @@ -19,7 +20,8 @@ import javax.inject.Inject class NotificationStackScrollLogger @Inject constructor( @NotificationHeadsUpLog private val buffer: LogBuffer, - @NotificationRenderLog private val notificationRenderBuffer: LogBuffer + @NotificationRenderLog private val notificationRenderBuffer: LogBuffer, + @ShadeLog private val shadeLogBuffer: LogBuffer, ) { fun hunAnimationSkipped(entry: NotificationEntry, reason: String) { buffer.log(TAG, INFO, { @@ -63,7 +65,7 @@ class NotificationStackScrollLogger @Inject constructor( }) } - fun d(@CompileTimeConstant msg: String) = buffer.log(TAG, DEBUG, msg) + fun logShadeDebugEvent(@CompileTimeConstant msg: String) = shadeLogBuffer.log(TAG, DEBUG, msg) fun logEmptySpaceClick( isBelowLastNotification: Boolean, @@ -71,7 +73,7 @@ class NotificationStackScrollLogger @Inject constructor( touchIsClick: Boolean, motionEventDesc: String ) { - buffer.log(TAG, DEBUG, { + shadeLogBuffer.log(TAG, DEBUG, { int1 = statusBarState bool1 = touchIsClick bool2 = isBelowLastNotification diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt index 730ef57f1972..26b51a95acad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt @@ -37,6 +37,7 @@ import com.android.systemui.animation.DelegateLaunchAnimatorController import com.android.systemui.assist.AssistManager import com.android.systemui.camera.CameraIntents.Companion.isInsecureCameraIntent import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.DisplayId import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle @@ -72,6 +73,7 @@ constructor( private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>, private val activityLaunchAnimator: ActivityLaunchAnimator, private val context: Context, + @DisplayId private val displayId: Int, private val lockScreenUserManager: NotificationLockscreenUserManager, private val statusBarWindowController: StatusBarWindowController, private val wakefulnessLifecycle: WakefulnessLifecycle, @@ -471,9 +473,7 @@ constructor( intent.getPackage() ) { adapter: RemoteAnimationAdapter? -> val options = - ActivityOptions( - CentralSurfaces.getActivityOptions(centralSurfaces!!.displayId, adapter) - ) + ActivityOptions(CentralSurfaces.getActivityOptions(displayId, adapter)) // We know that the intent of the caller is to dismiss the keyguard and // this runnable is called right after the keyguard is solved, so we tell @@ -596,7 +596,7 @@ constructor( val options = ActivityOptions( CentralSurfaces.getActivityOptions( - centralSurfaces!!.displayId, + displayId, animationAdapter ) ) @@ -762,7 +762,7 @@ constructor( TaskStackBuilder.create(context) .addNextIntent(intent) .startActivities( - CentralSurfaces.getActivityOptions(centralSurfaces!!.displayId, adapter), + CentralSurfaces.getActivityOptions(displayId, adapter), userHandle ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 478baf2f58d6..2b9c3d33e9b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -355,15 +355,11 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { void updateNotificationPanelTouchState(); - int getDisplayId(); - int getRotation(); @VisibleForTesting void setBarStateForTest(int state); - void wakeUpForFullScreenIntent(); - void showTransientUnchecked(); void clearTransient(); 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 8902a186c43a..62e98f934888 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -1613,7 +1613,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // (Right now, there's a circular dependency.) mNotificationShadeWindowController.setWindowRootView(windowRootView); mNotificationShadeWindowViewController.setupExpandedStatusBar(); - mShadeController.setShadeViewController(mShadeSurface); mShadeController.setNotificationShadeWindowViewController( mNotificationShadeWindowViewController); mBackActionInteractor.setup(mQsController, mShadeSurface); @@ -1773,7 +1772,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { try { EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION, sbn.getKey()); - wakeUpForFullScreenIntent(); + mPowerInteractor.wakeUpForFullScreenIntent(); ActivityOptions opts = ActivityOptions.makeBasic(); opts.setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); @@ -1786,16 +1785,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mHeadsUpManager.releaseAllImmediately(); } - @Override - public void wakeUpForFullScreenIntent() { - if (isGoingToSleep() || mDozing) { - mPowerManager.wakeUp( - SystemClock.uptimeMillis(), - PowerManager.WAKE_REASON_APPLICATION, - "com.android.systemui:full_screen_intent"); - } - } - /** * Called when another window is about to transfer it's input focus. */ @@ -2108,11 +2097,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } @Override - public int getDisplayId() { - return mDisplayId; - } - - @Override public void readyForKeyguardDone() { mStatusBarKeyguardViewManager.readyForKeyguardDone(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 39b5b5a4cef8..8e9f382d8c00 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -35,11 +35,9 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger; import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; @@ -58,7 +56,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da private final ArrayList<ModernStatusBarMobileView> mModernMobileViews = new ArrayList<>(); private final int mIconSize; - private StatusBarWifiView mWifiView; private ModernStatusBarWifiView mModernWifiView; private boolean mDemoMode; private int mColor; @@ -238,36 +235,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da addView(v, 0, createLayoutParams()); } - public void addDemoWifiView(WifiIconState state) { - Log.d(TAG, "addDemoWifiView: "); - StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, state.slot); - - int viewIndex = getChildCount(); - // If we have mobile views, put wifi before them - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof StatusBarMobileView - || child instanceof ModernStatusBarMobileView) { - viewIndex = i; - break; - } - } - - mWifiView = view; - mWifiView.applyWifiState(state); - mWifiView.setStaticDrawableColor(mColor); - addView(view, viewIndex, createLayoutParams()); - } - - public void updateWifiState(WifiIconState state) { - Log.d(TAG, "updateWifiState: "); - if (mWifiView == null) { - addDemoWifiView(state); - } else { - mWifiView.applyWifiState(state); - } - } - /** * Add a new mobile icon view */ @@ -352,10 +319,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void onRemoveIcon(StatusIconDisplayable view) { if (view.getSlot().equals("wifi")) { - if (view instanceof StatusBarWifiView) { - removeView(mWifiView); - mWifiView = null; - } else if (view instanceof ModernStatusBarWifiView) { + if (view instanceof ModernStatusBarWifiView) { Log.d(TAG, "onRemoveIcon: removing modern wifi view"); removeView(mModernWifiView); mModernWifiView = null; @@ -409,9 +373,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) { setColor(DarkIconDispatcher.getTint(areas, mStatusIcons, tint)); - if (mWifiView != null) { - mWifiView.onDarkChanged(areas, darkIntensity, tint); - } if (mModernWifiView != null) { mModernWifiView.onDarkChanged(areas, darkIntensity, tint); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java index f26a84ba20a3..dc5eb0d281c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -38,8 +38,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.settings.DisplayTracker; @@ -65,7 +63,6 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private final SysuiDarkIconDispatcher mStatusBarIconController; private final BatteryController mBatteryController; - private final boolean mUseNewLightBarLogic; private BiometricUnlockController mBiometricUnlockController; private LightBarTransitionsController mNavigationBarController; @@ -123,10 +120,8 @@ public class LightBarController implements BatteryController.BatteryStateChangeC DarkIconDispatcher darkIconDispatcher, BatteryController batteryController, NavigationModeController navModeController, - FeatureFlags featureFlags, DumpManager dumpManager, DisplayTracker displayTracker) { - mUseNewLightBarLogic = featureFlags.isEnabled(Flags.NEW_LIGHT_BAR_LOGIC); mDarkIconColor = ctx.getColor(R.color.dark_mode_icon_color_single_tone); mLightIconColor = ctx.getColor(R.color.light_mode_icon_color_single_tone); mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher; @@ -188,51 +183,31 @@ public class LightBarController implements BatteryController.BatteryStateChangeC final boolean last = mNavigationLight; mHasLightNavigationBar = isLight(appearance, navigationBarMode, APPEARANCE_LIGHT_NAVIGATION_BARS); - if (mUseNewLightBarLogic) { - final boolean ignoreScrimForce = mDirectReplying && mNavbarColorManagedByIme; - final boolean darkForScrim = mForceDarkForScrim && !ignoreScrimForce; - final boolean lightForScrim = mForceLightForScrim && !ignoreScrimForce; - final boolean darkForQs = (mQsCustomizing || mQsExpanded) && !mBouncerVisible; - final boolean darkForTop = darkForQs || mGlobalActionsVisible; - mNavigationLight = - ((mHasLightNavigationBar && !darkForScrim) || lightForScrim) && !darkForTop; - if (DEBUG_NAVBAR) { - mLastNavigationBarAppearanceChangedLog = getLogStringBuilder() - .append("onNavigationBarAppearanceChanged()") - .append(" appearance=").append(appearance) - .append(" nbModeChanged=").append(nbModeChanged) - .append(" navigationBarMode=").append(navigationBarMode) - .append(" navbarColorManagedByIme=").append(navbarColorManagedByIme) - .append(" mHasLightNavigationBar=").append(mHasLightNavigationBar) - .append(" ignoreScrimForce=").append(ignoreScrimForce) - .append(" darkForScrim=").append(darkForScrim) - .append(" lightForScrim=").append(lightForScrim) - .append(" darkForQs=").append(darkForQs) - .append(" darkForTop=").append(darkForTop) - .append(" mNavigationLight=").append(mNavigationLight) - .append(" last=").append(last) - .append(" timestamp=").append(System.currentTimeMillis()) - .toString(); - if (DEBUG_LOGS) Log.d(TAG, mLastNavigationBarAppearanceChangedLog); - } - } else { - mNavigationLight = mHasLightNavigationBar - && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim) - && !mQsCustomizing; - if (DEBUG_NAVBAR) { - mLastNavigationBarAppearanceChangedLog = getLogStringBuilder() - .append("onNavigationBarAppearanceChanged()") - .append(" appearance=").append(appearance) - .append(" nbModeChanged=").append(nbModeChanged) - .append(" navigationBarMode=").append(navigationBarMode) - .append(" navbarColorManagedByIme=").append(navbarColorManagedByIme) - .append(" mHasLightNavigationBar=").append(mHasLightNavigationBar) - .append(" mNavigationLight=").append(mNavigationLight) - .append(" last=").append(last) - .append(" timestamp=").append(System.currentTimeMillis()) - .toString(); - if (DEBUG_LOGS) Log.d(TAG, mLastNavigationBarAppearanceChangedLog); - } + final boolean ignoreScrimForce = mDirectReplying && mNavbarColorManagedByIme; + final boolean darkForScrim = mForceDarkForScrim && !ignoreScrimForce; + final boolean lightForScrim = mForceLightForScrim && !ignoreScrimForce; + final boolean darkForQs = (mQsCustomizing || mQsExpanded) && !mBouncerVisible; + final boolean darkForTop = darkForQs || mGlobalActionsVisible; + mNavigationLight = + ((mHasLightNavigationBar && !darkForScrim) || lightForScrim) && !darkForTop; + if (DEBUG_NAVBAR) { + mLastNavigationBarAppearanceChangedLog = getLogStringBuilder() + .append("onNavigationBarAppearanceChanged()") + .append(" appearance=").append(appearance) + .append(" nbModeChanged=").append(nbModeChanged) + .append(" navigationBarMode=").append(navigationBarMode) + .append(" navbarColorManagedByIme=").append(navbarColorManagedByIme) + .append(" mHasLightNavigationBar=").append(mHasLightNavigationBar) + .append(" ignoreScrimForce=").append(ignoreScrimForce) + .append(" darkForScrim=").append(darkForScrim) + .append(" lightForScrim=").append(lightForScrim) + .append(" darkForQs=").append(darkForQs) + .append(" darkForTop=").append(darkForTop) + .append(" mNavigationLight=").append(mNavigationLight) + .append(" last=").append(last) + .append(" timestamp=").append(System.currentTimeMillis()) + .toString(); + if (DEBUG_LOGS) Log.d(TAG, mLastNavigationBarAppearanceChangedLog); } if (mNavigationLight != last) { updateNavigation(); @@ -311,66 +286,39 @@ public class LightBarController implements BatteryController.BatteryStateChangeC public void setScrimState(ScrimState scrimState, float scrimBehindAlpha, GradientColors scrimInFrontColor) { - if (mUseNewLightBarLogic) { - boolean bouncerVisibleLast = mBouncerVisible; - boolean forceDarkForScrimLast = mForceDarkForScrim; - boolean forceLightForScrimLast = mForceLightForScrim; - mBouncerVisible = - scrimState == ScrimState.BOUNCER || scrimState == ScrimState.BOUNCER_SCRIMMED; - final boolean forceForScrim = mBouncerVisible - || scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD; - final boolean scrimColorIsLight = scrimInFrontColor.supportsDarkText(); - - mForceDarkForScrim = forceForScrim && !scrimColorIsLight; - mForceLightForScrim = forceForScrim && scrimColorIsLight; - if (mBouncerVisible != bouncerVisibleLast) { - reevaluate(); - } else if (mHasLightNavigationBar) { - if (mForceDarkForScrim != forceDarkForScrimLast) reevaluate(); - } else { - if (mForceLightForScrim != forceLightForScrimLast) reevaluate(); - } - if (DEBUG_NAVBAR) { - mLastSetScrimStateLog = getLogStringBuilder() - .append("setScrimState()") - .append(" scrimState=").append(scrimState) - .append(" scrimBehindAlpha=").append(scrimBehindAlpha) - .append(" scrimInFrontColor=").append(scrimInFrontColor) - .append(" forceForScrim=").append(forceForScrim) - .append(" scrimColorIsLight=").append(scrimColorIsLight) - .append(" mHasLightNavigationBar=").append(mHasLightNavigationBar) - .append(" mBouncerVisible=").append(mBouncerVisible) - .append(" mForceDarkForScrim=").append(mForceDarkForScrim) - .append(" mForceLightForScrim=").append(mForceLightForScrim) - .append(" timestamp=").append(System.currentTimeMillis()) - .toString(); - if (DEBUG_LOGS) Log.d(TAG, mLastSetScrimStateLog); - } + boolean bouncerVisibleLast = mBouncerVisible; + boolean forceDarkForScrimLast = mForceDarkForScrim; + boolean forceLightForScrimLast = mForceLightForScrim; + mBouncerVisible = + scrimState == ScrimState.BOUNCER || scrimState == ScrimState.BOUNCER_SCRIMMED; + final boolean forceForScrim = mBouncerVisible + || scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD; + final boolean scrimColorIsLight = scrimInFrontColor.supportsDarkText(); + + mForceDarkForScrim = forceForScrim && !scrimColorIsLight; + mForceLightForScrim = forceForScrim && scrimColorIsLight; + if (mBouncerVisible != bouncerVisibleLast) { + reevaluate(); + } else if (mHasLightNavigationBar) { + if (mForceDarkForScrim != forceDarkForScrimLast) reevaluate(); } else { - boolean forceDarkForScrimLast = mForceDarkForScrim; - // For BOUNCER/BOUNCER_SCRIMMED cases, we assume that alpha is always below threshold. - // This enables IMEs to control the navigation bar color. - // For other cases, scrim should be able to veto the light navigation bar. - // NOTE: this was also wrong for S and has been removed in the new logic. - mForceDarkForScrim = scrimState != ScrimState.BOUNCER - && scrimState != ScrimState.BOUNCER_SCRIMMED - && scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD - && !scrimInFrontColor.supportsDarkText(); - if (mHasLightNavigationBar && (mForceDarkForScrim != forceDarkForScrimLast)) { - reevaluate(); - } - if (DEBUG_NAVBAR) { - mLastSetScrimStateLog = getLogStringBuilder() - .append("setScrimState()") - .append(" scrimState=").append(scrimState) - .append(" scrimBehindAlpha=").append(scrimBehindAlpha) - .append(" scrimInFrontColor=").append(scrimInFrontColor) - .append(" mHasLightNavigationBar=").append(mHasLightNavigationBar) - .append(" mForceDarkForScrim=").append(mForceDarkForScrim) - .append(" timestamp=").append(System.currentTimeMillis()) - .toString(); - if (DEBUG_LOGS) Log.d(TAG, mLastSetScrimStateLog); - } + if (mForceLightForScrim != forceLightForScrimLast) reevaluate(); + } + if (DEBUG_NAVBAR) { + mLastSetScrimStateLog = getLogStringBuilder() + .append("setScrimState()") + .append(" scrimState=").append(scrimState) + .append(" scrimBehindAlpha=").append(scrimBehindAlpha) + .append(" scrimInFrontColor=").append(scrimInFrontColor) + .append(" forceForScrim=").append(forceForScrim) + .append(" scrimColorIsLight=").append(scrimColorIsLight) + .append(" mHasLightNavigationBar=").append(mHasLightNavigationBar) + .append(" mBouncerVisible=").append(mBouncerVisible) + .append(" mForceDarkForScrim=").append(mForceDarkForScrim) + .append(" mForceLightForScrim=").append(mForceLightForScrim) + .append(" timestamp=").append(System.currentTimeMillis()) + .toString(); + if (DEBUG_LOGS) Log.d(TAG, mLastSetScrimStateLog); } } @@ -498,7 +446,6 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private final DarkIconDispatcher mDarkIconDispatcher; private final BatteryController mBatteryController; private final NavigationModeController mNavModeController; - private final FeatureFlags mFeatureFlags; private final DumpManager mDumpManager; private final DisplayTracker mDisplayTracker; @@ -507,14 +454,12 @@ public class LightBarController implements BatteryController.BatteryStateChangeC DarkIconDispatcher darkIconDispatcher, BatteryController batteryController, NavigationModeController navModeController, - FeatureFlags featureFlags, DumpManager dumpManager, DisplayTracker displayTracker) { mDarkIconDispatcher = darkIconDispatcher; mBatteryController = batteryController; mNavModeController = navModeController; - mFeatureFlags = featureFlags; mDumpManager = dumpManager; mDisplayTracker = displayTracker; } @@ -522,7 +467,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC /** Create an {@link LightBarController} */ public LightBarController create(Context context) { return new LightBarController(context, mDarkIconDispatcher, mBatteryController, - mNavModeController, mFeatureFlags, mDumpManager, mDisplayTracker); + mNavModeController, mDumpManager, mDisplayTracker); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index e4e912e1ceec..3cd09a61e12a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -56,8 +56,6 @@ import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.ScrimAlpha; @@ -249,8 +247,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private int mScrimsVisibility; private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener; private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator; - private final FeatureFlags mFeatureFlags; - private final boolean mUseNewLightBarLogic; private Consumer<Integer> mScrimVisibleListener; private boolean mBlankScreen; private boolean mScreenBlankingCallbackCalled; @@ -303,12 +299,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, KeyguardTransitionInteractor keyguardTransitionInteractor, @Main CoroutineDispatcher mainDispatcher, - LargeScreenShadeInterpolator largeScreenShadeInterpolator, - FeatureFlags featureFlags) { + LargeScreenShadeInterpolator largeScreenShadeInterpolator) { mScrimStateListener = lightBarController::setScrimState; mLargeScreenShadeInterpolator = largeScreenShadeInterpolator; - mFeatureFlags = featureFlags; - mUseNewLightBarLogic = featureFlags.isEnabled(Flags.NEW_LIGHT_BAR_LOGIC); mDefaultScrimAlpha = BUSY_SCRIM_ALPHA; mKeyguardStateController = keyguardStateController; @@ -1153,13 +1146,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (mClipsQsScrim && mQsBottomVisible) { alpha = mNotificationsAlpha; } - if (mUseNewLightBarLogic) { - mScrimStateListener.accept(mState, alpha, mColors); - } else { - // NOTE: This wasn't wrong, but it implied that each scrim might have different colors, - // when in fact they all share the same GradientColors instance, which we own. - mScrimStateListener.accept(mState, alpha, mScrimInFront.getColors()); - } + mScrimStateListener.accept(mState, alpha, mColors); } private void dispatchScrimsVisible() { @@ -1483,15 +1470,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor(); mColors.setMainColor(background); mColors.setSecondaryColor(accent); - if (mUseNewLightBarLogic) { - final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background); - mColors.setSupportsDarkText(isBackgroundLight); - } else { - // NOTE: This was totally backward, but LightBarController was flipping it back. - // There may be other consumers of this which would struggle though - mColors.setSupportsDarkText( - ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5); - } + final boolean isBackgroundLight = !ContrastColorUtil.isColorDark(background); + mColors.setSupportsDarkText(isBackgroundLight); int surface = Utils.getColorAttr(mScrimBehind.getContext(), com.android.internal.R.attr.materialColorSurface).getDefaultColor(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 59583ddf3e34..42b0a4f13e0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW; -import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW; import android.annotation.Nullable; @@ -43,12 +42,10 @@ import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.BaseStatusBarFrameLayout; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder; @@ -97,16 +94,9 @@ public interface StatusBarIconController { */ void setIcon(String slot, int resourceId, CharSequence contentDescription); - /** */ - void setWifiIcon(String slot, WifiIconState state); - /** * Sets up a wifi icon using the new data pipeline. No effect if the wifi icon has already been * set up (inflated and added to the view hierarchy). - * - * This method completely replaces {@link #setWifiIcon} with the information from the new wifi - * data pipeline. Icons will automatically keep their state up to date, so we don't have to - * worry about funneling state objects through anymore. */ void setNewWifiIcon(); @@ -408,13 +398,7 @@ public interface StatusBarIconController { mMobileIconsViewModel = null; } - if (statusBarPipelineFlags.runNewWifiIconBackend()) { - // This starts the flow for the new pipeline, and will notify us of changes if - // {@link StatusBarPipelineFlags#useNewWifiIcon} is also true. - mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation); - } else { - mWifiViewModel = null; - } + mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation); } public boolean isDemoable() { @@ -462,9 +446,6 @@ public interface StatusBarIconController { case TYPE_ICON: return addIcon(index, slot, blocked, holder.getIcon()); - case TYPE_WIFI: - return addWifiIcon(index, slot, holder.getWifiState()); - case TYPE_WIFI_NEW: return addNewWifiIcon(index, slot); @@ -487,29 +468,7 @@ public interface StatusBarIconController { return view; } - @VisibleForTesting - protected StatusIconDisplayable addWifiIcon(int index, String slot, WifiIconState state) { - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - throw new IllegalStateException("Attempting to add a wifi icon while the new " - + "icons are enabled is not supported"); - } - - final StatusBarWifiView view = onCreateStatusBarWifiView(slot); - view.applyWifiState(state); - mGroup.addView(view, index, onCreateLayoutParams()); - - if (mIsInDemoMode) { - mDemoStatusIcons.addDemoWifiView(state); - } - return view; - } - protected StatusIconDisplayable addNewWifiIcon(int index, String slot) { - if (!mStatusBarPipelineFlags.useNewWifiIcon()) { - throw new IllegalStateException("Attempting to add a wifi icon using the new" - + "pipeline, but the enabled flag is false."); - } - ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot); mGroup.addView(view, index, onCreateLayoutParams()); @@ -573,11 +532,6 @@ public interface StatusBarIconController { return new StatusBarIconView(mContext, slot, null, blocked); } - private StatusBarWifiView onCreateStatusBarWifiView(String slot) { - StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, slot); - return view; - } - private ModernStatusBarWifiView onCreateModernStatusBarWifiView(String slot) { return ModernStatusBarWifiView.constructAndBind(mContext, slot, mWifiViewModel); } @@ -640,9 +594,6 @@ public interface StatusBarIconController { case TYPE_ICON: onSetIcon(viewIndex, holder.getIcon()); return; - case TYPE_WIFI: - onSetWifiIcon(viewIndex, holder.getWifiState()); - return; case TYPE_MOBILE: onSetMobileIcon(viewIndex, holder.getMobileState()); return; @@ -655,23 +606,6 @@ public interface StatusBarIconController { } } - public void onSetWifiIcon(int viewIndex, WifiIconState state) { - View view = mGroup.getChildAt(viewIndex); - if (view instanceof StatusBarWifiView) { - ((StatusBarWifiView) view).applyWifiState(state); - } else if (view instanceof ModernStatusBarWifiView) { - // ModernStatusBarWifiView will automatically apply state based on its callbacks, so - // we don't need to call applyWifiState. - } else { - throw new IllegalStateException("View at " + viewIndex + " must be of type " - + "StatusBarWifiView or ModernStatusBarWifiView"); - } - - if (mIsInDemoMode) { - mDemoStatusIcons.updateWifiState(state); - } - } - public void onSetMobileIcon(int viewIndex, MobileIconState state) { View view = mGroup.getChildAt(viewIndex); if (view instanceof StatusBarMobileView) { @@ -703,9 +637,7 @@ public interface StatusBarIconController { mIsInDemoMode = true; if (mDemoStatusIcons == null) { mDemoStatusIcons = createDemoStatusIcons(); - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - mDemoStatusIcons.addModernWifiView(mWifiViewModel); - } + mDemoStatusIcons.addModernWifiView(mWifiViewModel); } mDemoStatusIcons.onDemoModeStarted(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 80d5651a65dc..d1a02d6cd611 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -40,7 +40,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; @@ -202,35 +201,7 @@ public class StatusBarIconControllerImpl implements Tunable, } @Override - public void setWifiIcon(String slot, WifiIconState state) { - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - Log.d(TAG, "ignoring old pipeline callback because the new wifi icon is enabled"); - return; - } - - if (state == null) { - removeIcon(slot, 0); - return; - } - - StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0); - if (holder == null) { - holder = StatusBarIconHolder.fromWifiIconState(state); - setIcon(slot, holder); - } else { - holder.setWifiState(state); - handleSet(slot, holder); - } - } - - - @Override public void setNewWifiIcon() { - if (!mStatusBarPipelineFlags.useNewWifiIcon()) { - Log.d(TAG, "ignoring new pipeline callback because the new wifi icon is disabled"); - return; - } - String slot = mContext.getString(com.android.internal.R.string.status_bar_wifi); StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, /* tag= */ 0); if (holder == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java index 833cb93f62e9..01fd247f54bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java @@ -25,7 +25,6 @@ import android.os.UserHandle; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel; import java.lang.annotation.Retention; @@ -36,7 +35,6 @@ import java.lang.annotation.RetentionPolicy; */ public class StatusBarIconHolder { public static final int TYPE_ICON = 0; - public static final int TYPE_WIFI = 1; public static final int TYPE_MOBILE = 2; /** * TODO (b/249790733): address this once the new pipeline is in place @@ -65,7 +63,6 @@ public class StatusBarIconHolder { @IntDef({ TYPE_ICON, - TYPE_WIFI, TYPE_MOBILE, TYPE_MOBILE_NEW, TYPE_WIFI_NEW @@ -74,7 +71,6 @@ public class StatusBarIconHolder { @interface IconType {} private StatusBarIcon mIcon; - private WifiIconState mWifiState; private MobileIconState mMobileState; private @IconType int mType = TYPE_ICON; private int mTag = 0; @@ -83,7 +79,6 @@ public class StatusBarIconHolder { public static String getTypeString(@IconType int type) { switch(type) { case TYPE_ICON: return "ICON"; - case TYPE_WIFI: return "WIFI_OLD"; case TYPE_MOBILE: return "MOBILE_OLD"; case TYPE_MOBILE_NEW: return "MOBILE_NEW"; case TYPE_WIFI_NEW: return "WIFI_NEW"; @@ -101,25 +96,6 @@ public class StatusBarIconHolder { return wrapper; } - /** */ - public static StatusBarIconHolder fromResId( - Context context, - int resId, - CharSequence contentDescription) { - StatusBarIconHolder holder = new StatusBarIconHolder(); - holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(), - Icon.createWithResource( context, resId), 0, 0, contentDescription); - return holder; - } - - /** */ - public static StatusBarIconHolder fromWifiIconState(WifiIconState state) { - StatusBarIconHolder holder = new StatusBarIconHolder(); - holder.mWifiState = state; - holder.mType = TYPE_WIFI; - return holder; - } - /** Creates a new holder with for the new wifi icon. */ public static StatusBarIconHolder forNewWifiIcon() { StatusBarIconHolder holder = new StatusBarIconHolder(); @@ -178,15 +154,6 @@ public class StatusBarIconHolder { } @Nullable - public WifiIconState getWifiState() { - return mWifiState; - } - - public void setWifiState(WifiIconState state) { - mWifiState = state; - } - - @Nullable public MobileIconState getMobileState() { return mMobileState; } @@ -199,8 +166,6 @@ public class StatusBarIconHolder { switch (mType) { case TYPE_ICON: return mIcon.visible; - case TYPE_WIFI: - return mWifiState.visible; case TYPE_MOBILE: return mMobileState.visible; case TYPE_MOBILE_NEW: @@ -223,10 +188,6 @@ public class StatusBarIconHolder { mIcon.visible = visible; break; - case TYPE_WIFI: - mWifiState.visible = visible; - break; - case TYPE_MOBILE: mMobileState.visible = visible; break; 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 f79a08173bf2..ec0c00e26c2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -54,8 +54,10 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.EventLogTags; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeViewController; @@ -94,6 +96,7 @@ import javax.inject.Inject; class StatusBarNotificationActivityStarter implements NotificationActivityStarter { private final Context mContext; + private final int mDisplayId; private final Handler mMainThreadHandler; private final Executor mUiBgExecutor; @@ -120,12 +123,12 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte private final MetricsLogger mMetricsLogger; private final StatusBarNotificationActivityStarterLogger mLogger; - private final CentralSurfaces mCentralSurfaces; private final NotificationPresenter mPresenter; private final ShadeViewController mShadeViewController; private final NotificationShadeWindowController mNotificationShadeWindowController; private final ActivityLaunchAnimator mActivityLaunchAnimator; private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; + private final PowerInteractor mPowerInteractor; private final UserTracker mUserTracker; private final OnUserInteractionCallback mOnUserInteractionCallback; @@ -134,6 +137,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte @Inject StatusBarNotificationActivityStarter( Context context, + @DisplayId int displayId, Handler mainThreadHandler, Executor uiBgExecutor, NotificationVisibilityProvider visibilityProvider, @@ -156,16 +160,17 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, OnUserInteractionCallback onUserInteractionCallback, - CentralSurfaces centralSurfaces, NotificationPresenter presenter, ShadeViewController shadeViewController, NotificationShadeWindowController notificationShadeWindowController, ActivityLaunchAnimator activityLaunchAnimator, NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, LaunchFullScreenIntentProvider launchFullScreenIntentProvider, + PowerInteractor powerInteractor, FeatureFlags featureFlags, UserTracker userTracker) { mContext = context; + mDisplayId = displayId; mMainThreadHandler = mainThreadHandler; mUiBgExecutor = uiBgExecutor; mVisibilityProvider = visibilityProvider; @@ -190,12 +195,11 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mMetricsLogger = metricsLogger; mLogger = logger; mOnUserInteractionCallback = onUserInteractionCallback; - // TODO: use KeyguardStateController#isOccluded to remove this dependency - mCentralSurfaces = centralSurfaces; mPresenter = presenter; mShadeViewController = shadeViewController; mActivityLaunchAnimator = activityLaunchAnimator; mNotificationAnimationProvider = notificationAnimationProvider; + mPowerInteractor = powerInteractor; mUserTracker = userTracker; launchFullScreenIntentProvider.registerListener(entry -> launchFullScreenIntent(entry)); @@ -280,7 +284,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mShadeController.addPostCollapseAction(runnable); mShadeController.collapseShade(true /* animate */); } else if (mKeyguardStateController.isShowing() - && mCentralSurfaces.isOccluded()) { + && mKeyguardStateController.isOccluded()) { mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); mShadeController.collapseShade(); } else { @@ -452,11 +456,11 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte long eventTime = row.getAndResetLastActionUpTime(); Bundle options = eventTime > 0 ? getActivityOptions( - mCentralSurfaces.getDisplayId(), + mDisplayId, adapter, mKeyguardStateController.isShowing(), eventTime) - : getActivityOptions(mCentralSurfaces.getDisplayId(), adapter); + : getActivityOptions(mDisplayId, adapter); int result = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, null, null, options); mLogger.logSendPendingIntent(entry, intent, result); @@ -491,7 +495,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte (adapter) -> TaskStackBuilder.create(mContext) .addNextIntentWithParentStack(intent) .startActivities(getActivityOptions( - mCentralSurfaces.getDisplayId(), + mDisplayId, adapter), new UserHandle(UserHandle.getUserId(appUid)))); }); @@ -539,7 +543,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate, intent.getPackage(), (adapter) -> tsb.startActivities( - getActivityOptions(mCentralSurfaces.getDisplayId(), adapter), + getActivityOptions(mDisplayId, adapter), mUserTracker.getUserHandle())); }); return true; @@ -592,7 +596,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte try { EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, entry.getKey()); - mCentralSurfaces.wakeUpForFullScreenIntent(); + mPowerInteractor.wakeUpForFullScreenIntent(); ActivityOptions options = ActivityOptions.makeBasic(); options.setPendingIntentBackgroundActivityStartMode( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index d731f8886536..69199966dea6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -30,7 +30,6 @@ import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.MobileDataIndicators; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; -import com.android.systemui.statusbar.connectivity.WifiIndicators; import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -51,7 +50,6 @@ public class StatusBarSignalPolicy implements SignalCallback, private final String mSlotAirplane; private final String mSlotMobile; - private final String mSlotWifi; private final String mSlotEthernet; private final String mSlotVpn; private final String mSlotNoCalling; @@ -67,17 +65,14 @@ public class StatusBarSignalPolicy implements SignalCallback, private boolean mHideAirplane; private boolean mHideMobile; - private boolean mHideWifi; private boolean mHideEthernet; private boolean mActivityEnabled; // Track as little state as possible, and only for padding purposes private boolean mIsAirplaneMode = false; - private boolean mIsWifiEnabled = false; private ArrayList<MobileIconState> mMobileStates = new ArrayList<>(); private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>(); - private WifiIconState mWifiIconState = new WifiIconState(); private boolean mInitialized; @Inject @@ -99,7 +94,6 @@ public class StatusBarSignalPolicy implements SignalCallback, mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); - mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet); mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn); mSlotNoCalling = mContext.getString(com.android.internal.R.string.status_bar_no_calling); @@ -154,15 +148,13 @@ public class StatusBarSignalPolicy implements SignalCallback, ArraySet<String> hideList = StatusBarIconController.getIconHideList(mContext, newValue); boolean hideAirplane = hideList.contains(mSlotAirplane); boolean hideMobile = hideList.contains(mSlotMobile); - boolean hideWifi = hideList.contains(mSlotWifi); boolean hideEthernet = hideList.contains(mSlotEthernet); if (hideAirplane != mHideAirplane || hideMobile != mHideMobile - || hideEthernet != mHideEthernet || hideWifi != mHideWifi) { + || hideEthernet != mHideEthernet) { mHideAirplane = hideAirplane; mHideMobile = hideMobile; mHideEthernet = hideEthernet; - mHideWifi = hideWifi; // Re-register to get new callbacks. mNetworkController.removeCallback(this); mNetworkController.addCallback(this); @@ -170,56 +162,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } @Override - public void setWifiIndicators(@NonNull WifiIndicators indicators) { - if (DEBUG) { - Log.d(TAG, "setWifiIndicators: " + indicators); - } - boolean visible = indicators.statusIcon.visible && !mHideWifi; - boolean in = indicators.activityIn && mActivityEnabled && visible; - boolean out = indicators.activityOut && mActivityEnabled && visible; - mIsWifiEnabled = indicators.enabled; - - WifiIconState newState = mWifiIconState.copy(); - - if (mWifiIconState.noDefaultNetwork && mWifiIconState.noNetworksAvailable - && !mIsAirplaneMode) { - newState.visible = true; - newState.resId = R.drawable.ic_qs_no_internet_unavailable; - } else if (mWifiIconState.noDefaultNetwork && !mWifiIconState.noNetworksAvailable - && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { - newState.visible = true; - newState.resId = R.drawable.ic_qs_no_internet_available; - } else { - newState.visible = visible; - newState.resId = indicators.statusIcon.icon; - newState.activityIn = in; - newState.activityOut = out; - newState.contentDescription = indicators.statusIcon.contentDescription; - MobileIconState first = getFirstMobileState(); - newState.signalSpacerVisible = first != null && first.typeId != 0; - } - newState.slot = mSlotWifi; - newState.airplaneSpacerVisible = mIsAirplaneMode; - updateWifiIconWithState(newState); - mWifiIconState = newState; - } - - private void updateShowWifiSignalSpacer(WifiIconState state) { - MobileIconState first = getFirstMobileState(); - state.signalSpacerVisible = first != null && first.typeId != 0; - } - - private void updateWifiIconWithState(WifiIconState state) { - if (DEBUG) Log.d(TAG, "WifiIconState: " + state == null ? "" : state.toString()); - if (state.visible && state.resId > 0) { - mIconController.setWifiIcon(mSlotWifi, state); - mIconController.setIconVisibility(mSlotWifi, true); - } else { - mIconController.setIconVisibility(mSlotWifi, false); - } - } - - @Override public void setCallIndicator(@NonNull IconState statusIcon, int subId) { if (DEBUG) { Log.d(TAG, "setCallIndicator: " @@ -257,10 +199,6 @@ public class StatusBarSignalPolicy implements SignalCallback, return; } - // Visibility of the data type indicator changed - boolean typeChanged = indicators.statusType != state.typeId - && (indicators.statusType == 0 || state.typeId == 0); - state.visible = indicators.statusIcon.visible && !mHideMobile; state.strengthId = indicators.statusIcon.icon; state.typeId = indicators.statusType; @@ -277,15 +215,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } // Always send a copy to maintain value type semantics mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates)); - - if (typeChanged) { - WifiIconState wifiCopy = mWifiIconState.copy(); - updateShowWifiSignalSpacer(wifiCopy); - if (!Objects.equals(wifiCopy, mWifiIconState)) { - updateWifiIconWithState(wifiCopy); - mWifiIconState = wifiCopy; - } - } } private CallIndicatorIconState getNoCallingState(int subId) { @@ -308,15 +237,6 @@ public class StatusBarSignalPolicy implements SignalCallback, return null; } - private MobileIconState getFirstMobileState() { - if (mMobileStates.size() > 0) { - return mMobileStates.get(0); - } - - return null; - } - - /** * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators * so we don't have to update the icon manager at this point, just remove the old ones @@ -504,60 +424,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } } - public static class WifiIconState extends SignalIconState{ - public int resId; - public boolean airplaneSpacerVisible; - public boolean signalSpacerVisible; - public boolean noDefaultNetwork; - public boolean noValidatedNetwork; - public boolean noNetworksAvailable; - - @Override - public boolean equals(Object o) { - // Skipping reference equality bc this should be more of a value type - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - WifiIconState that = (WifiIconState) o; - return resId == that.resId - && airplaneSpacerVisible == that.airplaneSpacerVisible - && signalSpacerVisible == that.signalSpacerVisible - && noDefaultNetwork == that.noDefaultNetwork - && noValidatedNetwork == that.noValidatedNetwork - && noNetworksAvailable == that.noNetworksAvailable; - } - - public void copyTo(WifiIconState other) { - super.copyTo(other); - other.resId = resId; - other.airplaneSpacerVisible = airplaneSpacerVisible; - other.signalSpacerVisible = signalSpacerVisible; - other.noDefaultNetwork = noDefaultNetwork; - other.noValidatedNetwork = noValidatedNetwork; - other.noNetworksAvailable = noNetworksAvailable; - } - - public WifiIconState copy() { - WifiIconState newState = new WifiIconState(); - copyTo(newState); - return newState; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), - resId, airplaneSpacerVisible, signalSpacerVisible, noDefaultNetwork, - noValidatedNetwork, noNetworksAvailable); - } - - @Override public String toString() { - return "WifiIconState(resId=" + resId + ", visible=" + visible + ")"; - } - } - /** * A little different. This one delegates to SignalDrawable instead of a specific resId */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java index 97a1bd1851d9..c8e73d33c452 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java @@ -26,6 +26,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.phone.SystemBarAttributesListener; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; @@ -72,6 +73,13 @@ public interface StatusBarFragmentModule { /** */ @Provides @StatusBarFragmentScope + static StatusBarLocation getStatusBarLocation() { + return StatusBarLocation.HOME; + } + + /** */ + @Provides + @StatusBarFragmentScope @Named(START_SIDE_CONTENT) static View startSideContent(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.status_bar_start_side_content); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt index 4a684d9f8e36..29829e46cda7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt @@ -45,18 +45,6 @@ constructor( fun runNewMobileIconsBackend(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_MOBILE_ICONS_BACKEND) || useNewMobileIcons() - /** True if we should display the wifi icon using the new status bar data pipeline. */ - fun useNewWifiIcon(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_WIFI_ICON) - - /** - * True if we should run the new wifi icon backend to get the logging. - * - * Does *not* affect whether we render the wifi icon using the new backend data. See - * [useNewWifiIcon] for that. - */ - fun runNewWifiIconBackend(): Boolean = - featureFlags.isEnabled(Flags.NEW_STATUS_BAR_WIFI_ICON_BACKEND) || useNewWifiIcon() - /** * Returns true if we should apply some coloring to the icons that were rendered with the new * pipeline to help with debugging. @@ -71,5 +59,5 @@ constructor( * @return true if this icon is controlled by any of the status bar pipeline flags */ fun isIconControlledByFlags(slotName: String): Boolean = - slotName == wifiSlot && useNewWifiIcon() || slotName == mobileSlot && useNewMobileIcons() + slotName == wifiSlot || (slotName == mobileSlot && useNewMobileIcons()) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt index 83738544ee84..a1b96dd327e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt @@ -101,12 +101,7 @@ open class ModernStatusBarView(context: Context, attrs: AttributeSet?) : this.binding = bindingCreator.invoke() } - /** - * Creates a [StatusBarIconView] that is always in DOT mode and adds it to this view. - * - * Mostly duplicated from [com.android.systemui.statusbar.StatusBarWifiView] and - * [com.android.systemui.statusbar.StatusBarMobileView]. - */ + /** Creates a [StatusBarIconView] that is always in DOT mode and adds it to this view. */ private fun initDotView() { // TODO(b/238425913): Could we just have this dot view be part of the layout with a dot // drawable so we don't need to inflate it manually? Would that not work with animations? diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt index 174298ab6490..6d7182376fab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt @@ -68,12 +68,7 @@ constructor( launch { locationViewModel.wifiIcon.collect { wifiIcon -> // Only notify the icon controller if we want to *render* the new icon. - // Note that this flow may still run if - // [statusBarPipelineFlags.runNewWifiIconBackend] is true because we may - // want to get the logging data without rendering. - if ( - wifiIcon is WifiIcon.Visible && statusBarPipelineFlags.useNewWifiIcon() - ) { + if (wifiIcon is WifiIcon.Visible) { iconController.setNewWifiIcon() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index 3d165912a09c..7df083afcd19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -19,6 +19,9 @@ package com.android.systemui.statusbar.policy; import android.annotation.Nullable; import android.view.View; +import androidx.annotation.NonNull; + +import com.android.systemui.Dumpable; import com.android.systemui.demomode.DemoMode; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; @@ -136,7 +139,7 @@ public interface BatteryController extends DemoMode, * A listener that will be notified whenever a change in battery level or power save mode has * occurred. */ - interface BatteryStateChangeCallback { + interface BatteryStateChangeCallback extends Dumpable { default void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { } @@ -158,6 +161,11 @@ public interface BatteryController extends DemoMode, default void onIsBatteryDefenderChanged(boolean isBatteryDefender) { } + + @Override + default void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + pw.println(this); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index e69d86c31abc..d5d8f4d7598e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -22,6 +22,7 @@ import static android.os.BatteryManager.EXTRA_CHARGING_STATUS; import static android.os.BatteryManager.EXTRA_PRESENT; import static com.android.settingslib.fuelgauge.BatterySaverLogging.SAVER_ENABLED_QS; +import static com.android.systemui.util.DumpUtilsKt.asIndenting; import android.annotation.WorkerThread; import android.content.BroadcastReceiver; @@ -33,6 +34,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; import android.os.PowerSaveState; +import android.util.IndentingPrintWriter; import android.util.Log; import android.view.View; @@ -157,15 +159,29 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } @Override - public void dump(PrintWriter pw, String[] args) { - pw.println("BatteryController state:"); - pw.print(" mLevel="); pw.println(mLevel); - pw.print(" mPluggedIn="); pw.println(mPluggedIn); - pw.print(" mCharging="); pw.println(mCharging); - pw.print(" mCharged="); pw.println(mCharged); - pw.print(" mIsBatteryDefender="); pw.println(mIsBatteryDefender); - pw.print(" mPowerSave="); pw.println(mPowerSave); - pw.print(" mStateUnknown="); pw.println(mStateUnknown); + public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + IndentingPrintWriter ipw = asIndenting(pw); + ipw.println("BatteryController state:"); + ipw.increaseIndent(); + ipw.print("mHasReceivedBattery="); ipw.println(mHasReceivedBattery); + ipw.print("mLevel="); ipw.println(mLevel); + ipw.print("mPluggedIn="); ipw.println(mPluggedIn); + ipw.print("mCharging="); ipw.println(mCharging); + ipw.print("mCharged="); ipw.println(mCharged); + ipw.print("mIsBatteryDefender="); ipw.println(mIsBatteryDefender); + ipw.print("mPowerSave="); ipw.println(mPowerSave); + ipw.print("mStateUnknown="); ipw.println(mStateUnknown); + ipw.println("Callbacks:------------------"); + // Since the above lines are already indented, we need to indent twice for the callbacks. + ipw.increaseIndent(); + synchronized (mChangeCallbacks) { + final int n = mChangeCallbacks.size(); + for (int i = 0; i < n; i++) { + mChangeCallbacks.get(i).dump(ipw, args); + } + } + ipw.decreaseIndent(); + ipw.println("------------------"); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java index bcf3b0cbfc86..24987abd7a85 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java @@ -240,8 +240,10 @@ public class StatusBarWindowController { Insets.of(0, safeTouchRegionHeight, 0, 0)); } lp.providedInsets = new InsetsFrameProvider[] { - new InsetsFrameProvider(mInsetsSourceOwner, 0, statusBars()), - new InsetsFrameProvider(mInsetsSourceOwner, 0, tappableElement()), + new InsetsFrameProvider(mInsetsSourceOwner, 0, statusBars()) + .setInsetsSize(getInsets(height)), + new InsetsFrameProvider(mInsetsSourceOwner, 0, tappableElement()) + .setInsetsSize(getInsets(height)), gestureInsetsProvider }; return lp; @@ -306,12 +308,37 @@ public class StatusBarWindowController { mLpChanged.height = state.mIsLaunchAnimationRunning ? ViewGroup.LayoutParams.MATCH_PARENT : mBarHeight; for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { + int height = SystemBarUtils.getStatusBarHeightForRotation(mContext, rot); mLpChanged.paramsForRotation[rot].height = - state.mIsLaunchAnimationRunning ? ViewGroup.LayoutParams.MATCH_PARENT : - SystemBarUtils.getStatusBarHeightForRotation(mContext, rot); + state.mIsLaunchAnimationRunning ? ViewGroup.LayoutParams.MATCH_PARENT : height; + // The status bar height could change at runtime if one display has a cutout while + // another doesn't (like some foldables). It could also change when using debug cutouts. + // So, we need to re-fetch the height and re-apply it to the insets each time to avoid + // bugs like b/290300359. + InsetsFrameProvider[] providers = mLpChanged.paramsForRotation[rot].providedInsets; + if (providers != null) { + for (InsetsFrameProvider provider : providers) { + provider.setInsetsSize(getInsets(height)); + } + } } } + /** + * Get the insets that should be applied to the status bar window given the current status bar + * height. + * + * The status bar window height can sometimes be full-screen (see {@link #applyHeight(State)}. + * However, the status bar *insets* should *not* be full-screen, because this would prevent apps + * from drawing any content and can cause animations to be cancelled (see b/283958440). Instead, + * the status bar insets should always be equal to the space occupied by the actual status bar + * content -- setting the insets correctly will prevent window manager from unnecessarily + * re-drawing this window and other windows. This method provides the correct insets. + */ + private Insets getInsets(int height) { + return Insets.of(0, height, 0, 0); + } + private void apply(State state) { if (!mIsAttached) { return; diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt index 3e1c13c1cba8..c1ac800b8159 100644 --- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt @@ -64,9 +64,9 @@ constructor( // These values must only be accessed on the handler. private var batteryCapacity = 1.0f private var suppressed = false - private var inputDeviceId: Int? = null private var instanceId: InstanceId? = null - + @VisibleForTesting var inputDeviceId: Int? = null + private set @VisibleForTesting var instanceIdSequence = InstanceIdSequence(1 shl 13) fun init() { @@ -110,10 +110,10 @@ constructor( fun updateBatteryState(deviceId: Int, batteryState: BatteryState) { handler.post updateBattery@{ + inputDeviceId = deviceId if (batteryState.capacity == batteryCapacity || batteryState.capacity <= 0f) return@updateBattery - inputDeviceId = deviceId batteryCapacity = batteryState.capacity debugLog { "Updating notification battery state to $batteryCapacity " + diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt index c109eb4134bb..324ef4baf7d5 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt @@ -58,6 +58,16 @@ class ToastLogger @Inject constructor( }) } + fun logOnSkipToastForInvalidDisplay(packageName: String, token: String, displayId: Int) { + log(DEBUG, { + str1 = packageName + str2 = token + int1 = displayId + }, { + "[$str2] Skip toast for [$str1] scheduled on unavailable display #$int1" + }) + } + private inline fun log( logLevel: LogLevel, initializer: LogMessage.() -> Unit, diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java index ed14c8ad150c..ae8128d20d3d 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java @@ -32,6 +32,7 @@ import android.os.IBinder; import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; +import android.view.Display; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; import android.widget.ToastPresenter; @@ -115,8 +116,14 @@ public class ToastUI implements CoreStartable, CommandQueue.Callbacks { Context context = mContext.createContextAsUser(userHandle, 0); DisplayManager mDisplayManager = mContext.getSystemService(DisplayManager.class); - Context displayContext = context.createDisplayContext( - mDisplayManager.getDisplay(displayId)); + Display display = mDisplayManager.getDisplay(displayId); + if (display == null) { + // Display for which this toast was scheduled for is no longer available. + mToastLogger.logOnSkipToastForInvalidDisplay(packageName, token.toString(), + displayId); + return; + } + Context displayContext = context.createDisplayContext(display); mToast = mToastFactory.createToast(mContext /* sysuiContext */, text, packageName, userHandle.getIdentifier(), mOrientation); diff --git a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java b/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java deleted file mode 100644 index b54d1566eba3..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2019 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.tracing; - -import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_H; -import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_L; - -import android.content.Context; -import android.os.SystemClock; - -import androidx.annotation.NonNull; - -import com.android.systemui.Dumpable; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.shared.tracing.FrameProtoTracer; -import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams; -import com.android.systemui.shared.tracing.ProtoTraceable; -import com.android.systemui.tracing.nano.SystemUiTraceEntryProto; -import com.android.systemui.tracing.nano.SystemUiTraceFileProto; -import com.android.systemui.tracing.nano.SystemUiTraceProto; - -import com.google.protobuf.nano.MessageNano; - -import java.io.File; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Queue; - -import javax.inject.Inject; - -/** - * Controller for coordinating winscope proto tracing. - */ -@SysUISingleton -public class ProtoTracer implements - Dumpable, - ProtoTraceParams< - MessageNano, - SystemUiTraceFileProto, - SystemUiTraceEntryProto, - SystemUiTraceProto> { - - private static final String TAG = "ProtoTracer"; - private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; - - private final Context mContext; - private final FrameProtoTracer<MessageNano, SystemUiTraceFileProto, SystemUiTraceEntryProto, - SystemUiTraceProto> mProtoTracer; - - @Inject - public ProtoTracer(Context context, DumpManager dumpManager) { - mContext = context; - mProtoTracer = new FrameProtoTracer<>(this); - dumpManager.registerDumpable(this); - } - - @Override - public File getTraceFile() { - return new File(mContext.getFilesDir(), "sysui_trace.pb"); - } - - @Override - public SystemUiTraceFileProto getEncapsulatingTraceProto() { - return new SystemUiTraceFileProto(); - } - - @Override - public SystemUiTraceEntryProto updateBufferProto(SystemUiTraceEntryProto reuseObj, - ArrayList<ProtoTraceable<SystemUiTraceProto>> traceables) { - SystemUiTraceEntryProto proto = reuseObj != null - ? reuseObj - : new SystemUiTraceEntryProto(); - proto.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); - proto.systemUi = proto.systemUi != null ? proto.systemUi : new SystemUiTraceProto(); - for (ProtoTraceable t : traceables) { - t.writeToProto(proto.systemUi); - } - return proto; - } - - @Override - public byte[] serializeEncapsulatingProto(SystemUiTraceFileProto encapsulatingProto, - Queue<SystemUiTraceEntryProto> buffer) { - encapsulatingProto.magicNumber = MAGIC_NUMBER_VALUE; - encapsulatingProto.entry = buffer.toArray(new SystemUiTraceEntryProto[0]); - return MessageNano.toByteArray(encapsulatingProto); - } - - @Override - public byte[] getProtoBytes(MessageNano proto) { - return MessageNano.toByteArray(proto); - } - - @Override - public int getProtoSize(MessageNano proto) { - return proto.getCachedSize(); - } - - public void start() { - mProtoTracer.start(); - } - - public void stop() { - mProtoTracer.stop(); - } - - public boolean isEnabled() { - return mProtoTracer.isEnabled(); - } - - public void add(ProtoTraceable<SystemUiTraceProto> traceable) { - mProtoTracer.add(traceable); - } - - public void remove(ProtoTraceable<SystemUiTraceProto> traceable) { - mProtoTracer.remove(traceable); - } - - public void scheduleFrameUpdate() { - mProtoTracer.scheduleFrameUpdate(); - } - - public void update() { - mProtoTracer.update(); - } - - @Override - public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { - pw.println("ProtoTracer:"); - pw.print(" "); pw.println("enabled: " + mProtoTracer.isEnabled()); - pw.print(" "); pw.println("usagePct: " + mProtoTracer.getBufferUsagePct()); - pw.print(" "); pw.println("file: " + getTraceFile()); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto deleted file mode 100644 index d940a6b5c460..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package com.android.systemui.tracing; - -option java_multiple_files = true; - -message SystemUiTraceProto { - - optional EdgeBackGestureHandlerProto edge_back_gesture_handler = 1; -} - -message EdgeBackGestureHandlerProto { - - optional bool allow_gesture = 1; -} - -/* represents a file full of system ui trace entries. - Encoded, it should start with 0x9 0x53 0x59 0x53 0x55 0x49 0x54 0x52 0x43 (.SYSUITRC), such - that they can be easily identified. */ -message SystemUiTraceFileProto { - - /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L - (this is needed because enums have to be 32 bits and there's no nice way to put 64bit - constants into .proto files. */ - enum MagicNumber { - INVALID = 0; - MAGIC_NUMBER_L = 0x55535953; /* SYSU (little-endian ASCII) */ - MAGIC_NUMBER_H = 0x43525449; /* ITRC (little-endian ASCII) */ - } - - optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */ - repeated SystemUiTraceEntryProto entry = 2; -} - -/* one system ui trace entry. */ -message SystemUiTraceEntryProto { - /* required: elapsed realtime in nanos since boot of when this entry was logged */ - optional fixed64 elapsed_realtime_nanos = 1; - - optional SystemUiTraceProto system_ui = 3; -} diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index d9a8e0cfb53a..38226ec45910 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -45,7 +45,7 @@ import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.settings.dagger.MultiUserUtilsModule; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; import com.android.systemui.shade.ShadeController; -import com.android.systemui.shade.ShadeControllerImpl; +import com.android.systemui.shade.ShadeControllerEmptyImpl; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationListener; @@ -138,7 +138,7 @@ public abstract class TvSystemUIModule { abstract DockManager bindDockManager(DockManagerImpl dockManager); @Binds - abstract ShadeController provideShadeController(ShadeControllerImpl shadeController); + abstract ShadeController provideShadeController(ShadeControllerEmptyImpl shadeController); @SysUISingleton @Provides diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 5144d1966222..943e906a1ebc 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -51,15 +51,11 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.notetask.NoteTaskInitializer; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; -import com.android.systemui.shared.tracing.ProtoTraceable; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.tracing.ProtoTracer; -import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; -import com.android.wm.shell.nano.WmShellTraceProto; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; @@ -94,8 +90,7 @@ import javax.inject.Inject; @SysUISingleton public final class WMShell implements CoreStartable, - CommandQueue.Callbacks, - ProtoTraceable<SystemUiTraceProto> { + CommandQueue.Callbacks { private static final String TAG = WMShell.class.getName(); private static final int INVALID_SYSUI_STATE_MASK = SYSUI_STATE_DIALOG_SHOWING @@ -122,7 +117,6 @@ public final class WMShell implements private final ScreenLifecycle mScreenLifecycle; private final SysUiState mSysUiState; private final WakefulnessLifecycle mWakefulnessLifecycle; - private final ProtoTracer mProtoTracer; private final UserTracker mUserTracker; private final DisplayTracker mDisplayTracker; private final NoteTaskInitializer mNoteTaskInitializer; @@ -184,7 +178,6 @@ public final class WMShell implements KeyguardUpdateMonitor keyguardUpdateMonitor, ScreenLifecycle screenLifecycle, SysUiState sysUiState, - ProtoTracer protoTracer, WakefulnessLifecycle wakefulnessLifecycle, UserTracker userTracker, DisplayTracker displayTracker, @@ -203,7 +196,6 @@ public final class WMShell implements mOneHandedOptional = oneHandedOptional; mDesktopModeOptional = desktopMode; mWakefulnessLifecycle = wakefulnessLifecycle; - mProtoTracer = protoTracer; mUserTracker = userTracker; mDisplayTracker = displayTracker; mNoteTaskInitializer = noteTaskInitializer; @@ -223,7 +215,6 @@ public final class WMShell implements // Subscribe to user changes mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor()); - mProtoTracer.add(this); mCommandQueue.addCallback(this); mPipOptional.ifPresent(this::initPip); mSplitScreenOptional.ifPresent(this::initSplitScreen); @@ -361,12 +352,6 @@ public final class WMShell implements } @Override - public void writeToProto(SystemUiTraceProto proto) { - // Dump to WMShell proto here - // TODO: Figure out how we want to synchronize while dumping to proto - } - - @Override public void dump(PrintWriter pw, String[] args) { // Handle commands if provided if (mShell.handleCommand(args, pw)) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java index 319a02d911ed..d506584cdfac 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java @@ -33,12 +33,15 @@ import android.app.admin.IKeyguardCallback; import android.app.admin.IKeyguardClient; import android.content.ComponentName; import android.content.Intent; +import android.hardware.display.DisplayManager; +import android.os.Binder; import android.os.Handler; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.testing.ViewUtils; +import android.view.Display; import android.view.SurfaceControlViewHost; import android.view.SurfaceView; import android.view.View; @@ -50,7 +53,6 @@ import com.android.systemui.SysuiTestCase; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -60,7 +62,6 @@ import org.mockito.MockitoAnnotations; @RunWithLooper @RunWith(AndroidTestingRunner.class) @SmallTest -@Ignore("b/286245842") public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { private static final int TARGET_USER_ID = KeyguardUpdateMonitor.getCurrentUser(); @@ -79,7 +80,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { private KeyguardSecurityCallback mKeyguardCallback; @Mock private KeyguardUpdateMonitor mUpdateMonitor; - @Mock + private SurfaceControlViewHost.SurfacePackage mSurfacePackage; @Before @@ -99,6 +100,11 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { when(mKeyguardClient.queryLocalInterface(anyString())).thenReturn(mKeyguardClient); when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient); + Display display = mContext.getSystemService(DisplayManager.class).getDisplay( + Display.DEFAULT_DISPLAY); + mSurfacePackage = (new SurfaceControlViewHost(mContext, display, + new Binder())).getSurfacePackage(); + mTestController = new AdminSecondaryLockScreenController.Factory( mContext, mKeyguardSecurityContainer, mUpdateMonitor, mHandler) .create(mKeyguardCallback); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java index fa32835c2695..677d3ff3df69 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java @@ -187,9 +187,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { @Test public void testLockedOut_verifyPasswordAndUnlock_doesNotEnableViewInput() { - mKeyguardAbsKeyInputViewController.handleAttemptLockout( - SystemClock.elapsedRealtime() + 1000); - mKeyguardAbsKeyInputViewController.verifyPasswordAndUnlock(); + mKeyguardAbsKeyInputViewController.handleAttemptLockout(SystemClock.elapsedRealtime()); verify(mAbsKeyInputView).setPasswordEntryInputEnabled(false); verify(mAbsKeyInputView).setPasswordEntryEnabled(false); verify(mAbsKeyInputView, never()).setPasswordEntryInputEnabled(true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt index 9db267c2c929..d256ee163877 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt @@ -105,6 +105,7 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { `when`(keyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton) // For posture tests: `when`(keyguardPinView.buttons).thenReturn(arrayOf()) + `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) pinViewController = KeyguardPinViewController( @@ -167,7 +168,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() { `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) - `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3) `when`(passwordTextView.text).thenReturn("") @@ -182,7 +182,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() { `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) - `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6) `when`(passwordTextView.text).thenReturn("") diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt index c0c690892958..c223c5af6079 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt @@ -164,7 +164,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { testScope.runTest { val isThrottled by collectLastValue(underTest.isThrottled) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() assertThat(isThrottled).isFalse() } @@ -172,7 +172,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun authenticate_withIncorrectPin_returnsFalse() = testScope.runTest { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - assertThat(underTest.authenticate(listOf(9, 8, 7))).isFalse() + assertThat(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4))).isFalse() } @Test(expected = IllegalArgumentException::class) @@ -270,7 +270,15 @@ class AuthenticationInteractorTest : SysuiTestCase() { val isThrottled by collectLastValue(underTest.isThrottled) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setAutoConfirmEnabled(true) - assertThat(underTest.authenticate(listOf(1, 2, 3), tryAutoConfirm = true)).isNull() + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN.toMutableList().apply { + removeLast() + }, + tryAutoConfirm = true + ) + ) + .isNull() assertThat(isThrottled).isFalse() } @@ -280,7 +288,13 @@ class AuthenticationInteractorTest : SysuiTestCase() { val isUnlocked by collectLastValue(underTest.isUnlocked) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setAutoConfirmEnabled(true) - assertThat(underTest.authenticate(listOf(1, 2, 4, 4), tryAutoConfirm = true)).isFalse() + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 }, + tryAutoConfirm = true + ) + ) + .isFalse() assertThat(isUnlocked).isFalse() } @@ -290,7 +304,12 @@ class AuthenticationInteractorTest : SysuiTestCase() { val isUnlocked by collectLastValue(underTest.isUnlocked) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setAutoConfirmEnabled(true) - assertThat(underTest.authenticate(listOf(1, 2, 3, 4, 5), tryAutoConfirm = true)) + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN + listOf(7), + tryAutoConfirm = true + ) + ) .isFalse() assertThat(isUnlocked).isFalse() } @@ -301,7 +320,13 @@ class AuthenticationInteractorTest : SysuiTestCase() { val isUnlocked by collectLastValue(underTest.isUnlocked) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setAutoConfirmEnabled(true) - assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isTrue() + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN, + tryAutoConfirm = true + ) + ) + .isTrue() assertThat(isUnlocked).isTrue() } @@ -311,7 +336,13 @@ class AuthenticationInteractorTest : SysuiTestCase() { val isUnlocked by collectLastValue(underTest.isUnlocked) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setAutoConfirmEnabled(false) - assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isNull() + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN, + tryAutoConfirm = true + ) + ) + .isNull() assertThat(isUnlocked).isFalse() } @@ -334,7 +365,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { val throttling by collectLastValue(underTest.throttling) val isThrottled by collectLastValue(underTest.isThrottled) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - underTest.authenticate(listOf(1, 2, 3, 4)) + underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) assertThat(isUnlocked).isTrue() assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) @@ -366,7 +397,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) // Correct PIN, but throttled, so doesn't attempt it: - assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isNull() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isNull() assertThat(isUnlocked).isFalse() assertThat(isThrottled).isTrue() assertThat(throttling) @@ -412,9 +443,62 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) // Correct PIN and no longer throttled so unlocks successfully: - assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() assertThat(isUnlocked).isTrue() assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) } + + @Test + fun hintedPinLength_withoutAutoConfirm_isNull() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(false) + + assertThat(hintedPinLength).isNull() + } + + @Test + fun hintedPinLength_withAutoConfirmPinTooShort_isNull() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.overrideCredential( + buildList { + repeat(utils.authenticationRepository.hintedPinLength - 1) { add(it + 1) } + } + ) + utils.authenticationRepository.setAutoConfirmEnabled(true) + + assertThat(hintedPinLength).isNull() + } + + @Test + fun hintedPinLength_withAutoConfirmPinAtRightLength_isSameLength() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(true) + utils.authenticationRepository.overrideCredential( + buildList { repeat(utils.authenticationRepository.hintedPinLength) { add(it + 1) } } + ) + + assertThat(hintedPinLength).isEqualTo(utils.authenticationRepository.hintedPinLength) + } + + @Test + fun hintedPinLength_withAutoConfirmPinTooLong_isNull() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.overrideCredential( + buildList { + repeat(utils.authenticationRepository.hintedPinLength + 1) { add(it + 1) } + } + ) + utils.authenticationRepository.setAutoConfirmEnabled(true) + + assertThat(hintedPinLength).isNull() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java index 1482f291b57a..40b572934f74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java @@ -33,10 +33,10 @@ import android.provider.Settings; import androidx.test.filters.SmallTest; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; @@ -61,7 +61,6 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { private Handler mHandler; @Mock private ContentResolver mContentResolver; - private FakeFeatureFlags mFeatureFlags; @Mock private BatteryController mBatteryController; @@ -74,8 +73,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { when(mBatteryMeterView.getContext()).thenReturn(mContext); when(mBatteryMeterView.getResources()).thenReturn(mContext.getResources()); - mFeatureFlags = new FakeFeatureFlags(); - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, false); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, false); } @Test @@ -134,7 +133,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { @Test public void shieldFlagDisabled_viewNotified() { - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, false); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, false); initController(); @@ -143,7 +143,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { @Test public void shieldFlagEnabled_viewNotified() { - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, true); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, true); initController(); @@ -153,12 +154,12 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { private void initController() { mController = new BatteryMeterViewController( mBatteryMeterView, + StatusBarLocation.HOME, mUserTracker, mConfigurationController, mTunerService, mHandler, mContentResolver, - mFeatureFlags, mBatteryController ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index d022653a33c8..eaa31ac1d157 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -44,9 +44,6 @@ import android.view.View import android.view.ViewPropertyAnimator import android.view.WindowInsets import android.view.WindowManager -import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION -import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY -import android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG import android.view.WindowMetrics import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -55,9 +52,14 @@ import com.android.systemui.R import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestableContext +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl +import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractorImpl +import com.android.systemui.biometrics.shared.model.FingerprintSensorType +import com.android.systemui.biometrics.shared.model.SensorStrength +import com.android.systemui.biometrics.ui.viewmodel.SideFpsOverlayViewModel import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.dump.DumpManager @@ -99,7 +101,7 @@ private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3 @SmallTest @RoboPilotTest @RunWith(AndroidJUnit4::class) -@TestableLooper.RunWithLooper +@TestableLooper.RunWithLooper(setAsMainLooper = true) class SideFpsControllerTest : SysuiTestCase() { @JvmField @Rule var rule = MockitoJUnit.rule() @@ -118,6 +120,8 @@ class SideFpsControllerTest : SysuiTestCase() { private lateinit var keyguardBouncerRepository: FakeKeyguardBouncerRepository private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor private lateinit var displayStateInteractor: DisplayStateInteractor + private lateinit var sideFpsOverlayViewModel: SideFpsOverlayViewModel + private val fingerprintRepository = FakeFingerprintPropertyRepository() private val executor = FakeExecutor(FakeSystemClock()) private val rearDisplayStateRepository = FakeRearDisplayStateRepository() @@ -159,6 +163,15 @@ class SideFpsControllerTest : SysuiTestCase() { executor, rearDisplayStateRepository ) + sideFpsOverlayViewModel = + SideFpsOverlayViewModel(context, SideFpsOverlayInteractorImpl(fingerprintRepository)) + + fingerprintRepository.setProperties( + sensorId = 1, + strength = SensorStrength.STRONG, + sensorType = FingerprintSensorType.REAR, + sensorLocations = mapOf("" to SensorLocationInternal("", 2500, 0, 0)) + ) context.addMockSystemService(DisplayManager::class.java, displayManager) context.addMockSystemService(WindowManager::class.java, windowManager) @@ -265,6 +278,7 @@ class SideFpsControllerTest : SysuiTestCase() { executor, handler, alternateBouncerInteractor, + { sideFpsOverlayViewModel }, TestCoroutineScope(), dumpManager ) @@ -683,106 +697,6 @@ class SideFpsControllerTest : SysuiTestCase() { verify(windowManager).removeView(any()) } - /** - * {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_0, - * and uses RotateUtils.rotateBounds to map to the correct indicator location given the device - * rotation. Assuming RotationUtils.rotateBounds works correctly, tests for indicator placement - * in other rotations have been omitted. - */ - @Test - fun verifiesIndicatorPlacementForXAlignedSensor_0() = - testWithDisplay( - deviceConfig = DeviceConfig.X_ALIGNED, - isReverseDefaultRotation = false, - { rotation = Surface.ROTATION_0 } - ) { - sideFpsController.overlayOffsets = sensorLocation - - sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds) - - overlayController.show(SENSOR_ID, REASON_UNKNOWN) - executor.runAllReady() - - verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture()) - assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX) - assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0) - } - - /** - * {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_270 - * in reverse default rotation. It then uses RotateUtils.rotateBounds to map to the correct - * indicator location given the device rotation. Assuming RotationUtils.rotateBounds works - * correctly, tests for indicator placement in other rotations have been omitted. - */ - @Test - fun verifiesIndicatorPlacementForXAlignedSensor_InReverseDefaultRotation_270() = - testWithDisplay( - deviceConfig = DeviceConfig.X_ALIGNED, - isReverseDefaultRotation = true, - { rotation = Surface.ROTATION_270 } - ) { - sideFpsController.overlayOffsets = sensorLocation - - sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds) - - overlayController.show(SENSOR_ID, REASON_UNKNOWN) - executor.runAllReady() - - verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture()) - assertThat(overlayViewParamsCaptor.value.x).isEqualTo(sensorLocation.sensorLocationX) - assertThat(overlayViewParamsCaptor.value.y).isEqualTo(0) - } - - /** - * {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_0, - * and uses RotateUtils.rotateBounds to map to the correct indicator location given the device - * rotation. Assuming RotationUtils.rotateBounds works correctly, tests for indicator placement - * in other rotations have been omitted. - */ - @Test - fun verifiesIndicatorPlacementForYAlignedSensor_0() = - testWithDisplay( - deviceConfig = DeviceConfig.Y_ALIGNED, - isReverseDefaultRotation = false, - { rotation = Surface.ROTATION_0 } - ) { - sideFpsController.overlayOffsets = sensorLocation - - sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds) - - overlayController.show(SENSOR_ID, REASON_UNKNOWN) - executor.runAllReady() - - verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture()) - assertThat(overlayViewParamsCaptor.value.x).isEqualTo(displayWidth - boundsWidth) - assertThat(overlayViewParamsCaptor.value.y).isEqualTo(sensorLocation.sensorLocationY) - } - - /** - * {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_270 - * in reverse default rotation. It then uses RotateUtils.rotateBounds to map to the correct - * indicator location given the device rotation. Assuming RotationUtils.rotateBounds works - * correctly, tests for indicator placement in other rotations have been omitted. - */ - @Test - fun verifiesIndicatorPlacementForYAlignedSensor_InReverseDefaultRotation_270() = - testWithDisplay( - deviceConfig = DeviceConfig.Y_ALIGNED, - isReverseDefaultRotation = true, - { rotation = Surface.ROTATION_270 } - ) { - sideFpsController.overlayOffsets = sensorLocation - - sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds) - - overlayController.show(SENSOR_ID, REASON_UNKNOWN) - executor.runAllReady() - - verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture()) - assertThat(overlayViewParamsCaptor.value.x).isEqualTo(displayWidth - boundsWidth) - assertThat(overlayViewParamsCaptor.value.y).isEqualTo(sensorLocation.sensorLocationY) - } - @Test fun hasSideFpsSensor_withSensorProps_returnsTrue() = testWithDisplay { // By default all those tests assume the side fps sensor is available. @@ -795,51 +709,6 @@ class SideFpsControllerTest : SysuiTestCase() { assertThat(fingerprintManager.hasSideFpsSensor()).isFalse() } - - @Test - fun testLayoutParams_isKeyguardDialogType() = - testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED) { - sideFpsController.overlayOffsets = sensorLocation - sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds) - overlayController.show(SENSOR_ID, REASON_UNKNOWN) - executor.runAllReady() - - verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture()) - - val lpType = overlayViewParamsCaptor.value.type - - assertThat((lpType and TYPE_KEYGUARD_DIALOG) != 0).isTrue() - } - - @Test - fun testLayoutParams_hasNoMoveAnimationWindowFlag() = - testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED) { - sideFpsController.overlayOffsets = sensorLocation - sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds) - overlayController.show(SENSOR_ID, REASON_UNKNOWN) - executor.runAllReady() - - verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture()) - - val lpFlags = overlayViewParamsCaptor.value.privateFlags - - assertThat((lpFlags and PRIVATE_FLAG_NO_MOVE_ANIMATION) != 0).isTrue() - } - - @Test - fun testLayoutParams_hasTrustedOverlayWindowFlag() = - testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED) { - sideFpsController.overlayOffsets = sensorLocation - sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds) - overlayController.show(SENSOR_ID, REASON_UNKNOWN) - executor.runAllReady() - - verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture()) - - val lpFlags = overlayViewParamsCaptor.value.privateFlags - - assertThat((lpFlags and PRIVATE_FLAG_TRUSTED_OVERLAY) != 0).isTrue() - } } private fun insetsForSmallNavbar() = insetsWithBottom(60) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt index 239e317b92f5..ea2561594793 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt @@ -73,10 +73,15 @@ class FingerprintRepositoryImplTest : SysuiTestCase() { @Test fun initializeProperties() = testScope.runTest { - val isInitialized = collectLastValue(repository.isInitialized) + val sensorId by collectLastValue(repository.sensorId) + val strength by collectLastValue(repository.strength) + val sensorType by collectLastValue(repository.sensorType) + val sensorLocations by collectLastValue(repository.sensorLocations) - assertDefaultProperties() - assertThat(isInitialized()).isFalse() + // Assert default properties. + assertThat(sensorId).isEqualTo(-1) + assertThat(strength).isEqualTo(SensorStrength.CONVENIENCE) + assertThat(sensorType).isEqualTo(FingerprintSensorType.UNKNOWN) val fingerprintProps = listOf( @@ -115,31 +120,24 @@ class FingerprintRepositoryImplTest : SysuiTestCase() { fingerprintAuthenticatorsCaptor.value.onAllAuthenticatorsRegistered(fingerprintProps) - assertThat(repository.sensorId.value).isEqualTo(1) - assertThat(repository.strength.value).isEqualTo(SensorStrength.STRONG) - assertThat(repository.sensorType.value).isEqualTo(FingerprintSensorType.REAR) + assertThat(sensorId).isEqualTo(1) + assertThat(strength).isEqualTo(SensorStrength.STRONG) + assertThat(sensorType).isEqualTo(FingerprintSensorType.REAR) - assertThat(repository.sensorLocations.value.size).isEqualTo(2) - assertThat(repository.sensorLocations.value).containsKey("display_id_1") - with(repository.sensorLocations.value["display_id_1"]!!) { + assertThat(sensorLocations?.size).isEqualTo(2) + assertThat(sensorLocations).containsKey("display_id_1") + with(sensorLocations?.get("display_id_1")!!) { assertThat(displayId).isEqualTo("display_id_1") assertThat(sensorLocationX).isEqualTo(100) assertThat(sensorLocationY).isEqualTo(300) assertThat(sensorRadius).isEqualTo(20) } - assertThat(repository.sensorLocations.value).containsKey("") - with(repository.sensorLocations.value[""]!!) { + assertThat(sensorLocations).containsKey("") + with(sensorLocations?.get("")!!) { assertThat(displayId).isEqualTo("") assertThat(sensorLocationX).isEqualTo(540) assertThat(sensorLocationY).isEqualTo(1636) assertThat(sensorRadius).isEqualTo(130) } - assertThat(isInitialized()).isTrue() } - - private fun assertDefaultProperties() { - assertThat(repository.sensorId.value).isEqualTo(-1) - assertThat(repository.strength.value).isEqualTo(SensorStrength.CONVENIENCE) - assertThat(repository.sensorType.value).isEqualTo(FingerprintSensorType.UNKNOWN) - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt index fd96cf45504b..896f9b114679 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt @@ -22,6 +22,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength +import com.android.systemui.coroutines.collectLastValue import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope @@ -51,8 +52,9 @@ class SideFpsOverlayInteractorTest : SysuiTestCase() { } @Test - fun testGetOverlayOffsets() = + fun testGetOverlayoffsets() = testScope.runTest { + // Arrange. fingerprintRepository.setProperties( sensorId = 1, strength = SensorStrength.STRONG, @@ -76,16 +78,33 @@ class SideFpsOverlayInteractorTest : SysuiTestCase() { ) ) - var offsets = interactor.getOverlayOffsets("display_id_1") - assertThat(offsets.displayId).isEqualTo("display_id_1") - assertThat(offsets.sensorLocationX).isEqualTo(100) - assertThat(offsets.sensorLocationY).isEqualTo(300) - assertThat(offsets.sensorRadius).isEqualTo(20) + // Act. + val offsets by collectLastValue(interactor.overlayOffsets) + val displayId by collectLastValue(interactor.displayId) - offsets = interactor.getOverlayOffsets("invalid_display_id") - assertThat(offsets.displayId).isEqualTo("") - assertThat(offsets.sensorLocationX).isEqualTo(540) - assertThat(offsets.sensorLocationY).isEqualTo(1636) - assertThat(offsets.sensorRadius).isEqualTo(130) + // Assert offsets of empty displayId. + assertThat(displayId).isEqualTo("") + assertThat(offsets?.displayId).isEqualTo("") + assertThat(offsets?.sensorLocationX).isEqualTo(540) + assertThat(offsets?.sensorLocationY).isEqualTo(1636) + assertThat(offsets?.sensorRadius).isEqualTo(130) + + // Offsets should be updated correctly. + interactor.changeDisplay("display_id_1") + assertThat(displayId).isEqualTo("display_id_1") + assertThat(offsets?.displayId).isEqualTo("display_id_1") + assertThat(offsets?.sensorLocationX).isEqualTo(100) + assertThat(offsets?.sensorLocationY).isEqualTo(300) + assertThat(offsets?.sensorRadius).isEqualTo(20) + + // Should return default offset when the displayId is invalid. + interactor.changeDisplay("invalid_display_id") + assertThat(displayId).isEqualTo("invalid_display_id") + assertThat(offsets?.displayId).isEqualTo(SensorLocationInternal.DEFAULT.displayId) + assertThat(offsets?.sensorLocationX) + .isEqualTo(SensorLocationInternal.DEFAULT.sensorLocationX) + assertThat(offsets?.sensorLocationY) + .isEqualTo(SensorLocationInternal.DEFAULT.sensorLocationY) + assertThat(offsets?.sensorRadius).isEqualTo(SensorLocationInternal.DEFAULT.sensorRadius) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt new file mode 100644 index 000000000000..a8593216e22a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics.ui.viewmodel + +import android.graphics.Rect +import android.hardware.biometrics.SensorLocationInternal +import android.hardware.display.DisplayManagerGlobal +import android.view.Display +import android.view.DisplayAdjustments +import android.view.DisplayInfo +import android.view.Surface +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.SysuiTestableContext +import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository +import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor +import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractorImpl +import com.android.systemui.biometrics.shared.model.FingerprintSensorType +import com.android.systemui.biometrics.shared.model.SensorStrength +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers +import org.mockito.Mockito +import org.mockito.junit.MockitoJUnit + +private const val DISPLAY_ID = 2 + +@SmallTest +@RunWith(JUnit4::class) +class SideFpsOverlayViewModelTest : SysuiTestCase() { + + @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + private var testScope: TestScope = TestScope(StandardTestDispatcher()) + + private val fingerprintRepository = FakeFingerprintPropertyRepository() + private lateinit var interactor: SideFpsOverlayInteractor + private lateinit var viewModel: SideFpsOverlayViewModel + + enum class DeviceConfig { + X_ALIGNED, + Y_ALIGNED, + } + + private lateinit var deviceConfig: DeviceConfig + private lateinit var indicatorBounds: Rect + private lateinit var displayBounds: Rect + private lateinit var sensorLocation: SensorLocationInternal + private var displayWidth: Int = 0 + private var displayHeight: Int = 0 + private var boundsWidth: Int = 0 + private var boundsHeight: Int = 0 + + @Before + fun setup() { + interactor = SideFpsOverlayInteractorImpl(fingerprintRepository) + + fingerprintRepository.setProperties( + sensorId = 1, + strength = SensorStrength.STRONG, + sensorType = FingerprintSensorType.REAR, + sensorLocations = + mapOf( + "" to + SensorLocationInternal( + "" /* displayId */, + 540 /* sensorLocationX */, + 1636 /* sensorLocationY */, + 130 /* sensorRadius */ + ), + "display_id_1" to + SensorLocationInternal( + "display_id_1" /* displayId */, + 100 /* sensorLocationX */, + 300 /* sensorLocationY */, + 20 /* sensorRadius */ + ) + ) + ) + } + + @Test + fun testOverlayOffsets() = + testScope.runTest { + viewModel = SideFpsOverlayViewModel(mContext, interactor) + + val interactorOffsets by collectLastValue(interactor.overlayOffsets) + val viewModelOffsets by collectLastValue(viewModel.overlayOffsets) + + assertThat(viewModelOffsets).isEqualTo(interactorOffsets) + } + + private fun testWithDisplay( + deviceConfig: DeviceConfig = DeviceConfig.X_ALIGNED, + isReverseDefaultRotation: Boolean = false, + initInfo: DisplayInfo.() -> Unit = {}, + block: () -> Unit + ) { + this.deviceConfig = deviceConfig + + when (deviceConfig) { + DeviceConfig.X_ALIGNED -> { + displayWidth = 3000 + displayHeight = 1500 + sensorLocation = SensorLocationInternal("", 2500, 0, 0) + boundsWidth = 200 + boundsHeight = 100 + } + DeviceConfig.Y_ALIGNED -> { + displayWidth = 2500 + displayHeight = 2000 + sensorLocation = SensorLocationInternal("", 0, 300, 0) + boundsWidth = 100 + boundsHeight = 200 + } + } + + indicatorBounds = Rect(0, 0, boundsWidth, boundsHeight) + displayBounds = Rect(0, 0, displayWidth, displayHeight) + + val displayInfo = DisplayInfo() + displayInfo.initInfo() + + val dmGlobal = Mockito.mock(DisplayManagerGlobal::class.java) + val display = + Display( + dmGlobal, + DISPLAY_ID, + displayInfo, + DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS + ) + + whenever(dmGlobal.getDisplayInfo(ArgumentMatchers.eq(DISPLAY_ID))).thenReturn(displayInfo) + + val sideFpsOverlayViewModelContext = + context.createDisplayContext(display) as SysuiTestableContext + sideFpsOverlayViewModelContext.orCreateTestableResources.addOverride( + com.android.internal.R.bool.config_reverseDefaultRotation, + isReverseDefaultRotation + ) + viewModel = SideFpsOverlayViewModel(sideFpsOverlayViewModelContext, interactor) + + block() + } + + /** + * {@link SideFpsOverlayViewModel#updateSensorBounds} calculates indicator placement for + * ROTATION_0, and uses RotateUtils.rotateBounds to map to the correct indicator location given + * the device rotation. Assuming RotationUtils.rotateBounds works correctly, tests for indicator + * placement in other rotations have been omitted. + */ + @Test + fun verifiesIndicatorPlacementForXAlignedSensor_0() = + testScope.runTest { + testWithDisplay( + deviceConfig = DeviceConfig.X_ALIGNED, + isReverseDefaultRotation = false, + { rotation = Surface.ROTATION_0 } + ) { + viewModel.updateSensorBounds(indicatorBounds, displayBounds, sensorLocation) + + val displayInfo: DisplayInfo = DisplayInfo() + context.display!!.getDisplayInfo(displayInfo) + assertThat(displayInfo.rotation).isEqualTo(Surface.ROTATION_0) + + assertThat(viewModel.sensorBounds.value).isNotNull() + assertThat(viewModel.sensorBounds.value.left) + .isEqualTo(sensorLocation.sensorLocationX) + assertThat(viewModel.sensorBounds.value.top).isEqualTo(0) + } + } + + /** + * {@link SideFpsOverlayViewModel#updateSensorBounds} calculates indicator placement for + * ROTATION_270 in reverse default rotation. It then uses RotateUtils.rotateBounds to map to the + * correct indicator location given the device rotation. Assuming RotationUtils.rotateBounds + * works correctly, tests for indicator placement in other rotations have been omitted. + */ + @Test + fun verifiesIndicatorPlacementForXAlignedSensor_InReverseDefaultRotation_270() = + testScope.runTest { + testWithDisplay( + deviceConfig = DeviceConfig.X_ALIGNED, + isReverseDefaultRotation = true, + { rotation = Surface.ROTATION_270 } + ) { + viewModel.updateSensorBounds(indicatorBounds, displayBounds, sensorLocation) + + assertThat(viewModel.sensorBounds.value).isNotNull() + assertThat(viewModel.sensorBounds.value.left) + .isEqualTo(sensorLocation.sensorLocationX) + assertThat(viewModel.sensorBounds.value.top).isEqualTo(0) + } + } + + /** + * {@link SideFpsOverlayViewModel#updateSensorBounds} calculates indicator placement for + * ROTATION_0, and uses RotateUtils.rotateBounds to map to the correct indicator location given + * the device rotation. Assuming RotationUtils.rotateBounds works correctly, tests for indicator + * placement in other rotations have been omitted. + */ + @Test + fun verifiesIndicatorPlacementForYAlignedSensor_0() = + testScope.runTest { + testWithDisplay( + deviceConfig = DeviceConfig.Y_ALIGNED, + isReverseDefaultRotation = false, + { rotation = Surface.ROTATION_0 } + ) { + viewModel.updateSensorBounds(indicatorBounds, displayBounds, sensorLocation) + + assertThat(viewModel.sensorBounds.value).isNotNull() + assertThat(viewModel.sensorBounds.value.left).isEqualTo(displayWidth - boundsWidth) + assertThat(viewModel.sensorBounds.value.top) + .isEqualTo(sensorLocation.sensorLocationY) + } + } + + /** + * {@link SideFpsOverlayViewModel#updateSensorBounds} calculates indicator placement for + * ROTATION_270 in reverse default rotation. It then uses RotateUtils.rotateBounds to map to the + * correct indicator location given the device rotation. Assuming RotationUtils.rotateBounds + * works correctly, tests for indicator placement in other rotations have been omitted. + */ + @Test + fun verifiesIndicatorPlacementForYAlignedSensor_InReverseDefaultRotation_270() = + testScope.runTest { + testWithDisplay( + deviceConfig = DeviceConfig.Y_ALIGNED, + isReverseDefaultRotation = true, + { rotation = Surface.ROTATION_270 } + ) { + viewModel.updateSensorBounds(indicatorBounds, displayBounds, sensorLocation) + + assertThat(viewModel.sensorBounds.value).isNotNull() + assertThat(viewModel.sensorBounds.value.left).isEqualTo(displayWidth - boundsWidth) + assertThat(viewModel.sensorBounds.value.top) + .isEqualTo(sensorLocation.sensorLocationY) + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index 481f36e8ea38..6babf0490ea9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -94,7 +94,7 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) // Correct input. - assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @@ -118,13 +118,20 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - // Wrong 4-digit pin - assertThat(underTest.authenticate(listOf(1, 2, 3, 5), tryAutoConfirm = true)).isFalse() + // Wrong 6-digit pin + assertThat(underTest.authenticate(listOf(1, 2, 3, 5, 5, 6), tryAutoConfirm = true)) + .isFalse() assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) // Correct input. - assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isTrue() + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN, + tryAutoConfirm = true + ) + ) + .isTrue() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @@ -147,7 +154,13 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) // Correct input. - assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isNull() + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN, + tryAutoConfirm = true + ) + ) + .isNull() assertThat(message).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @@ -309,7 +322,7 @@ class BouncerInteractorTest : SysuiTestCase() { ) // Correct PIN, but throttled, so doesn't change away from the bouncer scene: - assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isNull() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isNull() assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer) assertTryAgainMessage( message, @@ -339,7 +352,7 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer) // Correct PIN and no longer throttled so changes to the Gone scene: - assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() assertThat(currentScene?.key).isEqualTo(SceneKey.Gone) assertThat(isThrottled).isFalse() assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt index 0867558545bd..2cc949326fa0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils @@ -60,10 +61,9 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { assertThat(animateFailure).isFalse() // Wrong PIN: - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(4) - underTest.onPinButtonClicked(5) - underTest.onPinButtonClicked(6) + FakeAuthenticationRepository.DEFAULT_PIN.drop(2).forEach { digit -> + underTest.onPinButtonClicked(digit) + } underTest.onAuthenticateButtonClicked() assertThat(animateFailure).isTrue() @@ -71,10 +71,9 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { assertThat(animateFailure).isFalse() // Correct PIN: - underTest.onPinButtonClicked(1) - underTest.onPinButtonClicked(2) - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(4) + FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> + underTest.onPinButtonClicked(digit) + } underTest.onAuthenticateButtonClicked() assertThat(animateFailure).isFalse() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 5c6d4c69b50b..45d1af722369 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils @@ -211,10 +212,9 @@ class PinBouncerViewModelTest : SysuiTestCase() { ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() - underTest.onPinButtonClicked(1) - underTest.onPinButtonClicked(2) - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(4) + FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> + underTest.onPinButtonClicked(digit) + } underTest.onAuthenticateButtonClicked() @@ -275,10 +275,9 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) // Enter the correct PIN: - underTest.onPinButtonClicked(1) - underTest.onPinButtonClicked(2) - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(4) + FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> + underTest.onPinButtonClicked(digit) + } assertThat(message?.text).isEmpty() underTest.onAuthenticateButtonClicked() @@ -300,10 +299,9 @@ class PinBouncerViewModelTest : SysuiTestCase() { ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() - underTest.onPinButtonClicked(1) - underTest.onPinButtonClicked(2) - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(4) + FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> + underTest.onPinButtonClicked(digit) + } assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @@ -324,10 +322,12 @@ class PinBouncerViewModelTest : SysuiTestCase() { ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() - underTest.onPinButtonClicked(1) - underTest.onPinButtonClicked(2) - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(5) // PIN is now wrong! + FakeAuthenticationRepository.DEFAULT_PIN.dropLast(1).forEach { digit -> + underTest.onPinButtonClicked(digit) + } + underTest.onPinButtonClicked( + FakeAuthenticationRepository.DEFAULT_PIN.last() + 1 + ) // PIN is now wrong! assertThat(entries).hasSize(0) assertThat(message?.text).isEqualTo(WRONG_PIN) @@ -386,47 +386,6 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden) } - @Test - fun hintedPinLength_withoutAutoConfirm_isNull() = - testScope.runTest { - val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setAutoConfirmEnabled(false) - - assertThat(hintedPinLength).isNull() - } - - @Test - fun hintedPinLength_withAutoConfirmPinLessThanSixDigits_isNull() = - testScope.runTest { - val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setAutoConfirmEnabled(true) - - assertThat(hintedPinLength).isNull() - } - - @Test - fun hintedPinLength_withAutoConfirmPinExactlySixDigits_isSix() = - testScope.runTest { - val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setAutoConfirmEnabled(true) - utils.authenticationRepository.overrideCredential(listOf(1, 2, 3, 4, 5, 6)) - - assertThat(hintedPinLength).isEqualTo(6) - } - - @Test - fun hintedPinLength_withAutoConfirmPinMoreThanSixDigits_isNull() = - testScope.runTest { - val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - utils.authenticationRepository.setAutoConfirmEnabled(true) - - assertThat(hintedPinLength).isNull() - } - companion object { private const val ENTER_YOUR_PIN = "Enter your pin" private const val WRONG_PIN = "Wrong pin" diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt index 023ed061c642..45bb9313264c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt @@ -21,6 +21,10 @@ import android.os.PowerManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.statusbar.phone.ScreenOffAnimationController @@ -44,6 +48,7 @@ class PowerInteractorTest : SysuiTestCase() { private lateinit var underTest: PowerInteractor private lateinit var repository: FakePowerRepository + private val keyguardRepository = FakeKeyguardRepository() @Mock private lateinit var falsingCollector: FalsingCollector @Mock private lateinit var screenOffAnimationController: ScreenOffAnimationController @Mock private lateinit var statusBarStateController: StatusBarStateController @@ -59,6 +64,7 @@ class PowerInteractorTest : SysuiTestCase() { underTest = PowerInteractor( repository, + keyguardRepository, falsingCollector, screenOffAnimationController, statusBarStateController, @@ -125,6 +131,57 @@ class PowerInteractorTest : SysuiTestCase() { verify(falsingCollector).onScreenOnFromTouch() } + @Test + fun wakeUpForFullScreenIntent_notGoingToSleepAndNotDozing_notWoken() { + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + whenever(statusBarStateController.isDozing).thenReturn(false) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNull() + assertThat(repository.lastWakeReason).isNull() + } + + @Test + fun wakeUpForFullScreenIntent_startingToSleep_woken() { + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + whenever(statusBarStateController.isDozing).thenReturn(false) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNotNull() + assertThat(repository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_APPLICATION) + } + + @Test + fun wakeUpForFullScreenIntent_dozing_woken() { + whenever(statusBarStateController.isDozing).thenReturn(true) + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNotNull() + assertThat(repository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_APPLICATION) + } + companion object { private val IMMEDIATE = Dispatchers.Main.immediate } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt index a4fab1dbac57..77a22ac9b092 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.FakePowerRepository @@ -89,6 +90,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { dockManager, PowerInteractor( powerRepository, + FakeKeyguardRepository(), falsingCollector, screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt index 729c4a9145c2..52e0c9c9936b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt @@ -78,11 +78,11 @@ class ShadeControllerImplTest : SysuiTestCase() { deviceProvisionedController, notificationShadeWindowController, windowManager, + Lazy { shadeViewController }, Lazy { assistManager }, Lazy { gutsManager }, ) shadeController.setNotificationShadeWindowViewController(nswvc) - shadeController.setShadeViewController(shadeViewController) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt index f542ab099517..bf25f2975253 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt @@ -29,6 +29,7 @@ import android.view.DisplayCutout import android.view.View import android.view.ViewPropertyAnimator import android.view.WindowInsets +import android.widget.LinearLayout import android.widget.TextView import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.widget.ConstraintSet @@ -127,6 +128,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { var viewVisibility = View.GONE var viewAlpha = 1f + private val systemIcons = LinearLayout(context) private lateinit var shadeHeaderController: ShadeHeaderController private lateinit var carrierIconSlots: List<String> private val configurationController = FakeConfigurationController() @@ -146,6 +148,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { .thenReturn(batteryMeterView) whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons) + whenever<View>(view.findViewById(R.id.shade_header_system_icons)).thenReturn(systemIcons) viewContext = Mockito.spy(context) whenever(view.context).thenReturn(viewContext) @@ -451,6 +454,17 @@ class ShadeHeaderControllerTest : SysuiTestCase() { } @Test + fun testLargeScreenActive_collapseActionRun_onSystemIconsClick() { + shadeHeaderController.largeScreenActive = true + var wasRun = false + shadeHeaderController.shadeCollapseAction = Runnable { wasRun = true } + + systemIcons.performClick() + + assertThat(wasRun).isTrue() + } + + @Test fun testShadeExpandedFraction() { // View needs to be visible for this to actually take effect shadeHeaderController.qsVisible = true diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index ff2f1065049b..4a2518ae6f7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -101,16 +101,18 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Mock lateinit var activityStarter: ActivityStarter @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback private val disableFlagsRepository = FakeDisableFlagsRepository() + private val keyguardRepository = FakeKeyguardRepository() private val shadeInteractor = ShadeInteractor( testScope.backgroundScope, disableFlagsRepository, - keyguardRepository = FakeKeyguardRepository(), + keyguardRepository, userSetupRepository = FakeUserSetupRepository(), deviceProvisionedController = mock(), userInteractor = mock(), ) private val powerInteractor = PowerInteractor( FakePowerRepository(), + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController = mock(), statusBarStateController = mock(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt index 89faa239c5a2..a56fb2c515a8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt @@ -3,7 +3,11 @@ package com.android.systemui.statusbar.notification import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -15,8 +19,9 @@ import org.mockito.Mockito.verify @SmallTest @RunWith(JUnit4::class) class RoundableTest : SysuiTestCase() { - val targetView: View = mock() - val roundable = FakeRoundable(targetView) + private val targetView: View = mock() + private val featureFlags = FakeFeatureFlags() + private val roundable = FakeRoundable(targetView = targetView, featureFlags = featureFlags) @Test fun defaultConfig_shouldNotHaveRoundedCorner() { @@ -144,16 +149,62 @@ class RoundableTest : SysuiTestCase() { assertEquals(0.2f, roundable.roundableState.bottomRoundness) } + @Test + fun getCornerRadii_radius_maxed_to_height() { + whenever(targetView.height).thenReturn(10) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 1f, SOURCE1) + + assertCornerRadiiEquals(5f, 5f) + } + + @Test + fun getCornerRadii_topRadius_maxed_to_height() { + whenever(targetView.height).thenReturn(5) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 0f, SOURCE1) + + assertCornerRadiiEquals(5f, 0f) + } + + @Test + fun getCornerRadii_bottomRadius_maxed_to_height() { + whenever(targetView.height).thenReturn(5) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(0f, 1f, SOURCE1) + + assertCornerRadiiEquals(0f, 5f) + } + + @Test + fun getCornerRadii_radii_kept() { + whenever(targetView.height).thenReturn(100) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 1f, SOURCE1) + + assertCornerRadiiEquals(MAX_RADIUS, MAX_RADIUS) + } + + private fun assertCornerRadiiEquals(top: Float, bottom: Float) { + assertEquals("topCornerRadius", top, roundable.topCornerRadius) + assertEquals("bottomCornerRadius", bottom, roundable.bottomCornerRadius) + } + class FakeRoundable( targetView: View, radius: Float = MAX_RADIUS, + featureFlags: FeatureFlags ) : Roundable { override val roundableState = RoundableState( targetView = targetView, roundable = this, maxRadius = radius, + featureFlags = featureFlags ) + + override val clipHeight: Int + get() = roundableState.targetView.height } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index d3e5816cd1d4..daa45db6b90c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -426,23 +426,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } @Test - public void testShouldHeadsUp_oldWhen_flagDisabled() throws Exception { - ensureStateForHeadsUpWhenAwake(); - when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(false); - - NotificationEntry entry = createNotification(IMPORTANCE_HIGH); - entry.getSbn().getNotification().when = makeWhenHoursAgo(25); - - assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue(); - - verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong()); - verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any()); - } - - @Test public void testShouldHeadsUp_oldWhen_whenNow() throws Exception { ensureStateForHeadsUpWhenAwake(); - when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); @@ -455,7 +440,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { @Test public void testShouldHeadsUp_oldWhen_whenRecent() throws Exception { ensureStateForHeadsUpWhenAwake(); - when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); entry.getSbn().getNotification().when = makeWhenHoursAgo(13); @@ -469,7 +453,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { @Test public void testShouldHeadsUp_oldWhen_whenZero() throws Exception { ensureStateForHeadsUpWhenAwake(); - when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); entry.getSbn().getNotification().when = 0L; @@ -484,7 +467,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { @Test public void testShouldHeadsUp_oldWhen_whenNegative() throws Exception { ensureStateForHeadsUpWhenAwake(); - when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); entry.getSbn().getNotification().when = -1L; @@ -498,7 +480,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { @Test public void testShouldHeadsUp_oldWhen_hasFullScreenIntent() throws Exception { ensureStateForHeadsUpWhenAwake(); - when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true); long when = makeWhenHoursAgo(25); NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silent= */ false); @@ -514,7 +495,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { @Test public void testShouldHeadsUp_oldWhen_isForegroundService() throws Exception { ensureStateForHeadsUpWhenAwake(); - when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true); long when = makeWhenHoursAgo(25); NotificationEntry entry = createFgsNotification(IMPORTANCE_HIGH); @@ -530,7 +510,6 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { @Test public void testShouldNotHeadsUp_oldWhen() throws Exception { ensureStateForHeadsUpWhenAwake(); - when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true); long when = makeWhenHoursAgo(25); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt index a87dd2d3d670..8881f42783fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt @@ -58,6 +58,7 @@ class NotificationShelfInteractorTest : SysuiTestCase() { private val powerInteractor = PowerInteractor( powerRepository, + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt index 7ae150231b98..6221f3e89ad6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt @@ -69,6 +69,7 @@ class NotificationShelfViewModelTest : SysuiTestCase() { private val powerInteractor by lazy { PowerInteractor( powerRepository, + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt index 442ba0977cf6..5e0e140563cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt @@ -95,6 +95,7 @@ class ActivityStarterImplTest : SysuiTestCase() { Lazy { notifShadeWindowController }, activityLaunchAnimator, context, + DISPLAY_ID, lockScreenUserManager, statusBarWindowController, wakefulnessLifecycle, @@ -274,4 +275,8 @@ class ActivityStarterImplTest : SysuiTestCase() { mainExecutor.runAllReady() verify(statusBarStateController).setLeaveOpenOnKeyguardHide(true) } + + private companion object { + private const val DISPLAY_ID = 0 + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 88d8dfc50b47..3d35233ad646 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -447,10 +447,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mDeviceProvisionedController, mNotificationShadeWindowController, mContext.getSystemService(WindowManager.class), + () -> mNotificationPanelViewController, () -> mAssistManager, () -> mNotificationGutsManager )); - mShadeController.setShadeViewController(mNotificationPanelViewController); mShadeController.setNotificationShadeWindowViewController( mNotificationShadeWindowViewController); mShadeController.setNotificationPresenter(mNotificationPresenter); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java index 6a4b3c55f244..df3c1e50786b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java @@ -22,7 +22,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARE import static junit.framework.Assert.assertTrue; -import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -45,8 +44,6 @@ import com.android.internal.util.ContrastColorUtil; import com.android.internal.view.AppearanceRegion; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.statusbar.policy.BatteryController; @@ -65,20 +62,13 @@ public class LightBarControllerTest extends SysuiTestCase { private static final GradientColors COLORS_LIGHT = makeColors(Color.WHITE); private static final GradientColors COLORS_DARK = makeColors(Color.BLACK); - private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); private LightBarTransitionsController mLightBarTransitionsController; private LightBarTransitionsController mNavBarController; private SysuiDarkIconDispatcher mStatusBarIconController; private LightBarController mLightBarController; - /** Allow testing with NEW_LIGHT_BAR_LOGIC flag in different states */ - protected boolean testNewLightBarLogic() { - return false; - } - @Before public void setup() { - mFeatureFlags.set(Flags.NEW_LIGHT_BAR_LOGIC, testNewLightBarLogic()); mStatusBarIconController = mock(SysuiDarkIconDispatcher.class); mNavBarController = mock(LightBarTransitionsController.class); when(mNavBarController.supportsIconTintForNavMode(anyInt())).thenReturn(true); @@ -90,7 +80,6 @@ public class LightBarControllerTest extends SysuiTestCase { mStatusBarIconController, mock(BatteryController.class), mock(NavigationModeController.class), - mFeatureFlags, mock(DumpManager.class), new FakeDisplayTracker(mContext)); } @@ -211,8 +200,6 @@ public class LightBarControllerTest extends SysuiTestCase { @Test public void validateNavBarChangesUpdateIcons() { - assumeTrue(testNewLightBarLogic()); // Only run in the new suite - // On the launcher in dark mode buttons are light mLightBarController.setScrimState(ScrimState.UNLOCKED, 0f, COLORS_DARK); mLightBarController.onNavigationBarAppearanceChanged( @@ -251,8 +238,6 @@ public class LightBarControllerTest extends SysuiTestCase { @Test public void navBarHasDarkIconsInLockedShade_lightMode() { - assumeTrue(testNewLightBarLogic()); // Only run in the new suite - // On the locked shade QS in light mode buttons are light mLightBarController.setScrimState(ScrimState.SHADE_LOCKED, 1f, COLORS_LIGHT); mLightBarController.onNavigationBarAppearanceChanged( @@ -287,8 +272,6 @@ public class LightBarControllerTest extends SysuiTestCase { @Test public void navBarHasLightIconsInLockedShade_darkMode() { - assumeTrue(testNewLightBarLogic()); // Only run in the new suite - // On the locked shade QS in light mode buttons are light mLightBarController.setScrimState(ScrimState.SHADE_LOCKED, 1f, COLORS_DARK); mLightBarController.onNavigationBarAppearanceChanged( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt deleted file mode 100644 index d9c2cfa5ea7f..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerWithNewLogicTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.statusbar.phone - -import androidx.test.filters.SmallTest -import com.android.systemui.flags.Flags.NEW_LIGHT_BAR_LOGIC - -/** - * This file only needs to live as long as [NEW_LIGHT_BAR_LOGIC] does. When we delete that flag, we - * can roll this back into the old test. - */ -@SmallTest -class LightBarControllerWithNewLogicTest : LightBarControllerTest() { - override fun testNewLightBarLogic(): Boolean = true -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index bbc75c9eeae8..33c77cc3e250 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -64,7 +64,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.dock.DockManager; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.KeyguardState; @@ -142,7 +141,6 @@ public class ScrimControllerTest extends SysuiTestCase { // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The // event-dispatch-on-registration pattern caused some of these unit tests to fail.) @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - @Mock private FeatureFlags mFeatureFlags; private static class AnimatorListener implements Animator.AnimatorListener { private int mNumStarts; @@ -280,8 +278,7 @@ public class ScrimControllerTest extends SysuiTestCase { mPrimaryBouncerToGoneTransitionViewModel, mKeyguardTransitionInteractor, mMainDispatcher, - mLinearLargeScreenShadeInterpolator, - mFeatureFlags); + mLinearLargeScreenShadeInterpolator); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); @@ -974,8 +971,7 @@ public class ScrimControllerTest extends SysuiTestCase { mPrimaryBouncerToGoneTransitionViewModel, mKeyguardTransitionInteractor, mMainDispatcher, - mLinearLargeScreenShadeInterpolator, - mFeatureFlags); + mLinearLargeScreenShadeInterpolator); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java index 8aaa57ffe2cb..9157cd9e4f43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; -import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import static junit.framework.Assert.assertTrue; @@ -41,13 +40,11 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter; @@ -156,13 +153,9 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { assertTrue("Expected StatusBarIconView", (manager.getViewAt(0) instanceof StatusBarIconView)); - holder = holderForType(TYPE_WIFI); - manager.onIconAdded(1, "test_wifi", false, holder); - assertTrue(manager.getViewAt(1) instanceof StatusBarWifiView); - holder = holderForType(TYPE_MOBILE); - manager.onIconAdded(2, "test_mobile", false, holder); - assertTrue(manager.getViewAt(2) instanceof StatusBarMobileView); + manager.onIconAdded(1, "test_mobile", false, holder); + assertTrue(manager.getViewAt(1) instanceof StatusBarMobileView); } private StatusBarIconHolder holderForType(int type) { @@ -170,9 +163,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { case TYPE_MOBILE: return StatusBarIconHolder.fromMobileIconState(mock(MobileIconState.class)); - case TYPE_WIFI: - return StatusBarIconHolder.fromWifiIconState(mock(WifiIconState.class)); - case TYPE_ICON: default: return StatusBarIconHolder.fromIcon(mock(StatusBarIcon.class)); @@ -214,13 +204,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { } @Override - protected StatusBarWifiView addWifiIcon(int index, String slot, WifiIconState state) { - StatusBarWifiView mock = mock(StatusBarWifiView.class); - mGroup.addView(mock, index); - return mock; - } - - @Override protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { StatusBarMobileView mock = mock(StatusBarMobileView.class); mGroup.addView(mock, index); @@ -254,13 +237,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { } @Override - protected StatusBarWifiView addWifiIcon(int index, String slot, WifiIconState state) { - StatusBarWifiView mock = mock(StatusBarWifiView.class); - mGroup.addView(mock, index); - return mock; - } - - @Override protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { StatusBarMobileView mock = mock(StatusBarMobileView.class); mGroup.addView(mock, index); 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 d44af885a27e..9c7f6190de44 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 @@ -21,6 +21,8 @@ import static android.service.notification.NotificationListenerService.REASON_CL import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.AdditionalAnswers.answerVoid; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -61,10 +63,14 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.data.repository.FakePowerRepository; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.shade.ShadeControllerImpl; @@ -110,6 +116,8 @@ import java.util.Optional; @TestableLooper.RunWithLooper(setAsMainLooper = true) public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { + private static final int DISPLAY_ID = 0; + @Mock private AssistManager mAssistManager; @Mock @@ -118,13 +126,12 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private NotificationClickNotifier mClickNotifier; @Mock private StatusBarStateController mStatusBarStateController; + @Mock private ScreenOffAnimationController mScreenOffAnimationController; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock - private CentralSurfaces mCentralSurfaces; - @Mock private KeyguardStateController mKeyguardStateController; @Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider; @@ -150,6 +157,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private ActivityLaunchAnimator mActivityLaunchAnimator; @Mock private InteractionJankMonitor mJankMonitor; + private FakePowerRepository mPowerRepository; + private PowerInteractor mPowerInteractor; @Mock private UserTracker mUserTracker; private final FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -199,6 +208,14 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { when(mUserTracker.getUserHandle()).thenReturn( UserHandle.of(ActivityManager.getCurrentUser())); + mPowerRepository = new FakePowerRepository(); + mPowerInteractor = new PowerInteractor( + mPowerRepository, + new FakeKeyguardRepository(), + new FalsingCollectorFake(), + mScreenOffAnimationController, + mStatusBarStateController); + HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class); NotificationLaunchAnimatorControllerProvider notificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( @@ -209,6 +226,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter = new StatusBarNotificationActivityStarter( getContext(), + DISPLAY_ID, mHandler, mUiBgExecutor, mVisibilityProvider, @@ -231,13 +249,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(MetricsLogger.class), mock(StatusBarNotificationActivityStarterLogger.class), mOnUserInteractionCallback, - mCentralSurfaces, mock(NotificationPresenter.class), mock(ShadeViewController.class), mock(NotificationShadeWindowController.class), mActivityLaunchAnimator, notificationAnimationProvider, mock(LaunchFullScreenIntentProvider.class), + mPowerInteractor, mock(FeatureFlags.class), mUserTracker ); @@ -274,7 +292,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { notification.flags |= Notification.FLAG_AUTO_CANCEL; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mNotificationRow); @@ -340,7 +358,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Given sbn.getNotification().contentIntent = null; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mBubbleNotificationRow); @@ -368,7 +386,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Given sbn.getNotification().contentIntent = mContentIntent; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mBubbleNotificationRow); @@ -402,11 +420,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { when(entry.getImportance()).thenReturn(NotificationManager.IMPORTANCE_HIGH); when(entry.getSbn()).thenReturn(sbn); - // WHEN + // WHEN the intent is launched while dozing + when(mStatusBarStateController.isDozing()).thenReturn(true); mNotificationActivityStarter.launchFullScreenIntent(entry); // THEN display should try wake up for the full screen intent - verify(mCentralSurfaces).wakeUpForFullScreenIntent(); + assertThat(mPowerRepository.getLastWakeReason()).isNotNull(); + assertThat(mPowerRepository.getLastWakeWhy()).isNotNull(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt index 90821bdef0be..d212c026d66e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt @@ -126,6 +126,16 @@ class StylusUsiPowerUiTest : SysuiTestCase() { } @Test + fun updateBatteryState_capacitySame_inputDeviceChanges_updatesInputDeviceId() { + stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f)) + stylusUsiPowerUi.updateBatteryState(1, FixedCapacityBatteryState(0.1f)) + + assertThat(stylusUsiPowerUi.inputDeviceId).isEqualTo(1) + verify(notificationManager, times(1)) + .notify(eq(R.string.stylus_battery_low_percentage), any()) + } + + @Test fun updateBatteryState_existingNotification_capacityAboveThreshold_cancelsNotification() { stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f)) stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.8f)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java index 27957ed06d11..f299ad45a7ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; 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.Application; @@ -36,6 +37,7 @@ import android.app.ITransientNotificationCallback; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.hardware.display.DisplayManager; import android.os.Binder; import android.os.Build; import android.os.Parcel; @@ -74,6 +76,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; +import java.util.Arrays; + @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -400,6 +404,18 @@ public class ToastUITest extends SysuiTestCase { verify(mToastLogger, never()).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString()); } + @Test + public void testShowToast_invalidDisplayId_logsAndSkipsToast() { + int invalidDisplayId = getInvalidDisplayId(); + + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mCallback, invalidDisplayId); + + verify(mToastLogger).logOnSkipToastForInvalidDisplay(PACKAGE_NAME_1, TOKEN_1.toString(), + invalidDisplayId); + verifyZeroInteractions(mWindowManager); + } + private View verifyWmAddViewAndAttachToParent() { ArgumentCaptor<View> viewCaptor = ArgumentCaptor.forClass(View.class); verify(mWindowManager).addView(viewCaptor.capture(), any()); @@ -416,4 +432,10 @@ public class ToastUITest extends SysuiTestCase { return null; }; } + + private int getInvalidDisplayId() { + return Arrays.stream( + mContext.getSystemService(DisplayManager.class).getDisplays()) + .map(Display::getDisplayId).max(Integer::compare).get() + 1; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 9de7a87c8b82..ef0adbb91a63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -34,7 +34,6 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; @@ -76,7 +75,6 @@ public class WMShellTest extends SysuiTestCase { @Mock SplitScreen mSplitScreen; @Mock OneHanded mOneHanded; @Mock WakefulnessLifecycle mWakefulnessLifecycle; - @Mock ProtoTracer mProtoTracer; @Mock UserTracker mUserTracker; @Mock ShellExecutor mSysUiMainExecutor; @Mock NoteTaskInitializer mNoteTaskInitializer; @@ -99,7 +97,6 @@ public class WMShellTest extends SysuiTestCase { mKeyguardUpdateMonitor, mScreenLifecycle, mSysUiState, - mProtoTracer, mWakefulnessLifecycle, mUserTracker, displayTracker, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt index bebdd4004c3d..28892baec3c5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt @@ -40,7 +40,7 @@ class FakeAuthenticationRepository( private val _isUnlocked = MutableStateFlow(false) override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow() - override val hintedPinLength: Int = 6 + override val hintedPinLength: Int = HINTING_PIN_LENGTH private val _isPatternVisible = MutableStateFlow(true) override val isPatternVisible: StateFlow<Boolean> = _isPatternVisible.asStateFlow() @@ -82,7 +82,7 @@ class FakeAuthenticationRepository( } override suspend fun getPinLength(): Int { - return (credentialOverride ?: listOf(1, 2, 3, 4)).size + return (credentialOverride ?: DEFAULT_PIN).size } override fun setBypassEnabled(isBypassEnabled: Boolean) { @@ -148,6 +148,15 @@ class FakeAuthenticationRepository( } } + private fun getExpectedCredential(securityMode: SecurityMode): List<Any> { + return when (val credentialType = getCurrentCredentialType(securityMode)) { + LockPatternUtils.CREDENTIAL_TYPE_PIN -> credentialOverride ?: DEFAULT_PIN + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD -> "password".toList() + LockPatternUtils.CREDENTIAL_TYPE_PATTERN -> PATTERN.toCells() + else -> error("Unsupported credential type $credentialType!") + } + } + companion object { val DEFAULT_AUTHENTICATION_METHOD = AuthenticationMethodModel.Pin val PATTERN = @@ -162,6 +171,8 @@ class FakeAuthenticationRepository( ) const val MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING = 5 const val THROTTLE_DURATION_MS = 30000 + const val HINTING_PIN_LENGTH = 6 + val DEFAULT_PIN = buildList { repeat(HINTING_PIN_LENGTH) { add(it + 1) } } private fun AuthenticationMethodModel.toSecurityMode(): SecurityMode { return when (this) { @@ -188,15 +199,6 @@ class FakeAuthenticationRepository( } } - private fun getExpectedCredential(securityMode: SecurityMode): List<Any> { - return when (val credentialType = getCurrentCredentialType(securityMode)) { - LockPatternUtils.CREDENTIAL_TYPE_PIN -> listOf(1, 2, 3, 4) - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD -> "password".toList() - LockPatternUtils.CREDENTIAL_TYPE_PATTERN -> PATTERN.toCells() - else -> error("Unsupported credential type $credentialType!") - } - } - private fun LockscreenCredential.matches(expectedCredential: List<Any>): Boolean { @Suppress("UNCHECKED_CAST") return when { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt index 2362a5241244..0c5e43809fab 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt @@ -20,16 +20,12 @@ import android.hardware.biometrics.SensorLocationInternal import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow class FakeFingerprintPropertyRepository : FingerprintPropertyRepository { - private val _isInitialized: MutableStateFlow<Boolean> = MutableStateFlow(false) - override val isInitialized = _isInitialized.asStateFlow() - private val _sensorId: MutableStateFlow<Int> = MutableStateFlow(-1) - override val sensorId: StateFlow<Int> = _sensorId.asStateFlow() + override val sensorId = _sensorId.asStateFlow() private val _strength: MutableStateFlow<SensorStrength> = MutableStateFlow(SensorStrength.CONVENIENCE) @@ -37,12 +33,11 @@ class FakeFingerprintPropertyRepository : FingerprintPropertyRepository { private val _sensorType: MutableStateFlow<FingerprintSensorType> = MutableStateFlow(FingerprintSensorType.UNKNOWN) - override val sensorType: StateFlow<FingerprintSensorType> = _sensorType.asStateFlow() + override val sensorType = _sensorType.asStateFlow() private val _sensorLocations: MutableStateFlow<Map<String, SensorLocationInternal>> = MutableStateFlow(mapOf("" to SensorLocationInternal.DEFAULT)) - override val sensorLocations: StateFlow<Map<String, SensorLocationInternal>> = - _sensorLocations.asStateFlow() + override val sensorLocations = _sensorLocations.asStateFlow() fun setProperties( sensorId: Int, @@ -54,6 +49,5 @@ class FakeFingerprintPropertyRepository : FingerprintPropertyRepository { _strength.value = strength _sensorType.value = sensorType _sensorLocations.value = sensorLocations - _isInitialized.value = true } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java index c664c99cf2a7..56837e8cc7ef 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java @@ -21,7 +21,6 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import java.util.List; @@ -62,10 +61,6 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager> } @Override - public void setWifiIcon(String slot, WifiIconState state) { - } - - @Override public void setNewWifiIcon() { } diff --git a/services/Android.bp b/services/Android.bp index 453f57234145..b0a0e5e44a8c 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -159,7 +159,6 @@ java_library { "services.coverage", "services.credentials", "services.devicepolicy", - "services.flags", "services.midi", "services.musicsearch", "services.net", diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index cd2f844294e3..254e6ce14630 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -1094,8 +1094,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } void onEnteringPipBlocked(int uid) { - showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_pip_blocked, - Toast.LENGTH_LONG, mContext.getMainLooper()); + // Do nothing. ActivityRecord#checkEnterPictureInPictureState logs that the display does not + // support PiP. } void playSoundEffect(int effectType) { diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index ca15dd79adbc..c6d6122aeed6 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -40,6 +40,7 @@ import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameManagerService; import android.app.IGameModeListener; +import android.app.IGameStateListener; import android.app.StatsManager; import android.app.UidObserver; import android.content.BroadcastReceiver; @@ -148,6 +149,7 @@ public final class GameManagerService extends IGameManagerService.Stub { private final Object mLock = new Object(); private final Object mDeviceConfigLock = new Object(); private final Object mGameModeListenerLock = new Object(); + private final Object mGameStateListenerLock = new Object(); @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) final Handler mHandler; private final PackageManager mPackageManager; @@ -164,6 +166,8 @@ public final class GameManagerService extends IGameManagerService.Stub { // listener to caller uid map @GuardedBy("mGameModeListenerLock") private final ArrayMap<IGameModeListener, Integer> mGameModeListeners = new ArrayMap<>(); + @GuardedBy("mGameStateListenerLock") + private final ArrayMap<IGameStateListener, Integer> mGameStateListeners = new ArrayMap<>(); @Nullable private final GameServiceController mGameServiceController; private final Object mUidObserverLock = new Object(); @@ -352,6 +356,16 @@ public final class GameManagerService extends IGameManagerService.Stub { loadingBoostDuration); } } + synchronized (mGameStateListenerLock) { + for (IGameStateListener listener : mGameStateListeners.keySet()) { + try { + listener.onGameStateChanged(packageName, gameState, userId); + } catch (RemoteException ex) { + Slog.w(TAG, "Cannot notify game state change for listener added by " + + mGameStateListeners.get(listener)); + } + } + } break; } case CANCEL_GAME_LOADING_MODE: { @@ -1474,6 +1488,43 @@ public final class GameManagerService extends IGameManagerService.Stub { } /** + * Adds a game state listener. + */ + @Override + public void addGameStateListener(@NonNull IGameStateListener listener) { + try { + final IBinder listenerBinder = listener.asBinder(); + listenerBinder.linkToDeath(new DeathRecipient() { + @Override public void binderDied() { + removeGameStateListenerUnchecked(listener); + listenerBinder.unlinkToDeath(this, 0 /*flags*/); + } + }, 0 /*flags*/); + synchronized (mGameStateListenerLock) { + mGameStateListeners.put(listener, Binder.getCallingUid()); + } + } catch (RemoteException ex) { + Slog.e(TAG, + "Failed to link death recipient for IGameStateListener from caller " + + Binder.getCallingUid() + ", abandoned its listener registration", ex); + } + } + + /** + * Removes a game state listener. + */ + @Override + public void removeGameStateListener(@NonNull IGameStateListener listener) { + removeGameStateListenerUnchecked(listener); + } + + private void removeGameStateListenerUnchecked(IGameStateListener listener) { + synchronized (mGameStateListenerLock) { + mGameStateListeners.remove(listener); + } + } + + /** * Notified when boot is completed. */ @VisibleForTesting diff --git a/services/core/java/com/android/server/biometrics/BiometricSensorPrivacy.java b/services/core/java/com/android/server/biometrics/BiometricCameraManager.java index 6727fbcdec66..058ea6bbb696 100644 --- a/services/core/java/com/android/server/biometrics/BiometricSensorPrivacy.java +++ b/services/core/java/com/android/server/biometrics/BiometricCameraManager.java @@ -17,9 +17,16 @@ package com.android.server.biometrics; /** - * Interface for biometric operations to get camera privacy state. + * Interface for biometrics to get camera status. */ -public interface BiometricSensorPrivacy { - /* Returns true if privacy is enabled and camera access is disabled. */ +public interface BiometricCameraManager { + /** + * Returns true if any camera is in use. + */ + boolean isAnyCameraUnavailable(); + + /** + * Returns true if privacy is enabled and camera access is disabled. + */ boolean isCameraPrivacyEnabled(); } diff --git a/services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java b/services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java new file mode 100644 index 000000000000..000ee5446962 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics; + +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; + +import android.annotation.NonNull; +import android.hardware.SensorPrivacyManager; +import android.hardware.camera2.CameraManager; + +import java.util.concurrent.ConcurrentHashMap; + +public class BiometricCameraManagerImpl implements BiometricCameraManager { + + private final CameraManager mCameraManager; + private final SensorPrivacyManager mSensorPrivacyManager; + private final ConcurrentHashMap<String, Boolean> mIsCameraAvailable = new ConcurrentHashMap<>(); + + private final CameraManager.AvailabilityCallback mCameraAvailabilityCallback = + new CameraManager.AvailabilityCallback() { + @Override + public void onCameraAvailable(@NonNull String cameraId) { + mIsCameraAvailable.put(cameraId, true); + } + + @Override + public void onCameraUnavailable(@NonNull String cameraId) { + mIsCameraAvailable.put(cameraId, false); + } + }; + + public BiometricCameraManagerImpl(@NonNull CameraManager cameraManager, + @NonNull SensorPrivacyManager sensorPrivacyManager) { + mCameraManager = cameraManager; + mSensorPrivacyManager = sensorPrivacyManager; + mCameraManager.registerAvailabilityCallback(mCameraAvailabilityCallback, null); + } + + @Override + public boolean isAnyCameraUnavailable() { + for (String cameraId : mIsCameraAvailable.keySet()) { + if (!mIsCameraAvailable.get(cameraId)) { + return true; + } + } + return false; + } + + @Override + public boolean isCameraPrivacyEnabled() { + return mSensorPrivacyManager != null && mSensorPrivacyManager + .isSensorPrivacyEnabled(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, CAMERA); + } +} diff --git a/services/core/java/com/android/server/biometrics/BiometricSensorPrivacyImpl.java b/services/core/java/com/android/server/biometrics/BiometricSensorPrivacyImpl.java deleted file mode 100644 index b6701da1d348..000000000000 --- a/services/core/java/com/android/server/biometrics/BiometricSensorPrivacyImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.biometrics; - -import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; - -import android.annotation.Nullable; -import android.hardware.SensorPrivacyManager; - -public class BiometricSensorPrivacyImpl implements - BiometricSensorPrivacy { - private final SensorPrivacyManager mSensorPrivacyManager; - - public BiometricSensorPrivacyImpl(@Nullable SensorPrivacyManager sensorPrivacyManager) { - mSensorPrivacyManager = sensorPrivacyManager; - } - - @Override - public boolean isCameraPrivacyEnabled() { - return mSensorPrivacyManager != null && mSensorPrivacyManager - .isSensorPrivacyEnabled(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, CAMERA); - } -} diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 1fa97a3cb97f..e8ffe4feb458 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -48,6 +48,7 @@ import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorPropertiesInternal; +import android.hardware.camera2.CameraManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.net.Uri; @@ -125,7 +126,7 @@ public class BiometricService extends SystemService { AuthSession mAuthSession; private final Handler mHandler = new Handler(Looper.getMainLooper()); - private final BiometricSensorPrivacy mBiometricSensorPrivacy; + private final BiometricCameraManager mBiometricCameraManager; /** * Tracks authenticatorId invalidation. For more details, see @@ -936,7 +937,7 @@ public class BiometricService extends SystemService { return PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, mSensors, userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */, - getContext(), mBiometricSensorPrivacy); + getContext(), mBiometricCameraManager); } /** @@ -1030,9 +1031,9 @@ public class BiometricService extends SystemService { return context.getSystemService(UserManager.class); } - public BiometricSensorPrivacy getBiometricSensorPrivacy(Context context) { - return new BiometricSensorPrivacyImpl(context.getSystemService( - SensorPrivacyManager.class)); + public BiometricCameraManager getBiometricCameraManager(Context context) { + return new BiometricCameraManagerImpl(context.getSystemService(CameraManager.class), + context.getSystemService(SensorPrivacyManager.class)); } } @@ -1062,7 +1063,7 @@ public class BiometricService extends SystemService { mRequestCounter = mInjector.getRequestGenerator(); mBiometricContext = injector.getBiometricContext(context); mUserManager = injector.getUserManager(context); - mBiometricSensorPrivacy = injector.getBiometricSensorPrivacy(context); + mBiometricCameraManager = injector.getBiometricCameraManager(context); try { injector.getActivityManagerService().registerUserSwitchObserver( @@ -1299,7 +1300,7 @@ public class BiometricService extends SystemService { final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, mSensors, userId, promptInfo, opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists(), - getContext(), mBiometricSensorPrivacy); + getContext(), mBiometricCameraManager); final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus(); diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index e6f25cb88006..b1740a780539 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -72,16 +72,16 @@ class PreAuthInfo { final Context context; private final boolean mBiometricRequested; private final int mBiometricStrengthRequested; - private final BiometricSensorPrivacy mBiometricSensorPrivacy; + private final BiometricCameraManager mBiometricCameraManager; private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested, boolean credentialRequested, List<BiometricSensor> eligibleSensors, List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable, boolean confirmationRequested, boolean ignoreEnrollmentState, int userId, - Context context, BiometricSensorPrivacy biometricSensorPrivacy) { + Context context, BiometricCameraManager biometricCameraManager) { mBiometricRequested = biometricRequested; mBiometricStrengthRequested = biometricStrengthRequested; - mBiometricSensorPrivacy = biometricSensorPrivacy; + mBiometricCameraManager = biometricCameraManager; this.credentialRequested = credentialRequested; this.eligibleSensors = eligibleSensors; @@ -99,7 +99,7 @@ class PreAuthInfo { List<BiometricSensor> sensors, int userId, PromptInfo promptInfo, String opPackageName, boolean checkDevicePolicyManager, Context context, - BiometricSensorPrivacy biometricSensorPrivacy) + BiometricCameraManager biometricCameraManager) throws RemoteException { final boolean confirmationRequested = promptInfo.isConfirmationRequested(); @@ -127,7 +127,7 @@ class PreAuthInfo { checkDevicePolicyManager, requestedStrength, promptInfo.getAllowedSensorIds(), promptInfo.isIgnoreEnrollmentState(), - biometricSensorPrivacy); + biometricCameraManager); Slog.d(TAG, "Package: " + opPackageName + " Sensor ID: " + sensor.id @@ -151,7 +151,7 @@ class PreAuthInfo { return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested, eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested, - promptInfo.isIgnoreEnrollmentState(), userId, context, biometricSensorPrivacy); + promptInfo.isIgnoreEnrollmentState(), userId, context, biometricCameraManager); } /** @@ -168,12 +168,16 @@ class PreAuthInfo { BiometricSensor sensor, int userId, String opPackageName, boolean checkDevicePolicyManager, int requestedStrength, @NonNull List<Integer> requestedSensorIds, - boolean ignoreEnrollmentState, BiometricSensorPrivacy biometricSensorPrivacy) { + boolean ignoreEnrollmentState, BiometricCameraManager biometricCameraManager) { if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) { return BIOMETRIC_NO_HARDWARE; } + if (sensor.modality == TYPE_FACE && biometricCameraManager.isAnyCameraUnavailable()) { + return BIOMETRIC_HARDWARE_NOT_DETECTED; + } + final boolean wasStrongEnough = Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength); final boolean isStrongEnough = @@ -195,8 +199,8 @@ class PreAuthInfo { return BIOMETRIC_NOT_ENROLLED; } - if (biometricSensorPrivacy != null && sensor.modality == TYPE_FACE) { - if (biometricSensorPrivacy.isCameraPrivacyEnabled()) { + if (biometricCameraManager != null && sensor.modality == TYPE_FACE) { + if (biometricCameraManager.isCameraPrivacyEnabled()) { //Camera privacy is enabled as the access is disabled return BIOMETRIC_SENSOR_PRIVACY_ENABLED; } @@ -307,8 +311,8 @@ class PreAuthInfo { @BiometricAuthenticator.Modality int modality = TYPE_NONE; boolean cameraPrivacyEnabled = false; - if (mBiometricSensorPrivacy != null) { - cameraPrivacyEnabled = mBiometricSensorPrivacy.isCameraPrivacyEnabled(); + if (mBiometricCameraManager != null) { + cameraPrivacyEnabled = mBiometricCameraManager.isCameraPrivacyEnabled(); } if (mBiometricRequested && credentialRequested) { diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index 06417d725e7e..f51b62d77ab9 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -66,7 +66,6 @@ import com.android.internal.R; import com.android.internal.widget.LockPatternUtils; import com.android.server.biometrics.sensors.BaseClientMonitor; -import java.util.ArrayList; import java.util.List; public class Utils { @@ -98,33 +97,6 @@ public class Utils { } /** - * Get the enabled HAL instances. If virtual is enabled and available it will be returned as - * the only instance, otherwise all other instances will be returned. - * - * @param context system context - * @param declaredInstances known instances - * @return filtered list of enabled instances - */ - @NonNull - public static List<String> filterAvailableHalInstances(@NonNull Context context, - @NonNull List<String> declaredInstances) { - if (declaredInstances.size() <= 1) { - return declaredInstances; - } - - final int virtualAt = declaredInstances.indexOf("virtual"); - if (isVirtualEnabled(context) && virtualAt != -1) { - return List.of(declaredInstances.get(virtualAt)); - } - - declaredInstances = new ArrayList<>(declaredInstances); - if (virtualAt != -1) { - declaredInstances.remove(virtualAt); - } - return declaredInstances; - } - - /** * Combines {@link PromptInfo#setDeviceCredentialAllowed(boolean)} with * {@link PromptInfo#setAuthenticators(int)}, as the former is not flexible enough. */ diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 28cb7d9445ee..7cc6940f4b9d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -871,19 +871,22 @@ public class FingerprintService extends SystemService { super.registerAuthenticators_enforcePermission(); mRegistry.registerAll(() -> { - final List<ServiceProvider> providers = new ArrayList<>(); - providers.addAll(getHidlProviders(hidlSensors)); List<String> aidlSensors = new ArrayList<>(); final String[] instances = mAidlInstanceNameSupplier.get(); if (instances != null) { aidlSensors.addAll(Lists.newArrayList(instances)); } - providers.addAll(getAidlProviders( - Utils.filterAvailableHalInstances(getContext(), aidlSensors))); + + final Pair<List<FingerprintSensorPropertiesInternal>, List<String>> + filteredInstances = filterAvailableHalInstances(hidlSensors, aidlSensors); + + final List<ServiceProvider> providers = new ArrayList<>(); + providers.addAll(getHidlProviders(filteredInstances.first)); + providers.addAll(getAidlProviders(filteredInstances.second)); + return providers; }); } - @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void addAuthenticatorsRegisteredCallback( @@ -1038,6 +1041,33 @@ public class FingerprintService extends SystemService { }); } + private Pair<List<FingerprintSensorPropertiesInternal>, List<String>> + filterAvailableHalInstances( + @NonNull List<FingerprintSensorPropertiesInternal> hidlInstances, + @NonNull List<String> aidlInstances) { + if ((hidlInstances.size() + aidlInstances.size()) <= 1) { + return new Pair(hidlInstances, aidlInstances); + } + + final int virtualAt = aidlInstances.indexOf("virtual"); + if (Utils.isVirtualEnabled(getContext())) { + if (virtualAt != -1) { + //only virtual instance should be returned + return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt))); + } else { + Slog.e(TAG, "Could not find virtual interface while it is enabled"); + return new Pair(hidlInstances, aidlInstances); + } + } else { + //remove virtual instance + aidlInstances = new ArrayList<>(aidlInstances); + if (virtualAt != -1) { + aidlInstances.remove(virtualAt); + } + return new Pair(hidlInstances, aidlInstances); + } + } + @NonNull private List<ServiceProvider> getHidlProviders( @NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) { diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java index c421ec04d6f5..59844e1afd1c 100644 --- a/services/core/java/com/android/server/display/BrightnessThrottler.java +++ b/services/core/java/com/android/server/display/BrightnessThrottler.java @@ -38,17 +38,24 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel; import com.android.server.display.feature.DeviceConfigParameterProvider; +import com.android.server.display.utils.DeviceConfigParsingUtils; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Executor; +import java.util.function.BiFunction; +import java.util.function.Function; /** * This class monitors various conditions, such as skin temperature throttling status, and limits * the allowed brightness range accordingly. + * + * @deprecated will be replaced by + * {@link com.android.server.display.brightness.clamper.BrightnessThermalClamper} */ +@Deprecated class BrightnessThrottler { private static final String TAG = "BrightnessThrottler"; private static final boolean DEBUG = false; @@ -93,8 +100,21 @@ class BrightnessThrottler { // time the underlying display device changes. // This map is indexed by uniqueDisplayId, to provide maps for throttlingId -> throttlingData. // HashMap< uniqueDisplayId, HashMap< throttlingDataId, ThermalBrightnessThrottlingData >> - private final HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>> - mThermalBrightnessThrottlingDataOverride = new HashMap<>(1); + private final Map<String, Map<String, ThermalBrightnessThrottlingData>> + mThermalBrightnessThrottlingDataOverride = new HashMap<>(); + + private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> { + try { + int status = DeviceConfigParsingUtils.parseThermalStatus(key); + float brightnessPoint = DeviceConfigParsingUtils.parseBrightness(value); + return new ThrottlingLevel(status, brightnessPoint); + } catch (IllegalArgumentException iae) { + return null; + } + }; + + private final Function<List<ThrottlingLevel>, ThermalBrightnessThrottlingData> + mDataSetMapper = ThermalBrightnessThrottlingData::create; BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId, @@ -257,79 +277,15 @@ class BrightnessThrottler { // 456,2,moderate,0.9,critical,0.7,id_2 // displayId, number, <state, val> * number // displayId, <number, <state, val> * number>, throttlingId - private boolean parseAndAddData(@NonNull String strArray, - @NonNull HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>> - displayIdToThrottlingIdToBtd) { - boolean validConfig = true; - String[] items = strArray.split(","); - int i = 0; - - try { - String uniqueDisplayId = items[i++]; - - // number of throttling points - int noOfThrottlingPoints = Integer.parseInt(items[i++]); - List<ThrottlingLevel> throttlingLevels = new ArrayList<>(noOfThrottlingPoints); - - // throttling level and point - for (int j = 0; j < noOfThrottlingPoints; j++) { - String severity = items[i++]; - int status = parseThermalStatus(severity); - float brightnessPoint = parseBrightness(items[i++]); - throttlingLevels.add(new ThrottlingLevel(status, brightnessPoint)); - } - - String throttlingDataId = (i < items.length) ? items[i++] : DEFAULT_ID; - ThermalBrightnessThrottlingData throttlingLevelsData = - DisplayDeviceConfig.ThermalBrightnessThrottlingData.create(throttlingLevels); - - // Add throttlingLevelsData to inner map where necessary. - HashMap<String, ThermalBrightnessThrottlingData> throttlingMapForDisplay = - displayIdToThrottlingIdToBtd.get(uniqueDisplayId); - if (throttlingMapForDisplay == null) { - throttlingMapForDisplay = new HashMap<>(); - throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData); - displayIdToThrottlingIdToBtd.put(uniqueDisplayId, throttlingMapForDisplay); - } else if (throttlingMapForDisplay.containsKey(throttlingDataId)) { - Slog.e(TAG, "Throttling data for display " + uniqueDisplayId - + "contains duplicate throttling ids: '" + throttlingDataId + "'"); - return false; - } else { - throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData); - } - } catch (NumberFormatException | IndexOutOfBoundsException - | UnknownThermalStatusException e) { - Slog.e(TAG, "Throttling data is invalid array: '" + strArray + "'", e); - validConfig = false; - } - - if (i != items.length) { - validConfig = false; - } - return validConfig; - } - private void loadThermalBrightnessThrottlingDataFromDeviceConfig() { - HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>> tempThrottlingData = - new HashMap<>(1); mThermalBrightnessThrottlingDataString = mConfigParameterProvider.getBrightnessThrottlingData(); - boolean validConfig = true; mThermalBrightnessThrottlingDataOverride.clear(); if (mThermalBrightnessThrottlingDataString != null) { - String[] throttlingDataSplits = mThermalBrightnessThrottlingDataString.split(";"); - for (String s : throttlingDataSplits) { - if (!parseAndAddData(s, tempThrottlingData)) { - validConfig = false; - break; - } - } - - if (validConfig) { - mThermalBrightnessThrottlingDataOverride.putAll(tempThrottlingData); - tempThrottlingData.clear(); - } - + Map<String, Map<String, ThermalBrightnessThrottlingData>> tempThrottlingData = + DeviceConfigParsingUtils.parseDeviceConfigMap( + mThermalBrightnessThrottlingDataString, mDataPointMapper, mDataSetMapper); + mThermalBrightnessThrottlingDataOverride.putAll(tempThrottlingData); } else { Slog.w(TAG, "DeviceConfig ThermalBrightnessThrottlingData is null"); } @@ -395,42 +351,6 @@ class BrightnessThrottler { } } - private float parseBrightness(String intVal) throws NumberFormatException { - float value = Float.parseFloat(intVal); - if (value < PowerManager.BRIGHTNESS_MIN || value > PowerManager.BRIGHTNESS_MAX) { - throw new NumberFormatException("Brightness constraint value out of bounds."); - } - return value; - } - - @PowerManager.ThermalStatus private int parseThermalStatus(@NonNull String value) - throws UnknownThermalStatusException { - switch (value) { - case "none": - return PowerManager.THERMAL_STATUS_NONE; - case "light": - return PowerManager.THERMAL_STATUS_LIGHT; - case "moderate": - return PowerManager.THERMAL_STATUS_MODERATE; - case "severe": - return PowerManager.THERMAL_STATUS_SEVERE; - case "critical": - return PowerManager.THERMAL_STATUS_CRITICAL; - case "emergency": - return PowerManager.THERMAL_STATUS_EMERGENCY; - case "shutdown": - return PowerManager.THERMAL_STATUS_SHUTDOWN; - default: - throw new UnknownThermalStatusException("Invalid Thermal Status: " + value); - } - } - - private static class UnknownThermalStatusException extends Exception { - UnknownThermalStatusException(String message) { - super(message); - } - } - private final class SkinThermalStatusObserver extends IThermalEventListener.Stub { private final Injector mInjector; private final Handler mHandler; diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java index e27182fe3ea7..dd5afa2bdc39 100644 --- a/services/core/java/com/android/server/display/DisplayBrightnessState.java +++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java @@ -16,6 +16,8 @@ package com.android.server.display; +import android.text.TextUtils; + import com.android.server.display.brightness.BrightnessReason; import java.util.Objects; @@ -29,12 +31,14 @@ public final class DisplayBrightnessState { private final float mSdrBrightness; private final BrightnessReason mBrightnessReason; private final String mDisplayBrightnessStrategyName; + private final boolean mShouldUseAutoBrightness; private DisplayBrightnessState(Builder builder) { - this.mBrightness = builder.getBrightness(); - this.mSdrBrightness = builder.getSdrBrightness(); - this.mBrightnessReason = builder.getBrightnessReason(); - this.mDisplayBrightnessStrategyName = builder.getDisplayBrightnessStrategyName(); + mBrightness = builder.getBrightness(); + mSdrBrightness = builder.getSdrBrightness(); + mBrightnessReason = builder.getBrightnessReason(); + mDisplayBrightnessStrategyName = builder.getDisplayBrightnessStrategyName(); + mShouldUseAutoBrightness = builder.getShouldUseAutoBrightness(); } /** @@ -66,6 +70,13 @@ public final class DisplayBrightnessState { return mDisplayBrightnessStrategyName; } + /** + * @return {@code true} if the device is set up to run auto-brightness. + */ + public boolean getShouldUseAutoBrightness() { + return mShouldUseAutoBrightness; + } + @Override public String toString() { StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:"); @@ -75,6 +86,8 @@ public final class DisplayBrightnessState { stringBuilder.append(getSdrBrightness()); stringBuilder.append("\n brightnessReason:"); stringBuilder.append(getBrightnessReason()); + stringBuilder.append("\n shouldUseAutoBrightness:"); + stringBuilder.append(getShouldUseAutoBrightness()); return stringBuilder.toString(); } @@ -91,28 +104,20 @@ public final class DisplayBrightnessState { return false; } - DisplayBrightnessState - displayBrightnessState = (DisplayBrightnessState) other; + DisplayBrightnessState otherState = (DisplayBrightnessState) other; - if (mBrightness != displayBrightnessState.getBrightness()) { - return false; - } - if (mSdrBrightness != displayBrightnessState.getSdrBrightness()) { - return false; - } - if (!mBrightnessReason.equals(displayBrightnessState.getBrightnessReason())) { - return false; - } - if (!mDisplayBrightnessStrategyName.equals( - displayBrightnessState.getDisplayBrightnessStrategyName())) { - return false; - } - return true; + return mBrightness == otherState.getBrightness() + && mSdrBrightness == otherState.getSdrBrightness() + && mBrightnessReason.equals(otherState.getBrightnessReason()) + && TextUtils.equals(mDisplayBrightnessStrategyName, + otherState.getDisplayBrightnessStrategyName()) + && mShouldUseAutoBrightness == otherState.getShouldUseAutoBrightness(); } @Override public int hashCode() { - return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason); + return Objects.hash( + mBrightness, mSdrBrightness, mBrightnessReason, mShouldUseAutoBrightness); } /** @@ -123,6 +128,23 @@ public final class DisplayBrightnessState { private float mSdrBrightness; private BrightnessReason mBrightnessReason = new BrightnessReason(); private String mDisplayBrightnessStrategyName; + private boolean mShouldUseAutoBrightness; + + /** + * Create a builder starting with the values from the specified {@link + * DisplayBrightnessState}. + * + * @param state The state from which to initialize. + */ + public static Builder from(DisplayBrightnessState state) { + Builder builder = new Builder(); + builder.setBrightness(state.getBrightness()); + builder.setSdrBrightness(state.getSdrBrightness()); + builder.setBrightnessReason(state.getBrightnessReason()); + builder.setDisplayBrightnessStrategyName(state.getDisplayBrightnessStrategyName()); + builder.setShouldUseAutoBrightness(state.getShouldUseAutoBrightness()); + return builder; + } /** * Gets the brightness @@ -200,6 +222,21 @@ public final class DisplayBrightnessState { } /** + * See {@link DisplayBrightnessState#getShouldUseAutoBrightness}. + */ + public Builder setShouldUseAutoBrightness(boolean shouldUseAutoBrightness) { + this.mShouldUseAutoBrightness = shouldUseAutoBrightness; + return this; + } + + /** + * See {@link DisplayBrightnessState#getShouldUseAutoBrightness}. + */ + public boolean getShouldUseAutoBrightness() { + return mShouldUseAutoBrightness; + } + + /** * This is used to construct an immutable DisplayBrightnessState object from its builder */ public DisplayBrightnessState build() { diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index d9cb299db69f..c25b25312c89 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -458,7 +458,7 @@ public class DisplayDeviceConfig { public static final String QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC = "canSetBrightnessViaHwc"; - static final String DEFAULT_ID = "default"; + public static final String DEFAULT_ID = "default"; private static final float BRIGHTNESS_DEFAULT = 0.5f; private static final String ETC_DIR = "etc"; @@ -3127,11 +3127,15 @@ public class DisplayDeviceConfig { public static class ThermalBrightnessThrottlingData { public List<ThrottlingLevel> throttlingLevels; - static class ThrottlingLevel { + /** + * thermal status to brightness cap holder + */ + public static class ThrottlingLevel { public @PowerManager.ThermalStatus int thermalStatus; public float brightness; - ThrottlingLevel(@PowerManager.ThermalStatus int thermalStatus, float brightness) { + public ThrottlingLevel( + @PowerManager.ThermalStatus int thermalStatus, float brightness) { this.thermalStatus = thermalStatus; this.brightness = brightness; } diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 7043af863301..7417aeb22a64 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -48,6 +48,7 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.util.FloatProperty; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.MathUtils; import android.util.MutableFloat; @@ -64,7 +65,6 @@ import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FrameworkStatsLog; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.RingBuffer; import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; @@ -73,6 +73,7 @@ import com.android.server.display.brightness.BrightnessEvent; import com.android.server.display.brightness.BrightnessReason; import com.android.server.display.brightness.BrightnessUtils; import com.android.server.display.brightness.DisplayBrightnessController; +import com.android.server.display.brightness.clamper.BrightnessClamperController; import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy; import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener; @@ -380,6 +381,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private final BrightnessThrottler mBrightnessThrottler; + private final BrightnessClamperController mBrightnessClamperController; + private final Runnable mOnBrightnessChangeRunnable; private final BrightnessEvent mLastBrightnessEvent; @@ -492,7 +495,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(), () -> updatePowerState(), mDisplayId, mSensorManager); mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController); - mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(context, mDisplayId); mTag = "DisplayPowerController2[" + mDisplayId + "]"; mThermalBrightnessThrottlingDataId = logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; @@ -554,8 +556,17 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault, brightnessSetting, () -> postBrightnessChangeRunnable(), new HandlerExecutor(mHandler)); + + mBrightnessClamperController = new BrightnessClamperController(mHandler, + modeChangeCallback::run, new BrightnessClamperController.DisplayDeviceData( + mUniqueDisplayId, + mThermalBrightnessThrottlingDataId, + mDisplayDeviceConfig + )); // Seed the cached brightness saveBrightnessInfo(getScreenBrightnessSetting()); + mAutomaticBrightnessStrategy = + mDisplayBrightnessController.getAutomaticBrightnessStrategy(); DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null; DisplayWhiteBalanceController displayWhiteBalanceController = null; @@ -599,7 +610,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal setUpAutoBrightness(resources, handler); - mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic(); + mColorFadeEnabled = mInjector.isColorFadeEnabled(); mColorFadeFadesConfig = resources.getBoolean( R.bool.config_animateScreenLights); @@ -782,6 +793,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal final String thermalBrightnessThrottlingDataId = mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; + mBrightnessClamperController.onDisplayChanged( + new BrightnessClamperController.DisplayDeviceData(mUniqueDisplayId, + mThermalBrightnessThrottlingDataId, config)); + mHandler.postAtTime(() -> { boolean changed = false; if (mDisplayDevice != device) { @@ -1187,6 +1202,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayPowerProximityStateController.cleanup(); mBrightnessRangeController.stop(); mBrightnessThrottler.stop(); + mBrightnessClamperController.stop(); mHandler.removeCallbacksAndMessages(null); // Release any outstanding wakelocks we're still holding because of pending messages. @@ -1257,14 +1273,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal int state = mDisplayStateController .updateDisplayState(mPowerRequest, mIsEnabled, mIsInTransition); - if (mScreenOffBrightnessSensorController != null) { - mScreenOffBrightnessSensorController - .setLightSensorEnabled(mAutomaticBrightnessStrategy.shouldUseAutoBrightness() - && mIsEnabled && (state == Display.STATE_OFF || (state == Display.STATE_DOZE - && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig())) - && mLeadDisplayId == Layout.NO_LEAD_DISPLAY); - } - // Initialize things the first time the power state is changed. if (mustInitialize) { initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN); @@ -1290,6 +1298,17 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal slowChange = mBrightnessToFollowSlowChange; } + // Set up the ScreenOff controller used when coming out of SCREEN_OFF and the ALS sensor + // doesn't yet have a valid lux value to use with auto-brightness. + if (mScreenOffBrightnessSensorController != null) { + mScreenOffBrightnessSensorController + .setLightSensorEnabled(displayBrightnessState.getShouldUseAutoBrightness() + && mIsEnabled && (state == Display.STATE_OFF + || (state == Display.STATE_DOZE + && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig())) + && mLeadDisplayId == Layout.NO_LEAD_DISPLAY); + } + // Take note if the short term model was already active before applying the current // request changes. final boolean wasShortTermModelActive = @@ -1519,6 +1538,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // allowed range. float animateValue = clampScreenBrightness(brightnessState); + animateValue = mBrightnessClamperController.clamp(animateValue); + // If there are any HDR layers on the screen, we have a special brightness value that we // use instead. We still preserve the calculated brightness for Standard Dynamic Range // (SDR) layers, but the main brightness value will be the one for HDR. @@ -1563,7 +1584,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange, wasShortTermModelActive, mAutomaticBrightnessStrategy.isAutoBrightnessEnabled(), - brightnessIsTemporary); + brightnessIsTemporary, displayBrightnessState.getShouldUseAutoBrightness()); // We save the brightness info *after* the brightness setting has been changed and // adjustments made so that the brightness info reflects the latest value. @@ -1607,8 +1628,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive); mTempBrightnessEvent.setDisplayBrightnessStrategyName(displayBrightnessState .getDisplayBrightnessStrategyName()); - mTempBrightnessEvent.setAutomaticBrightnessEnabled(mAutomaticBrightnessStrategy - .shouldUseAutoBrightness()); + mTempBrightnessEvent.setAutomaticBrightnessEnabled( + displayBrightnessState.getShouldUseAutoBrightness()); // Temporary is what we use during slider interactions. We avoid logging those so that // we don't spam logcat when the slider is being used. boolean tempToTempTransition = @@ -2219,7 +2240,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated, boolean wasShortTermModelActive, boolean autobrightnessEnabled, - boolean brightnessIsTemporary) { + boolean brightnessIsTemporary, boolean shouldUseAutoBrightness) { final float brightnessInNits = mDisplayBrightnessController.convertToAdjustedNits(brightness); @@ -2234,7 +2255,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal || mAutomaticBrightnessController.isInIdleMode() || !autobrightnessEnabled || mBrightnessTracker == null - || !mAutomaticBrightnessStrategy.shouldUseAutoBrightness() + || !shouldUseAutoBrightness || brightnessInNits < 0.0f) { return; } @@ -2411,6 +2432,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal if (mDisplayStateController != null) { mDisplayStateController.dumpsys(pw); } + + pw.println(); + if (mBrightnessClamperController != null) { + mBrightnessClamperController.dump(ipw); + } } @@ -2916,6 +2942,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal displayUniqueId, brightnessMin, brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, hbmMetadata, context); } + + boolean isColorFadeEnabled() { + return !ActivityManager.isLowRamDeviceStatic(); + } } static class CachedBrightnessInfo { diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java index 2f52b708dfb5..ffd62a387a64 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java @@ -29,6 +29,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.AutomaticBrightnessController; import com.android.server.display.BrightnessSetting; import com.android.server.display.DisplayBrightnessState; +import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy; import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy; import java.io.PrintWriter; @@ -134,11 +135,21 @@ public final class DisplayBrightnessController { public DisplayBrightnessState updateBrightness( DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, int targetDisplayState) { + + DisplayBrightnessState state; synchronized (mLock) { mDisplayBrightnessStrategy = mDisplayBrightnessStrategySelector.selectStrategy( displayPowerRequest, targetDisplayState); - return mDisplayBrightnessStrategy.updateBrightness(displayPowerRequest); + state = mDisplayBrightnessStrategy.updateBrightness(displayPowerRequest); + } + + // This is a temporary measure until AutomaticBrightnessStrategy works as a traditional + // strategy. + // TODO: Remove when AutomaticBrightnessStrategy is populating the values directly. + if (state != null) { + state = addAutomaticBrightnessState(state); } + return state; } /** @@ -322,6 +333,13 @@ public final class DisplayBrightnessController { } /** + * TODO(b/253226419): Remove once auto-brightness is a fully-functioning strategy. + */ + public AutomaticBrightnessStrategy getAutomaticBrightnessStrategy() { + return mDisplayBrightnessStrategySelector.getAutomaticBrightnessStrategy(); + } + + /** * Convert a brightness float scale value to a nit value. Adjustments, such as RBC, are not * applied. This is used when storing the brightness in nits for the default display and when * passing the brightness value to follower displays. @@ -425,6 +443,18 @@ public final class DisplayBrightnessController { } } + /** + * TODO(b/253226419): Remove once auto-brightness is a fully-functioning strategy. + */ + private DisplayBrightnessState addAutomaticBrightnessState(DisplayBrightnessState state) { + AutomaticBrightnessStrategy autoStrat = getAutomaticBrightnessStrategy(); + + DisplayBrightnessState.Builder builder = DisplayBrightnessState.Builder.from(state); + builder.setShouldUseAutoBrightness( + autoStrat != null && autoStrat.shouldUseAutoBrightness()); + return builder.build(); + } + @GuardedBy("mLock") private void setTemporaryBrightnessLocked(float temporaryBrightness) { mDisplayBrightnessStrategySelector.getTemporaryDisplayBrightnessStrategy() diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java index 02ca2d33fc99..45f1be076508 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java @@ -25,6 +25,7 @@ import android.view.Display; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy; import com.android.server.display.brightness.strategy.BoostBrightnessStrategy; import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy; import com.android.server.display.brightness.strategy.DozeBrightnessStrategy; @@ -60,6 +61,8 @@ public class DisplayBrightnessStrategySelector { private final FollowerBrightnessStrategy mFollowerBrightnessStrategy; // The brightness strategy used to manage the brightness state when the request is invalid. private final InvalidBrightnessStrategy mInvalidBrightnessStrategy; + // Controls brightness when automatic (adaptive) brightness is running. + private final AutomaticBrightnessStrategy mAutomaticBrightnessStrategy; // We take note of the old brightness strategy so that we can know when the strategy changes. private String mOldBrightnessStrategyName; @@ -81,6 +84,7 @@ public class DisplayBrightnessStrategySelector { mBoostBrightnessStrategy = injector.getBoostBrightnessStrategy(); mFollowerBrightnessStrategy = injector.getFollowerBrightnessStrategy(displayId); mInvalidBrightnessStrategy = injector.getInvalidBrightnessStrategy(); + mAutomaticBrightnessStrategy = injector.getAutomaticBrightnessStrategy(context, displayId); mAllowAutoBrightnessWhileDozingConfig = context.getResources().getBoolean( R.bool.config_allowAutoBrightnessWhileDozing); mOldBrightnessStrategyName = mInvalidBrightnessStrategy.getName(); @@ -130,6 +134,10 @@ public class DisplayBrightnessStrategySelector { return mFollowerBrightnessStrategy; } + public AutomaticBrightnessStrategy getAutomaticBrightnessStrategy() { + return mAutomaticBrightnessStrategy; + } + /** * Returns a boolean flag indicating if the light sensor is to be used to decide the screen * brightness when dozing @@ -198,5 +206,9 @@ public class DisplayBrightnessStrategySelector { InvalidBrightnessStrategy getInvalidBrightnessStrategy() { return new InvalidBrightnessStrategy(); } + + AutomaticBrightnessStrategy getAutomaticBrightnessStrategy(Context context, int displayId) { + return new AutomaticBrightnessStrategy(context, displayId); + } } } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java new file mode 100644 index 000000000000..9345a3d97122 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.brightness.clamper; + +import android.annotation.NonNull; +import android.os.PowerManager; + +import java.io.PrintWriter; + +abstract class BrightnessClamper<T> { + + protected float mBrightnessCap = PowerManager.BRIGHTNESS_MAX; + protected boolean mIsActive = false; + + float getBrightnessCap() { + return mBrightnessCap; + } + + boolean isActive() { + return mIsActive; + } + + void dump(PrintWriter writer) { + writer.println("BrightnessClamper:" + getType()); + writer.println(" mBrightnessCap: " + mBrightnessCap); + writer.println(" mIsActive: " + mIsActive); + } + + @NonNull + abstract Type getType(); + + abstract void onDeviceConfigChanged(); + + abstract void onDisplayChanged(T displayData); + + abstract void stop(); + + enum Type { + THERMAL + } +} diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java new file mode 100644 index 000000000000..d0f28c3bea81 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.brightness.clamper; + +import static com.android.server.display.brightness.clamper.BrightnessClamper.Type; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.PowerManager; +import android.provider.DeviceConfig; +import android.provider.DeviceConfigInterface; +import android.util.IndentingPrintWriter; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.DisplayDeviceConfig; +import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; +import com.android.server.display.feature.DeviceConfigParameterProvider; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Clampers controller, all in DisplayControllerHandler + */ +public class BrightnessClamperController { + + private static final boolean ENABLED = false; + + private final DeviceConfigParameterProvider mDeviceConfigParameterProvider; + private final Handler mHandler; + private final ClamperChangeListener mClamperChangeListenerExternal; + + private final Executor mExecutor; + private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers = new ArrayList<>(); + private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = + properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged); + private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX; + @Nullable + private Type mClamperType = null; + + public BrightnessClamperController(Handler handler, + ClamperChangeListener clamperChangeListener, DisplayDeviceData data) { + this(new Injector(), handler, clamperChangeListener, data); + } + + @VisibleForTesting + BrightnessClamperController(Injector injector, Handler handler, + ClamperChangeListener clamperChangeListener, DisplayDeviceData data) { + mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider(); + mHandler = handler; + mClamperChangeListenerExternal = clamperChangeListener; + mExecutor = new HandlerExecutor(handler); + + Runnable clamperChangeRunnableInternal = this::recalculateBrightnessCap; + + ClamperChangeListener clamperChangeListenerInternal = () -> { + if (!mHandler.hasCallbacks(clamperChangeRunnableInternal)) { + mHandler.post(clamperChangeRunnableInternal); + } + }; + + if (ENABLED) { + mClampers.add( + new BrightnessThermalClamper(handler, clamperChangeListenerInternal, data)); + start(); + } + } + + /** + * Should be called when display changed. Forwards the call to individual clampers + */ + public void onDisplayChanged(DisplayDeviceData data) { + mClampers.forEach(clamper -> clamper.onDisplayChanged(data)); + } + + /** + * Applies clamping + * Called in DisplayControllerHandler + */ + public float clamp(float value) { + return Math.min(value, mBrightnessCap); + } + + /** + * Used to dump ClampersController state. + */ + public void dump(PrintWriter writer) { + writer.println("BrightnessClampersController:"); + writer.println(" mBrightnessCap: " + mBrightnessCap); + writer.println(" mClamperType: " + mClamperType); + IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " "); + mClampers.forEach(clamper -> clamper.dump(ipw)); + } + + /** + * This method should be called when the ClamperController is no longer in use. + * Called in DisplayControllerHandler + */ + public void stop() { + mDeviceConfigParameterProvider.removeOnPropertiesChangedListener( + mOnPropertiesChangedListener); + mClampers.forEach(BrightnessClamper::stop); + } + + + // Called in DisplayControllerHandler + private void recalculateBrightnessCap() { + float brightnessCap = PowerManager.BRIGHTNESS_MAX; + Type clamperType = null; + + BrightnessClamper<?> minClamper = mClampers.stream() + .filter(BrightnessClamper::isActive) + .min((clamper1, clamper2) -> Float.compare(clamper1.getBrightnessCap(), + clamper2.getBrightnessCap())).orElse(null); + + if (minClamper != null) { + brightnessCap = minClamper.getBrightnessCap(); + clamperType = minClamper.getType(); + } + + if (mBrightnessCap != brightnessCap || mClamperType != clamperType) { + mBrightnessCap = brightnessCap; + mClamperType = clamperType; + mClamperChangeListenerExternal.onChanged(); + } + } + + private void start() { + mDeviceConfigParameterProvider.addOnPropertiesChangedListener( + mExecutor, mOnPropertiesChangedListener); + } + + /** + * Clampers change listener + */ + public interface ClamperChangeListener { + /** + * Notifies that clamper state changed + */ + void onChanged(); + } + + @VisibleForTesting + static class Injector { + DeviceConfigParameterProvider getDeviceConfigParameterProvider() { + return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL); + } + } + + /** + * Data for clampers + */ + public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData { + @NonNull + private final String mUniqueDisplayId; + @NonNull + private final String mThermalThrottlingDataId; + + private final DisplayDeviceConfig mDisplayDeviceConfig; + + public DisplayDeviceData(@NonNull String uniqueDisplayId, + @NonNull String thermalThrottlingDataId, + @NonNull DisplayDeviceConfig displayDeviceConfig) { + mUniqueDisplayId = uniqueDisplayId; + mThermalThrottlingDataId = thermalThrottlingDataId; + mDisplayDeviceConfig = displayDeviceConfig; + } + + + @NonNull + @Override + public String getUniqueDisplayId() { + return mUniqueDisplayId; + } + + @NonNull + @Override + public String getThermalThrottlingDataId() { + return mThermalThrottlingDataId; + } + + @Nullable + @Override + public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() { + return mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId().get( + mThermalThrottlingDataId); + } + } +} diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java new file mode 100644 index 000000000000..8ae962b83c69 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.brightness.clamper; + +import static com.android.server.display.DisplayDeviceConfig.DEFAULT_ID; +import static com.android.server.display.brightness.clamper.BrightnessClamperController.ClamperChangeListener; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.Handler; +import android.os.IThermalEventListener; +import android.os.IThermalService; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.Temperature; +import android.provider.DeviceConfigInterface; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; +import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel; +import com.android.server.display.feature.DeviceConfigParameterProvider; +import com.android.server.display.utils.DeviceConfigParsingUtils; + +import java.io.PrintWriter; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; + + +class BrightnessThermalClamper extends + BrightnessClamper<BrightnessThermalClamper.ThermalData> { + + private static final String TAG = "BrightnessThermalClamper"; + + @Nullable + private final IThermalService mThermalService; + @NonNull + private final DeviceConfigParameterProvider mConfigParameterProvider; + @NonNull + private final Handler mHandler; + @NonNull + private final ClamperChangeListener mChangelistener; + // data from DeviceConfig, for all displays, for all dataSets + // mapOf(uniqueDisplayId to mapOf(dataSetId to ThermalBrightnessThrottlingData)) + @NonNull + private Map<String, Map<String, ThermalBrightnessThrottlingData>> + mThermalThrottlingDataOverride = Map.of(); + // data from DisplayDeviceConfig, for particular display+dataSet + @Nullable + private ThermalBrightnessThrottlingData mThermalThrottlingDataFromDeviceConfig = null; + // Active data, if mDataOverride contains data for mUniqueDisplayId, mDataId, then use it, + // otherwise mDataFromDeviceConfig + @Nullable + private ThermalBrightnessThrottlingData mThermalThrottlingDataActive = null; + private boolean mStarted = false; + @Nullable + private String mUniqueDisplayId = null; + @Nullable + private String mDataId = null; + @Temperature.ThrottlingStatus + private int mThrottlingStatus = Temperature.THROTTLING_NONE; + + private final IThermalEventListener mThermalEventListener = new IThermalEventListener.Stub() { + @Override + public void notifyThrottling(Temperature temperature) { + @Temperature.ThrottlingStatus int status = temperature.getStatus(); + mHandler.post(() -> thermalStatusChanged(status)); + } + }; + + private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> { + try { + int status = DeviceConfigParsingUtils.parseThermalStatus(key); + float brightnessPoint = DeviceConfigParsingUtils.parseBrightness(value); + return new ThrottlingLevel(status, brightnessPoint); + } catch (IllegalArgumentException iae) { + return null; + } + }; + + private final Function<List<ThrottlingLevel>, ThermalBrightnessThrottlingData> + mDataSetMapper = ThermalBrightnessThrottlingData::create; + + + BrightnessThermalClamper(Handler handler, ClamperChangeListener listener, + ThermalData thermalData) { + this(new Injector(), handler, listener, thermalData); + } + + @VisibleForTesting + BrightnessThermalClamper(Injector injector, Handler handler, + ClamperChangeListener listener, ThermalData thermalData) { + mThermalService = injector.getThermalService(); + mConfigParameterProvider = injector.getDeviceConfigParameterProvider(); + mHandler = handler; + mChangelistener = listener; + mHandler.post(() -> { + setDisplayData(thermalData); + loadOverrideData(); + start(); + }); + + } + + @Override + @NonNull + Type getType() { + return Type.THERMAL; + } + + @Override + void onDeviceConfigChanged() { + mHandler.post(() -> { + loadOverrideData(); + recalculateActiveData(); + }); + } + + @Override + void onDisplayChanged(ThermalData data) { + mHandler.post(() -> { + setDisplayData(data); + recalculateActiveData(); + }); + } + + @Override + void stop() { + if (!mStarted) { + return; + } + try { + mThermalService.unregisterThermalEventListener(mThermalEventListener); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to unregister thermal status listener", e); + } + mStarted = false; + } + + @Override + void dump(PrintWriter writer) { + writer.println("BrightnessThermalClamper:"); + writer.println(" mStarted: " + mStarted); + if (mThermalService != null) { + writer.println(" ThermalService available"); + } else { + writer.println(" ThermalService not available"); + } + writer.println(" mThrottlingStatus: " + mThrottlingStatus); + writer.println(" mUniqueDisplayId: " + mUniqueDisplayId); + writer.println(" mDataId: " + mDataId); + writer.println(" mDataOverride: " + mThermalThrottlingDataOverride); + writer.println(" mDataFromDeviceConfig: " + mThermalThrottlingDataFromDeviceConfig); + writer.println(" mDataActive: " + mThermalThrottlingDataActive); + super.dump(writer); + } + + private void recalculateActiveData() { + if (mUniqueDisplayId == null || mDataId == null) { + return; + } + mThermalThrottlingDataActive = mThermalThrottlingDataOverride + .getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId, + mThermalThrottlingDataFromDeviceConfig); + + recalculateBrightnessCap(); + } + + private void loadOverrideData() { + String throttlingDataOverride = mConfigParameterProvider.getBrightnessThrottlingData(); + mThermalThrottlingDataOverride = DeviceConfigParsingUtils.parseDeviceConfigMap( + throttlingDataOverride, mDataPointMapper, mDataSetMapper); + } + + private void setDisplayData(@NonNull ThermalData data) { + mUniqueDisplayId = data.getUniqueDisplayId(); + mDataId = data.getThermalThrottlingDataId(); + mThermalThrottlingDataFromDeviceConfig = data.getThermalBrightnessThrottlingData(); + if (mThermalThrottlingDataFromDeviceConfig == null && !DEFAULT_ID.equals(mDataId)) { + Slog.wtf(TAG, + "Thermal throttling data is missing for thermalThrottlingDataId=" + mDataId); + } + } + + private void recalculateBrightnessCap() { + float brightnessCap = PowerManager.BRIGHTNESS_MAX; + boolean isActive = false; + + if (mThermalThrottlingDataActive != null) { + // Throttling levels are sorted by increasing severity + for (ThrottlingLevel level : mThermalThrottlingDataActive.throttlingLevels) { + if (level.thermalStatus <= mThrottlingStatus) { + brightnessCap = level.brightness; + isActive = true; + } else { + // Throttling levels that are greater than the current status are irrelevant + break; + } + } + } + + if (brightnessCap != mBrightnessCap || mIsActive != isActive) { + mBrightnessCap = brightnessCap; + mIsActive = isActive; + mChangelistener.onChanged(); + } + } + + private void thermalStatusChanged(@Temperature.ThrottlingStatus int status) { + if (mThrottlingStatus != status) { + mThrottlingStatus = status; + recalculateBrightnessCap(); + } + } + + private void start() { + if (mThermalService == null) { + Slog.e(TAG, "Could not observe thermal status. Service not available"); + return; + } + try { + // We get a callback immediately upon registering so there's no need to query + // for the current value. + mThermalService.registerThermalEventListenerWithType(mThermalEventListener, + Temperature.TYPE_SKIN); + mStarted = true; + } catch (RemoteException e) { + Slog.e(TAG, "Failed to register thermal status listener", e); + } + } + + interface ThermalData { + @NonNull + String getUniqueDisplayId(); + + @NonNull + String getThermalThrottlingDataId(); + + @Nullable + ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData(); + } + + @VisibleForTesting + static class Injector { + IThermalService getThermalService() { + return IThermalService.Stub.asInterface( + ServiceManager.getService(Context.THERMAL_SERVICE)); + } + + DeviceConfigParameterProvider getDeviceConfigParameterProvider() { + return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL); + } + } +} diff --git a/services/core/java/com/android/server/display/utils/DeviceConfigParsingUtils.java b/services/core/java/com/android/server/display/utils/DeviceConfigParsingUtils.java new file mode 100644 index 000000000000..a8034c58689d --- /dev/null +++ b/services/core/java/com/android/server/display/utils/DeviceConfigParsingUtils.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.utils; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.PowerManager; +import android.util.Slog; + +import com.android.server.display.DisplayDeviceConfig; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * Provides utility methods for DeviceConfig string parsing + */ +public class DeviceConfigParsingUtils { + private static final String TAG = "DeviceConfigParsingUtils"; + + /** + * Parses map from device config + * Data format: + * (displayId:String,numberOfPoints:Int,(state:T,value:Float){numberOfPoints}, + * dataSetId:String)?;)+ + * result : mapOf(displayId to mapOf(dataSetId to V)) + */ + @NonNull + public static <T, V> Map<String, Map<String, V>> parseDeviceConfigMap( + @Nullable String data, + @NonNull BiFunction<String, String, T> dataPointMapper, + @NonNull Function<List<T>, V> dataSetMapper) { + if (data == null) { + return Map.of(); + } + Map<String, Map<String, V>> result = new HashMap<>(); + String[] dataSets = data.split(";"); // by displayId + dataSetId + for (String dataSet : dataSets) { + String[] items = dataSet.split(","); + int noOfItems = items.length; + // Validate number of items, at least: displayId,1,key1,value1 + if (noOfItems < 4) { + Slog.e(TAG, "Invalid dataSet(not enough items):" + dataSet, new Throwable()); + return Map.of(); + } + int i = 0; + String uniqueDisplayId = items[i++]; + + String numberOfPointsString = items[i++]; + int numberOfPoints; + try { + numberOfPoints = Integer.parseInt(numberOfPointsString); + } catch (NumberFormatException nfe) { + Slog.e(TAG, "Invalid dataSet(invalid number of points):" + dataSet, nfe); + return Map.of(); + } + // Validate number of itmes based on numberOfPoints: + // displayId,numberOfPoints,(key,value) x numberOfPoints,dataSetId(optional) + int expectedMinItems = 2 + numberOfPoints * 2; + if (noOfItems < expectedMinItems || noOfItems > expectedMinItems + 1) { + Slog.e(TAG, "Invalid dataSet(wrong number of points):" + dataSet, new Throwable()); + return Map.of(); + } + // Construct data points + List<T> dataPoints = new ArrayList<>(); + for (int j = 0; j < numberOfPoints; j++) { + String key = items[i++]; + String value = items[i++]; + T dataPoint = dataPointMapper.apply(key, value); + if (dataPoint == null) { + Slog.e(TAG, + "Invalid dataPoint ,key=" + key + ",value=" + value + ",dataSet=" + + dataSet, new Throwable()); + return Map.of(); + } + dataPoints.add(dataPoint); + } + // Construct dataSet + V dataSetMapped = dataSetMapper.apply(dataPoints); + if (dataSetMapped == null) { + Slog.e(TAG, "Invalid dataSetMapped dataPoints=" + dataPoints + ",dataSet=" + + dataSet, new Throwable()); + return Map.of(); + } + // Get dataSetId and dataSets map for displayId + String dataSetId = (i < items.length) ? items[i] : DisplayDeviceConfig.DEFAULT_ID; + Map<String, V> byDisplayId = result.computeIfAbsent(uniqueDisplayId, + k -> new HashMap<>()); + + // Try to store dataSet in datasets for display + if (byDisplayId.put(dataSetId, dataSetMapped) != null) { + Slog.e(TAG, "Duplicate dataSetId=" + dataSetId + ",data=" + data, new Throwable()); + return Map.of(); + } + } + return result; + } + + /** + * Parses thermal string value from device config + */ + @PowerManager.ThermalStatus + public static int parseThermalStatus(@NonNull String value) throws IllegalArgumentException { + switch (value) { + case "none": + return PowerManager.THERMAL_STATUS_NONE; + case "light": + return PowerManager.THERMAL_STATUS_LIGHT; + case "moderate": + return PowerManager.THERMAL_STATUS_MODERATE; + case "severe": + return PowerManager.THERMAL_STATUS_SEVERE; + case "critical": + return PowerManager.THERMAL_STATUS_CRITICAL; + case "emergency": + return PowerManager.THERMAL_STATUS_EMERGENCY; + case "shutdown": + return PowerManager.THERMAL_STATUS_SHUTDOWN; + default: + throw new IllegalArgumentException("Invalid Thermal Status: " + value); + } + } + + /** + * Parses brightness value from device config + */ + public static float parseBrightness(String stringVal) throws IllegalArgumentException { + float value = Float.parseFloat(stringVal); + if (value < PowerManager.BRIGHTNESS_MIN || value > PowerManager.BRIGHTNESS_MAX) { + throw new IllegalArgumentException("Brightness value out of bounds: " + stringVal); + } + return value; + } +} diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 02ee96a04b1f..7bda2c1fa5ab 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1175,6 +1175,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId); resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, userId); + resolver.registerContentObserver(Settings.Secure.getUriFor( + STYLUS_HANDWRITING_ENABLED), false, this); mRegistered = true; } @@ -1183,6 +1185,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD); final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE); + final Uri stylusHandwritingEnabledUri = Settings.Secure.getUriFor( + STYLUS_HANDWRITING_ENABLED); synchronized (ImfLock.class) { if (showImeUri.equals(uri)) { mMenuController.updateKeyboardFromSettingsLocked(); @@ -1200,6 +1204,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub showCurrentInputImplicitLocked(mCurFocusedWindow, SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE); } + } else if (stylusHandwritingEnabledUri.equals(uri)) { + InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); } else { boolean enabledChanged = false; String newEnabled = mSettings.getEnabledInputMethodsStr(); @@ -2363,7 +2369,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mCurVirtualDisplayToScreenMatrix = null; ImeTracker.forLogging().onFailed(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME); mCurStatsToken = null; - + InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); mMenuController.hideInputMethodMenuLocked(); } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 3325ddd58330..6df38098205a 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -322,7 +322,6 @@ import com.android.server.notification.toast.ToastRecord; import com.android.server.pm.PackageManagerService; import com.android.server.pm.UserManagerInternal; import com.android.server.policy.PermissionPolicyInternal; -import com.android.server.powerstats.StatsPullAtomCallbackImpl; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.utils.Slogf; @@ -1715,7 +1714,6 @@ public class NotificationManagerService extends SystemService { return; } - boolean queryRestart = false; boolean queryRemove = false; boolean packageChanged = false; boolean cancelNotifications = true; @@ -1727,7 +1725,6 @@ public class NotificationManagerService extends SystemService { || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED)) || action.equals(Intent.ACTION_PACKAGE_RESTARTED) || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) - || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE) || action.equals(Intent.ACTION_PACKAGES_SUSPENDED) || action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED) @@ -1768,10 +1765,6 @@ public class NotificationManagerService extends SystemService { cancelNotifications = false; unhideNotifications = true; } - - } else if (queryRestart) { - pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); - uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)}; } else { Uri uri = intent.getData(); if (uri == null) { @@ -1809,7 +1802,7 @@ public class NotificationManagerService extends SystemService { if (cancelNotifications) { for (String pkgName : pkgList) { cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0, - !queryRestart, changeUserId, reason, null); + changeUserId, reason); } } else if (hideNotifications && uidList != null && (uidList.length > 0)) { hideNotificationsForPackages(pkgList, uidList); @@ -1843,14 +1836,14 @@ public class NotificationManagerService extends SystemService { } else if (action.equals(Intent.ACTION_USER_STOPPED)) { int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); if (userHandle >= 0) { - cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle, - REASON_USER_STOPPED, null); + cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, userHandle, + REASON_USER_STOPPED); } } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) { int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); if (userHandle >= 0 && !mDpm.isKeepProfilesRunningEnabled()) { - cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle, - REASON_PROFILE_TURNED_OFF, null); + cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, userHandle, + REASON_PROFILE_TURNED_OFF); mSnoozeHelper.clearData(userHandle); } } else if (action.equals(Intent.ACTION_USER_PRESENT)) { @@ -2468,7 +2461,6 @@ public class NotificationManagerService extends SystemService { pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); - pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); pkgFilter.addDataScheme("package"); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, null); @@ -2499,6 +2491,16 @@ public class NotificationManagerService extends SystemService { getContext().registerReceiver(mReviewNotificationPermissionsReceiver, ReviewNotificationPermissionsReceiver.getFilter(), Context.RECEIVER_NOT_EXPORTED); + + mAppOps.startWatchingMode(AppOpsManager.OP_POST_NOTIFICATION, null, + new AppOpsManager.OnOpChangedInternalListener() { + @Override + public void onOpChanged(@NonNull String op, @NonNull String packageName, + int userId) { + mHandler.post( + () -> handleNotificationPermissionChange(packageName, userId)); + } + }); } /** @@ -2855,17 +2857,17 @@ public class NotificationManagerService extends SystemService { boolean fromListener) { if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) { // cancel - cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true, - UserHandle.getUserId(uid), REASON_CHANNEL_BANNED, - null); + cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, + UserHandle.getUserId(uid), REASON_CHANNEL_BANNED + ); if (isUidSystemOrPhone(uid)) { IntArray profileIds = mUserProfiles.getCurrentProfileIds(); int N = profileIds.size(); for (int i = 0; i < N; i++) { int profileId = profileIds.get(i); - cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true, - profileId, REASON_CHANNEL_BANNED, - null); + cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, + profileId, REASON_CHANNEL_BANNED + ); } } } @@ -3539,7 +3541,7 @@ public class NotificationManagerService extends SystemService { // Don't allow the app to cancel active FGS or UIJ notifications cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(), pkg, null, 0, FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB, - true, userId, REASON_APP_CANCEL_ALL, null); + userId, REASON_APP_CANCEL_ALL); } @Override @@ -3558,20 +3560,16 @@ public class NotificationManagerService extends SystemService { } mPermissionHelper.setNotificationPermission( pkg, UserHandle.getUserId(uid), enabled, true); - sendAppBlockStateChangedBroadcast(pkg, uid, !enabled); mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES) .setType(MetricsEvent.TYPE_ACTION) .setPackageName(pkg) .setSubtype(enabled ? 1 : 0)); mNotificationChannelLogger.logAppNotificationsAllowed(uid, pkg, enabled); - // Now, cancel any outstanding notifications that are part of a just-disabled app - if (!enabled) { - cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true, - UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null); - } - handleSavePolicyFile(); + // Outstanding notifications from this package will be cancelled, and the package will + // be sent the ACTION_APP_BLOCK_STATE_CHANGED broadcast, as soon as we get the + // callback from AppOpsManager. } /** @@ -4030,8 +4028,8 @@ public class NotificationManagerService extends SystemService { } enforceDeletingChannelHasNoFgService(pkg, callingUser, channelId); enforceDeletingChannelHasNoUserInitiatedJob(pkg, callingUser, channelId); - cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true, - callingUser, REASON_CHANNEL_REMOVED, null); + cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, + callingUser, REASON_CHANNEL_REMOVED); boolean previouslyExisted = mPreferencesHelper.deleteNotificationChannel( pkg, callingUid, channelId, callingUid, isSystemOrSystemUi); if (previouslyExisted) { @@ -4085,9 +4083,8 @@ public class NotificationManagerService extends SystemService { for (int i = 0; i < deletedChannels.size(); i++) { final NotificationChannel deletedChannel = deletedChannels.get(i); cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0, - true, - userId, REASON_CHANNEL_REMOVED, - null); + userId, REASON_CHANNEL_REMOVED + ); mListeners.notifyNotificationChannelChanged(pkg, UserHandle.getUserHandleForUid(callingUid), deletedChannel, @@ -4256,8 +4253,8 @@ public class NotificationManagerService extends SystemService { checkCallerIsSystem(); // Cancel posted notifications final int userId = UserHandle.getUserId(uid); - cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true, - UserHandle.getUserId(Binder.getCallingUid()), REASON_CLEAR_DATA, null); + cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, + UserHandle.getUserId(Binder.getCallingUid()), REASON_CLEAR_DATA); // Zen packagesChanged |= @@ -5895,6 +5892,21 @@ public class NotificationManagerService extends SystemService { } }; + private void handleNotificationPermissionChange(String pkg, @UserIdInt int userId) { + int uid = mPackageManagerInternal.getPackageUid(pkg, 0, userId); + if (uid == INVALID_UID) { + Log.e(TAG, String.format("No uid found for %s, %s!", pkg, userId)); + return; + } + boolean hasPermission = mPermissionHelper.hasPermission(uid); + sendAppBlockStateChangedBroadcast(pkg, uid, !hasPermission); + if (!hasPermission) { + cancelAllNotificationsInt(MY_UID, MY_PID, pkg, /* channelId= */ null, + /* mustHaveFlags= */ 0, /* mustNotHaveFlags= */ 0, userId, + REASON_PACKAGE_BANNED); + } + } + protected void checkNotificationListenerAccess() { if (!isCallerSystemOrPhone()) { getContext().enforceCallingPermission( @@ -6831,9 +6843,9 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper.deleteConversations(pkg, uid, shortcuts, /* callingUid */ Process.SYSTEM_UID, /* is system */ true); for (String channelId : deletedChannelIds) { - cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true, - UserHandle.getUserId(uid), REASON_CHANNEL_REMOVED, - null); + cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, + UserHandle.getUserId(uid), REASON_CHANNEL_REMOVED + ); } handleSavePolicyFile(); } @@ -9535,25 +9547,18 @@ public class NotificationManagerService extends SystemService { /** * Cancels all notifications from a given package that have all of the - * {@code mustHaveFlags}. + * {@code mustHaveFlags} and none of the {@code mustNotHaveFlags}. */ - void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId, - int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason, - ManagedServiceInfo listener) { + void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, + @Nullable String channelId, int mustHaveFlags, int mustNotHaveFlags, int userId, + int reason) { final long cancellationElapsedTimeMs = SystemClock.elapsedRealtime(); mHandler.post(new Runnable() { @Override public void run() { - String listenerName = listener == null ? null : listener.component.toShortString(); EventLogTags.writeNotificationCancelAll(callingUid, callingPid, pkg, userId, mustHaveFlags, mustNotHaveFlags, reason, - listenerName); - - // Why does this parameter exist? Do we actually want to execute the above if doit - // is false? - if (!doit) { - return; - } + /* listener= */ null); synchronized (mNotificationLock) { FlagChecker flagChecker = (int flags) -> { @@ -9565,14 +9570,15 @@ public class NotificationManagerService extends SystemService { } return true; }; - cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid, - pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker, + cancelAllNotificationsByListLocked(mNotificationList, pkg, + true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker, + false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason, + null /* listenerName */, true /* wasPosted */, + cancellationElapsedTimeMs); + cancelAllNotificationsByListLocked(mEnqueuedNotifications, pkg, + true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker, false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason, - listenerName, true /* wasPosted */, cancellationElapsedTimeMs); - cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid, - callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, - flagChecker, false /*includeCurrentProfiles*/, userId, - false /*sendDelete*/, reason, listenerName, false /* wasPosted */, + null /* listenerName */, false /* wasPosted */, cancellationElapsedTimeMs); mSnoozeHelper.cancel(userId, pkg); } @@ -9587,9 +9593,9 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList, - int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch, - String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId, - boolean sendDelete, int reason, String listenerName, boolean wasPosted, + @Nullable String pkg, boolean nullPkgIndicatesUserSwitch, @Nullable String channelId, + FlagChecker flagChecker, boolean includeCurrentProfiles, int userId, boolean sendDelete, + int reason, String listenerName, boolean wasPosted, @ElapsedRealtimeLong long cancellationElapsedTimeMs) { Set<String> childNotifications = null; for (int i = notificationList.size() - 1; i >= 0; --i) { @@ -9706,12 +9712,12 @@ public class NotificationManagerService extends SystemService { return true; }; - cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid, + cancelAllNotificationsByListLocked(mNotificationList, null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/, reason, listenerName, true, cancellationElapsedTimeMs); - cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid, - callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null, + cancelAllNotificationsByListLocked(mEnqueuedNotifications, + null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/, reason, listenerName, false, cancellationElapsedTimeMs); mSnoozeHelper.cancel(userId, includeCurrentProfiles); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0994fa4464db..dd6bcb1060ea 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5660,13 +5660,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final DisplayContent displayContent = getDisplayContent(); if (!visible) { mImeInsetsFrozenUntilStartInput = true; - if (usingShellTransitions) { - final WindowState wallpaperTarget = - displayContent.mWallpaperController.getWallpaperTarget(); - if (wallpaperTarget != null && wallpaperTarget.mActivityRecord == this) { - displayContent.mWallpaperController.hideWallpapers(wallpaperTarget); - } - } } if (!displayContent.mClosingApps.contains(this) diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 4262e94e2894..884100c5da33 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1591,6 +1591,9 @@ class ActivityStarter { if (forceTransientTransition) { transitionController.collect(mLastStartActivityRecord); transitionController.collect(mPriorAboveTask); + // If keyguard is active and occluded, the transient target won't be moved to front + // to be collected, so set transient again after it is collected. + transitionController.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask); final DisplayContent dc = mLastStartActivityRecord.getDisplayContent(); // update wallpaper target to TransientHide dc.mWallpaperController.adjustWallpaperWindows(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 738797b809a5..50948e1cdec4 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2186,7 +2186,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { * Processes the activities to be stopped or destroyed. This should be called when the resumed * activities are idle or drawn. */ - private void processStoppingAndFinishingActivities(ActivityRecord launchedActivity, + void processStoppingAndFinishingActivities(ActivityRecord launchedActivity, boolean processPausingActivities, String reason) { // Stop any activities that are scheduled to do so but have been waiting for the transition // animation to finish. @@ -2194,7 +2194,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { ArrayList<ActivityRecord> readyToStopActivities = null; for (int i = 0; i < mStoppingActivities.size(); i++) { final ActivityRecord s = mStoppingActivities.get(i); - final boolean animating = s.isInTransition(); + // Activity in a force hidden task should not be counted as animating, i.e., we want to + // send onStop before any configuration change when removing pip transition is ongoing. + final boolean animating = s.isInTransition() + && s.getTask() != null && !s.getTask().isForceHidden(); displaySwapping |= s.isDisplaySleepingAndSwapping(); ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b " + "finishing=%s", s, s.nowVisible, animating, s.finishing); diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java index 01158779c24f..4ce21bdadf49 100644 --- a/services/core/java/com/android/server/wm/AsyncRotationController.java +++ b/services/core/java/com/android/server/wm/AsyncRotationController.java @@ -93,15 +93,12 @@ class AsyncRotationController extends FadeAnimationController implements Consume /** Whether the start transaction of the transition is committed (by shell). */ private boolean mIsStartTransactionCommitted; - /** Whether all windows should wait for the start transaction. */ - private boolean mAlwaysWaitForStartTransaction; - /** Whether the target windows have been requested to sync their draw transactions. */ private boolean mIsSyncDrawRequested; private SeamlessRotator mRotator; - private final int mOriginalRotation; + private int mOriginalRotation; private final boolean mHasScreenRotationAnimation; AsyncRotationController(DisplayContent displayContent) { @@ -147,15 +144,6 @@ class AsyncRotationController extends FadeAnimationController implements Consume if (mTransitionOp == OP_LEGACY) { mIsStartTransactionCommitted = true; } else if (displayContent.mTransitionController.isCollecting(displayContent)) { - final Transition transition = - mDisplayContent.mTransitionController.getCollectingTransition(); - if (transition != null) { - final BLASTSyncEngine.SyncGroup syncGroup = - mDisplayContent.mWmService.mSyncEngine.getSyncSet(transition.getSyncId()); - if (syncGroup != null && syncGroup.mSyncMethod == BLASTSyncEngine.METHOD_BLAST) { - mAlwaysWaitForStartTransaction = true; - } - } keepAppearanceInPreviousRotation(); } } @@ -279,10 +267,12 @@ class AsyncRotationController extends FadeAnimationController implements Consume // The previous animation leash will be dropped when preparing fade-in animation, so // simply apply new animation without restoring the transformation. fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM); - } else if (op.mAction == Operation.ACTION_SEAMLESS && mRotator != null + } else if (op.mAction == Operation.ACTION_SEAMLESS && op.mLeash != null && op.mLeash.isValid()) { if (DEBUG) Slog.d(TAG, "finishOp undo seamless " + windowToken.getTopChild()); - mRotator.setIdentityMatrix(windowToken.getSyncTransaction(), op.mLeash); + final SurfaceControl.Transaction t = windowToken.getSyncTransaction(); + t.setMatrix(op.mLeash, 1, 0, 0, 1); + t.setPosition(op.mLeash, 0, 0); } } @@ -365,6 +355,32 @@ class AsyncRotationController extends FadeAnimationController implements Consume } } + /** + * Re-initialize the states if the current display rotation has changed to a different rotation. + * This is mainly for seamless rotation to update the transform based on new rotation. + */ + void updateRotation() { + if (mRotator == null) return; + final int currentRotation = mDisplayContent.getWindowConfiguration().getRotation(); + if (mOriginalRotation == currentRotation) { + return; + } + Slog.d(TAG, "Update original rotation " + currentRotation); + mOriginalRotation = currentRotation; + mDisplayContent.forAllWindows(w -> { + if (w.mForceSeamlesslyRotate && w.mHasSurface + && !mTargetWindowTokens.containsKey(w.mToken)) { + final Operation op = new Operation(Operation.ACTION_SEAMLESS); + op.mLeash = w.mToken.mSurfaceControl; + mTargetWindowTokens.put(w.mToken, op); + } + }, true /* traverseTopToBottom */); + mRotator = null; + mIsStartTransactionCommitted = false; + mIsSyncDrawRequested = false; + keepAppearanceInPreviousRotation(); + } + private void scheduleTimeout() { if (mTimeoutRunnable == null) { mTimeoutRunnable = () -> { @@ -589,7 +605,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume * start transaction of rotation transition is applied. */ private boolean canDrawBeforeStartTransaction(Operation op) { - return !mAlwaysWaitForStartTransaction && op.mAction != Operation.ACTION_SEAMLESS; + return op.mAction != Operation.ACTION_SEAMLESS; } /** The operation to control the rotation appearance associated with window token. */ diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index bfd2a10a8882..cb7414e2e86e 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3457,6 +3457,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp this, this, null /* remoteTransition */, displayChange); if (t != null) { mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY); + if (mAsyncRotationController != null) { + // Give a chance to update the transform if the current rotation is changed when + // some windows haven't finished previous rotation. + mAsyncRotationController.updateRotation(); + } if (mFixedRotationLaunchingApp != null) { // A fixed-rotation transition is done, then continue to start a seamless display // transition. diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index db3b267529e6..71192cd5a3be 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -723,6 +723,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mFlags |= WindowManager.TRANSIT_FLAG_INVISIBLE; return; } + // Activity doesn't need to capture snapshot if the starting window has associated to task. + if (wc.asActivityRecord() != null) { + final ActivityRecord activityRecord = wc.asActivityRecord(); + if (activityRecord.mStartingData != null + && activityRecord.mStartingData.mAssociatedTask != null) { + return; + } + } if (mContainerFreezer == null) { mContainerFreezer = new ScreenshotFreezer(); @@ -1183,16 +1191,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { hasParticipatedDisplay = true; continue; } - final WallpaperWindowToken wt = participant.asWallpaperToken(); - if (wt != null) { - final boolean visibleAtTransitionEnd = mVisibleAtTransitionEndTokens.contains(wt); - if (!visibleAtTransitionEnd && !wt.isVisibleRequested()) { - ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, - " Commit wallpaper becoming invisible: %s", wt); - wt.commitVisibility(false /* visible */); - } - continue; - } final Task tr = participant.asTask(); if (tr != null && tr.isVisibleRequested() && tr.inPinnedWindowingMode()) { final ActivityRecord top = tr.getTopNonFinishingActivity(); @@ -1212,6 +1210,20 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } } + // Commit wallpaper visibility after activity, because usually the wallpaper target token is + // an activity, and wallpaper's visibility is depends on activity's visibility. + for (int i = mParticipants.size() - 1; i >= 0; --i) { + final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken(); + if (wt == null) continue; + final WindowState target = wt.mDisplayContent.mWallpaperController.getWallpaperTarget(); + final boolean isTargetInvisible = target == null || !target.mToken.isVisible(); + if (isTargetInvisible || (!wt.isVisibleRequested() + && !mVisibleAtTransitionEndTokens.contains(wt))) { + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, + " Commit wallpaper becoming invisible: %s", wt); + wt.commitVisibility(false /* visible */); + } + } if (committedSomeInvisible) { mController.onCommittedInvisibles(); } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index d1b00a38701d..541fa9413f45 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -24,6 +24,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS; import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; @@ -568,6 +569,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } if (forceHiddenForPip) { wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */); + // When removing pip, make sure that onStop is sent to the app ahead of + // onPictureInPictureModeChanged. + // See also PinnedStackTests#testStopBeforeMultiWindowCallbacksOnDismiss + wc.asTask().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + wc.asTask().mTaskSupervisor.processStoppingAndFinishingActivities( + null /* launchedActivity */, false /* processPausingActivities */, + "force-stop-on-removing-pip"); } int containerEffect = applyWindowContainerChange(wc, entry.getValue(), @@ -1335,6 +1343,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub taskFragment.setAnimationParams(animationParams); break; } + case OP_TYPE_REORDER_TO_FRONT: { + final Task task = taskFragment.getTask(); + if (task != null) { + final TaskFragment topTaskFragment = task.getTaskFragment( + tf -> tf.asTask() == null); + if (topTaskFragment != null && topTaskFragment != taskFragment) { + final int index = task.mChildren.indexOf(topTaskFragment); + task.mChildren.remove(taskFragment); + task.mChildren.add(index, taskFragment); + } + } + break; + } } return effects; } diff --git a/services/flags/Android.bp b/services/flags/Android.bp deleted file mode 100644 index 29d2b9cfbb98..000000000000 --- a/services/flags/Android.bp +++ /dev/null @@ -1,18 +0,0 @@ -package { - // 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" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -java_library_static { - name: "services.flags", - defaults: ["platform_service_defaults"], - srcs: [ - "java/**/*.java", - ":feature_flags_aidl", - ], - libs: ["services.core"], -} diff --git a/services/flags/OWNERS b/services/flags/OWNERS deleted file mode 100644 index 3925b5c13c2d..000000000000 --- a/services/flags/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -# Bug component: 1306523 - -mankoff@google.com - -pixel@google.com -dsandler@android.com diff --git a/services/flags/java/com/android/server/flags/DynamicFlagBinderDelegate.java b/services/flags/java/com/android/server/flags/DynamicFlagBinderDelegate.java deleted file mode 100644 index 0db328792cf3..000000000000 --- a/services/flags/java/com/android/server/flags/DynamicFlagBinderDelegate.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.flags; - -import android.annotation.NonNull; -import android.flags.IFeatureFlagsCallback; -import android.flags.SyncableFlag; -import android.os.Build; -import android.os.IBinder; -import android.os.RemoteException; -import android.provider.DeviceConfig; -import android.util.Slog; - -import com.android.internal.os.BackgroundThread; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - -/** - * Handles DynamicFlags for {@link FeatureFlagsBinder}. - * - * Dynamic flags are simultaneously simpler and more complicated than process stable flags. We can - * return whatever value is last known for a flag is, without too much worry about the flags - * changing (they are dynamic after all). However, we have to alert all the relevant clients - * about those flag changes, and need to be able to restore to a default value if the flag gets - * reset/erased during runtime. - */ -class DynamicFlagBinderDelegate { - - private final FlagOverrideStore mFlagStore; - private final FlagCache<DynamicFlagData> mDynamicFlags = new FlagCache<>(); - private final Map<Integer, Set<IFeatureFlagsCallback>> mCallbacks = new HashMap<>(); - private static final Function<Integer, Set<IFeatureFlagsCallback>> NEW_CALLBACK_SET = - k -> new HashSet<>(); - - private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener = - new DeviceConfig.OnPropertiesChangedListener() { - @Override - public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { - String ns = properties.getNamespace(); - for (String name : properties.getKeyset()) { - // Don't alert for flags we don't care about. - // Don't alert for flags that have been overridden locally. - if (!mDynamicFlags.contains(ns, name) || mFlagStore.contains(ns, name)) { - continue; - } - mFlagChangeCallback.onFlagChanged( - ns, name, properties.getString(name, null)); - } - } - }; - - private final FlagOverrideStore.FlagChangeCallback mFlagChangeCallback = - (namespace, name, value) -> { - // Don't bother with callbacks for non-dynamic flags. - if (!mDynamicFlags.contains(namespace, name)) { - return; - } - - // Don't bother with callbacks if nothing changed. - // Handling erasure (null) is special, as we may be restoring back to a value - // we were already at. - DynamicFlagData data = mDynamicFlags.getOrNull(namespace, name); - if (data == null) { - return; // shouldn't happen, but better safe than sorry. - } - if (value == null) { - if (data.getValue().equals(data.getDefaultValue())) { - return; - } - value = data.getDefaultValue(); - } else if (data.getValue().equals(value)) { - return; - } - data.setValue(value); - - final Set<IFeatureFlagsCallback> cbCopy; - synchronized (mCallbacks) { - cbCopy = new HashSet<>(); - - for (Integer pid : mCallbacks.keySet()) { - if (data.containsPid(pid)) { - cbCopy.addAll(mCallbacks.get(pid)); - } - } - } - SyncableFlag sFlag = new SyncableFlag(namespace, name, value, true); - cbCopy.forEach(cb -> { - try { - cb.onFlagChange(sFlag); - } catch (RemoteException e) { - Slog.w( - FeatureFlagsService.TAG, - "Failed to communicate flag change to client."); - } - }); - }; - - DynamicFlagBinderDelegate(FlagOverrideStore flagStore) { - mFlagStore = flagStore; - mFlagStore.setChangeCallback(mFlagChangeCallback); - } - - SyncableFlag syncDynamicFlag(int pid, SyncableFlag sf) { - if (!sf.isDynamic()) { - return sf; - } - - String ns = sf.getNamespace(); - String name = sf.getName(); - - // Dynamic flags don't need any special threading or synchronization considerations. - // We simply give them whatever the current value is. - // However, we do need to keep track of dynamic flags, so that we can alert - // about changes coming in from adb, DeviceConfig, or other sources. - // And also so that we can keep flags relatively consistent across processes. - - DynamicFlagData data = mDynamicFlags.getOrNull(ns, name); - String value = getFlagValue(ns, name, sf.getValue()); - // DeviceConfig listeners are per-namespace. - if (!mDynamicFlags.containsNamespace(ns)) { - DeviceConfig.addOnPropertiesChangedListener( - ns, BackgroundThread.getExecutor(), mDeviceConfigListener); - } - data.addClientPid(pid); - data.setValue(value); - // Store the default value so that if an override gets erased, we can restore - // to something. - data.setDefaultValue(sf.getValue()); - - return new SyncableFlag(sf.getNamespace(), sf.getName(), value, true); - } - - - void registerCallback(int pid, IFeatureFlagsCallback callback) { - // Always add callback so that we don't end up with a possible race/leak. - // We remove the callback directly if we fail to call #linkToDeath. - // If we tried to add the callback after we linked, then we could end up in a - // scenario where we link, then the binder dies, firing our BinderGriever which tries - // to remove the callback (which has not yet been added), then finally we add the - // callback, creating a leak. - Set<IFeatureFlagsCallback> callbacks; - synchronized (mCallbacks) { - callbacks = mCallbacks.computeIfAbsent(pid, NEW_CALLBACK_SET); - callbacks.add(callback); - } - try { - callback.asBinder().linkToDeath(new BinderGriever(pid), 0); - } catch (RemoteException e) { - Slog.e( - FeatureFlagsService.TAG, - "Failed to link to binder death. Callback not registered."); - synchronized (mCallbacks) { - callbacks.remove(callback); - } - } - } - - void unregisterCallback(int pid, IFeatureFlagsCallback callback) { - // No need to unlink, since the BinderGriever will essentially be a no-op. - // We would have to track our BinderGriever's in a map otherwise. - synchronized (mCallbacks) { - Set<IFeatureFlagsCallback> callbacks = - mCallbacks.computeIfAbsent(pid, NEW_CALLBACK_SET); - callbacks.remove(callback); - } - } - - String getFlagValue(String namespace, String name, String defaultValue) { - // If we already have a value cached, just use that. - String value = null; - DynamicFlagData data = mDynamicFlags.getOrNull(namespace, name); - if (data != null) { - value = data.getValue(); - } else { - // Put the value in the cache for future reference. - data = new DynamicFlagData(namespace, name); - mDynamicFlags.setIfChanged(namespace, name, data); - } - // If we're not in a release build, flags can be overridden locally on device. - if (!Build.IS_USER && value == null) { - value = mFlagStore.get(namespace, name); - } - // If we still don't have a value, maybe DeviceConfig does? - // Fallback to sf.getValue() here as well. - if (value == null) { - value = DeviceConfig.getString(namespace, name, defaultValue); - } - - return value; - } - - private static class DynamicFlagData { - private final String mNamespace; - private final String mName; - private final Set<Integer> mPids = new HashSet<>(); - private String mValue; - private String mDefaultValue; - - private DynamicFlagData(String namespace, String name) { - mNamespace = namespace; - mName = name; - } - - String getValue() { - return mValue; - } - - void setValue(String value) { - mValue = value; - } - - String getDefaultValue() { - return mDefaultValue; - } - - void setDefaultValue(String value) { - mDefaultValue = value; - } - - void addClientPid(int pid) { - mPids.add(pid); - } - - boolean containsPid(int pid) { - return mPids.contains(pid); - } - - @Override - public boolean equals(Object other) { - if (other == null || !(other instanceof DynamicFlagData)) { - return false; - } - - DynamicFlagData o = (DynamicFlagData) other; - - return mName.equals(o.mName) && mNamespace.equals(o.mNamespace) - && mValue.equals(o.mValue) && mDefaultValue.equals(o.mDefaultValue); - } - - @Override - public int hashCode() { - return mName.hashCode() + mNamespace.hashCode() - + mValue.hashCode() + mDefaultValue.hashCode(); - } - } - - - private class BinderGriever implements IBinder.DeathRecipient { - private final int mPid; - - private BinderGriever(int pid) { - mPid = pid; - } - - @Override - public void binderDied() { - synchronized (mCallbacks) { - mCallbacks.remove(mPid); - } - } - } -} diff --git a/services/flags/java/com/android/server/flags/FeatureFlagsBinder.java b/services/flags/java/com/android/server/flags/FeatureFlagsBinder.java deleted file mode 100644 index 1fa85325aea6..000000000000 --- a/services/flags/java/com/android/server/flags/FeatureFlagsBinder.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.flags; - -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.flags.IFeatureFlags; -import android.flags.IFeatureFlagsCallback; -import android.flags.SyncableFlag; -import android.os.Build; -import android.os.ParcelFileDescriptor; - -import com.android.internal.flags.CoreFlags; -import com.android.server.flags.FeatureFlagsService.PermissionsChecker; - -import java.io.FileOutputStream; -import java.util.ArrayList; -import java.util.List; - -class FeatureFlagsBinder extends IFeatureFlags.Stub { - private final FlagOverrideStore mFlagStore; - private final FlagsShellCommand mShellCommand; - private final FlagCache<String> mFlagCache = new FlagCache<>(); - private final DynamicFlagBinderDelegate mDynamicFlagDelegate; - private final PermissionsChecker mPermissionsChecker; - - FeatureFlagsBinder( - FlagOverrideStore flagStore, - FlagsShellCommand shellCommand, - PermissionsChecker permissionsChecker) { - mFlagStore = flagStore; - mShellCommand = shellCommand; - mDynamicFlagDelegate = new DynamicFlagBinderDelegate(flagStore); - mPermissionsChecker = permissionsChecker; - } - - @Override - public void registerCallback(IFeatureFlagsCallback callback) { - mDynamicFlagDelegate.registerCallback(getCallingPid(), callback); - } - - @Override - public void unregisterCallback(IFeatureFlagsCallback callback) { - mDynamicFlagDelegate.unregisterCallback(getCallingPid(), callback); - } - - // Note: The internals of this method should be kept in sync with queryFlags - // as they both should return identical results. The difference is that this method - // caches any values it receives and/or reads, whereas queryFlags does not. - - @Override - public List<SyncableFlag> syncFlags(List<SyncableFlag> incomingFlags) { - int pid = getCallingPid(); - List<SyncableFlag> outputFlags = new ArrayList<>(); - - boolean hasFullSyncPrivileges = false; - SecurityException permissionFailureException = null; - try { - assertSyncPermission(); - hasFullSyncPrivileges = true; - } catch (SecurityException e) { - permissionFailureException = e; - } - - for (SyncableFlag sf : incomingFlags) { - if (!hasFullSyncPrivileges && !CoreFlags.isCoreFlag(sf)) { - throw permissionFailureException; - } - - String ns = sf.getNamespace(); - String name = sf.getName(); - SyncableFlag outFlag; - if (sf.isDynamic()) { - outFlag = mDynamicFlagDelegate.syncDynamicFlag(pid, sf); - } else { - synchronized (mFlagCache) { - String value = mFlagCache.getOrNull(ns, name); - if (value == null) { - String overrideValue = Build.IS_USER ? null : mFlagStore.get(ns, name); - value = overrideValue != null ? overrideValue : sf.getValue(); - mFlagCache.setIfChanged(ns, name, value); - } - outFlag = new SyncableFlag(sf.getNamespace(), sf.getName(), value, false); - } - } - outputFlags.add(outFlag); - } - return outputFlags; - } - - @Override - public void overrideFlag(SyncableFlag flag) { - assertWritePermission(); - mFlagStore.set(flag.getNamespace(), flag.getName(), flag.getValue()); - } - - @Override - public void resetFlag(SyncableFlag flag) { - assertWritePermission(); - mFlagStore.erase(flag.getNamespace(), flag.getName()); - } - - @Override - public List<SyncableFlag> queryFlags(List<SyncableFlag> incomingFlags) { - assertSyncPermission(); - List<SyncableFlag> outputFlags = new ArrayList<>(); - for (SyncableFlag sf : incomingFlags) { - String ns = sf.getNamespace(); - String name = sf.getName(); - String value; - String storeValue = mFlagStore.get(ns, name); - boolean overridden = storeValue != null; - - if (sf.isDynamic()) { - value = mDynamicFlagDelegate.getFlagValue(ns, name, sf.getValue()); - } else { - value = mFlagCache.getOrNull(ns, name); - if (value == null) { - value = Build.IS_USER ? null : storeValue; - if (value == null) { - value = sf.getValue(); - } - } - } - outputFlags.add(new SyncableFlag( - sf.getNamespace(), sf.getName(), value, sf.isDynamic(), overridden)); - } - - return outputFlags; - } - - private void assertSyncPermission() { - mPermissionsChecker.assertSyncPermission(); - clearCallingIdentity(); - } - - private void assertWritePermission() { - mPermissionsChecker.assertWritePermission(); - clearCallingIdentity(); - } - - - @SystemApi - public int handleShellCommand( - @NonNull ParcelFileDescriptor in, - @NonNull ParcelFileDescriptor out, - @NonNull ParcelFileDescriptor err, - @NonNull String[] args) { - FileOutputStream fout = new FileOutputStream(out.getFileDescriptor()); - FileOutputStream ferr = new FileOutputStream(err.getFileDescriptor()); - - return mShellCommand.process(args, fout, ferr); - } -} diff --git a/services/flags/java/com/android/server/flags/FeatureFlagsService.java b/services/flags/java/com/android/server/flags/FeatureFlagsService.java deleted file mode 100644 index 93b9e9e0dc8c..000000000000 --- a/services/flags/java/com/android/server/flags/FeatureFlagsService.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.server.flags; - -import static android.Manifest.permission.SYNC_FLAGS; -import static android.Manifest.permission.WRITE_FLAGS; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.flags.FeatureFlags; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.SystemService; - -/** - * A service that manages syncing {@link android.flags.FeatureFlags} across processes. - * - * This service holds flags stable for at least the lifetime of a process, meaning that if - * a process comes online with a flag set to true, any other process that connects here and - * tries to read the same flag will also receive the flag as true. The flag will remain stable - * until either all of the interested processes have died, or the device restarts. - * - * TODO(279054964): Add to dumpsys - * @hide - */ -public class FeatureFlagsService extends SystemService { - - static final String TAG = "FeatureFlagsService"; - private final FlagOverrideStore mFlagStore; - private final FlagsShellCommand mShellCommand; - - /** - * Initializes the system service. - * - * @param context The system server context. - */ - public FeatureFlagsService(Context context) { - super(context); - mFlagStore = new FlagOverrideStore( - new GlobalSettingsProxy(context.getContentResolver())); - mShellCommand = new FlagsShellCommand(mFlagStore); - } - - @Override - public void onStart() { - Slog.d(TAG, "Started Feature Flag Service"); - FeatureFlagsBinder service = new FeatureFlagsBinder( - mFlagStore, mShellCommand, new PermissionsChecker(getContext())); - publishBinderService( - Context.FEATURE_FLAGS_SERVICE, service); - publishLocalService(FeatureFlags.class, new FeatureFlags(service)); - } - - @Override - public void onBootPhase(int phase) { - super.onBootPhase(phase); - - if (phase == PHASE_SYSTEM_SERVICES_READY) { - // Immediately sync our core flags so that they get locked in. We don't want third-party - // apps to override them, and syncing immediately is the easiest way to prevent that. - FeatureFlags.getInstance().sync(); - } - } - - /** - * Delegate for checking flag permissions. - */ - @VisibleForTesting - public static class PermissionsChecker { - private final Context mContext; - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public PermissionsChecker(Context context) { - mContext = context; - } - - /** - * Ensures that the caller has {@link SYNC_FLAGS} permission. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public void assertSyncPermission() { - if (mContext.checkCallingOrSelfPermission(SYNC_FLAGS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException( - "Non-core flag queried. Requires SYNC_FLAGS permission!"); - } - } - - /** - * Ensures that the caller has {@link WRITE_FLAGS} permission. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public void assertWritePermission() { - if (mContext.checkCallingPermission(WRITE_FLAGS) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires WRITE_FLAGS permission!"); - } - } - } -} diff --git a/services/flags/java/com/android/server/flags/FlagCache.java b/services/flags/java/com/android/server/flags/FlagCache.java deleted file mode 100644 index cee1578a5dde..000000000000 --- a/services/flags/java/com/android/server/flags/FlagCache.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.flags; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -/** - * Threadsafe cache of values that stores the supplied default on cache miss. - * - * @param <V> The type of value to store. - */ -public class FlagCache<V> { - private final Function<String, HashMap<String, V>> mNewHashMap = k -> new HashMap<>(); - - // Cache is organized first by namespace, then by name. All values are stored as strings. - final Map<String, Map<String, V>> mCache = new HashMap<>(); - - FlagCache() { - } - - /** - * Returns true if the namespace exists in the cache already. - */ - boolean containsNamespace(String namespace) { - synchronized (mCache) { - return mCache.containsKey(namespace); - } - } - - /** - * Returns true if the value is stored in the cache. - */ - boolean contains(String namespace, String name) { - synchronized (mCache) { - Map<String, V> nsCache = mCache.get(namespace); - return nsCache != null && nsCache.containsKey(name); - } - } - - /** - * Sets the value if it is different from what is currently stored. - * - * If the value is not set, or the current value is null, it will store the value and - * return true. - * - * @return True if the value was set. False if the value is the same. - */ - boolean setIfChanged(String namespace, String name, V value) { - synchronized (mCache) { - Map<String, V> nsCache = mCache.computeIfAbsent(namespace, mNewHashMap); - V curValue = nsCache.get(name); - if (curValue == null || !curValue.equals(value)) { - nsCache.put(name, value); - return true; - } - return false; - } - } - - /** - * Gets the current value from the cache, setting it if it is currently absent. - * - * @return The value that is now in the cache after the call to the method. - */ - V getOrSet(String namespace, String name, V defaultValue) { - synchronized (mCache) { - Map<String, V> nsCache = mCache.computeIfAbsent(namespace, mNewHashMap); - V value = nsCache.putIfAbsent(name, defaultValue); - return value == null ? defaultValue : value; - } - } - - /** - * Gets the current value from the cache, returning null if not present. - * - * @return The value that is now in the cache if there is one. - */ - V getOrNull(String namespace, String name) { - synchronized (mCache) { - Map<String, V> nsCache = mCache.get(namespace); - if (nsCache == null) { - return null; - } - return nsCache.get(name); - } - } -} diff --git a/services/flags/java/com/android/server/flags/FlagOverrideStore.java b/services/flags/java/com/android/server/flags/FlagOverrideStore.java deleted file mode 100644 index b1ddc7e67f68..000000000000 --- a/services/flags/java/com/android/server/flags/FlagOverrideStore.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.flags; - -import android.database.Cursor; -import android.provider.Settings; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.HashMap; -import java.util.Map; - -/** - * Persistent storage for the {@link FeatureFlagsService}. - * - * The implementation stores data in Settings.<store> (generally {@link Settings.Global} - * is expected). - */ -@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) -public class FlagOverrideStore { - private static final String KEYNAME_PREFIX = "flag|"; - private static final String NAMESPACE_NAME_SEPARATOR = "."; - - private final SettingsProxy mSettingsProxy; - - private FlagChangeCallback mCallback; - - FlagOverrideStore(SettingsProxy settingsProxy) { - mSettingsProxy = settingsProxy; - } - - void setChangeCallback(FlagChangeCallback callback) { - mCallback = callback; - } - - /** Returns true if a non-null value is in the store. */ - boolean contains(String namespace, String name) { - return get(namespace, name) != null; - } - - /** Put a value in the store. */ - @VisibleForTesting - public void set(String namespace, String name, String value) { - mSettingsProxy.putString(getPropName(namespace, name), value); - mCallback.onFlagChanged(namespace, name, value); - } - - /** Read a value out of the store. */ - @VisibleForTesting - public String get(String namespace, String name) { - return mSettingsProxy.getString(getPropName(namespace, name)); - } - - /** Erase a value from the store. */ - @VisibleForTesting - public void erase(String namespace, String name) { - set(namespace, name, null); - } - - Map<String, Map<String, String>> getFlags() { - return getFlagsForNamespace(null); - } - - Map<String, Map<String, String>> getFlagsForNamespace(String namespace) { - Cursor c = mSettingsProxy.getContentResolver().query( - Settings.Global.CONTENT_URI, - new String[]{Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE}, - null, // Doesn't support a "LIKE" query - null, - null - ); - - if (c == null) { - return Map.of(); - } - int keynamePrefixLength = KEYNAME_PREFIX.length(); - Map<String, Map<String, String>> results = new HashMap<>(); - while (c.moveToNext()) { - String key = c.getString(0); - if (!key.startsWith(KEYNAME_PREFIX) - || key.indexOf(NAMESPACE_NAME_SEPARATOR, keynamePrefixLength) < 0) { - continue; - } - String value = c.getString(1); - if (value == null || value.isEmpty()) { - continue; - } - String ns = key.substring(keynamePrefixLength, key.indexOf(NAMESPACE_NAME_SEPARATOR)); - if (namespace != null && !namespace.equals(ns)) { - continue; - } - String name = key.substring(key.indexOf(NAMESPACE_NAME_SEPARATOR) + 1); - results.putIfAbsent(ns, new HashMap<>()); - results.get(ns).put(name, value); - } - c.close(); - return results; - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - static String getPropName(String namespace, String name) { - return KEYNAME_PREFIX + namespace + NAMESPACE_NAME_SEPARATOR + name; - } - - interface FlagChangeCallback { - void onFlagChanged(String namespace, String name, String value); - } -} diff --git a/services/flags/java/com/android/server/flags/FlagsShellCommand.java b/services/flags/java/com/android/server/flags/FlagsShellCommand.java deleted file mode 100644 index b7896ee18714..000000000000 --- a/services/flags/java/com/android/server/flags/FlagsShellCommand.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.flags; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastPrintWriter; - -import java.io.OutputStream; -import java.io.PrintWriter; -import java.util.Locale; -import java.util.Map; - -/** - * Process command line input for the flags service. - */ -@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) -public class FlagsShellCommand { - private final FlagOverrideStore mFlagStore; - - FlagsShellCommand(FlagOverrideStore flagStore) { - mFlagStore = flagStore; - } - - /** - * Interpret the command supplied in the constructor. - * - * @return Zero on success or non-zero on error. - */ - public int process( - String[] args, - OutputStream out, - OutputStream err) { - PrintWriter outPw = new FastPrintWriter(out); - PrintWriter errPw = new FastPrintWriter(err); - - if (args.length == 0) { - return printHelp(outPw); - } - switch (args[0].toLowerCase(Locale.ROOT)) { - case "help": - return printHelp(outPw); - case "list": - return listCmd(args, outPw, errPw); - case "set": - return setCmd(args, outPw, errPw); - case "get": - return getCmd(args, outPw, errPw); - case "erase": - return eraseCmd(args, outPw, errPw); - default: - return unknownCmd(outPw); - } - } - - private int printHelp(PrintWriter outPw) { - outPw.println("Feature Flags command, allowing listing, setting, getting, and erasing of"); - outPw.println("local flag overrides on a device."); - outPw.println(); - outPw.println("Commands:"); - outPw.println(" list [namespace]"); - outPw.println(" List all flag overrides. Namespace is optional."); - outPw.println(); - outPw.println(" get <namespace> <name>"); - outPw.println(" Return the string value of a specific flag, or <unset>"); - outPw.println(); - outPw.println(" set <namespace> <name> <value>"); - outPw.println(" Set a specific flag"); - outPw.println(); - outPw.println(" erase <namespace> <name>"); - outPw.println(" Unset a specific flag"); - outPw.flush(); - return 0; - } - - private int listCmd(String[] args, PrintWriter outPw, PrintWriter errPw) { - if (!validateNumArguments(args, 0, 1, args[0], errPw)) { - errPw.println("Expected `" + args[0] + " [namespace]`"); - errPw.flush(); - return -1; - } - Map<String, Map<String, String>> overrides; - if (args.length == 2) { - overrides = mFlagStore.getFlagsForNamespace(args[1]); - } else { - overrides = mFlagStore.getFlags(); - } - if (overrides.isEmpty()) { - outPw.println("No overrides set"); - } else { - int longestNamespaceLen = "namespace".length(); - int longestFlagLen = "flag".length(); - int longestValLen = "value".length(); - for (Map.Entry<String, Map<String, String>> namespace : overrides.entrySet()) { - longestNamespaceLen = Math.max(longestNamespaceLen, namespace.getKey().length()); - for (Map.Entry<String, String> flag : namespace.getValue().entrySet()) { - longestFlagLen = Math.max(longestFlagLen, flag.getKey().length()); - longestValLen = Math.max(longestValLen, flag.getValue().length()); - } - } - outPw.print(String.format("%-" + longestNamespaceLen + "s", "namespace")); - outPw.print(' '); - outPw.print(String.format("%-" + longestFlagLen + "s", "flag")); - outPw.print(' '); - outPw.println("value"); - for (int i = 0; i < longestNamespaceLen; i++) { - outPw.print('='); - } - outPw.print(' '); - for (int i = 0; i < longestFlagLen; i++) { - outPw.print('='); - } - outPw.print(' '); - for (int i = 0; i < longestValLen; i++) { - outPw.print('='); - } - outPw.println(); - for (Map.Entry<String, Map<String, String>> namespace : overrides.entrySet()) { - for (Map.Entry<String, String> flag : namespace.getValue().entrySet()) { - outPw.print( - String.format("%-" + longestNamespaceLen + "s", namespace.getKey())); - outPw.print(' '); - outPw.print(String.format("%-" + longestFlagLen + "s", flag.getKey())); - outPw.print(' '); - outPw.println(flag.getValue()); - } - } - } - outPw.flush(); - return 0; - } - - private int setCmd(String[] args, PrintWriter outPw, PrintWriter errPw) { - if (!validateNumArguments(args, 3, args[0], errPw)) { - errPw.println("Expected `" + args[0] + " <namespace> <name> <value>`"); - errPw.flush(); - return -1; - } - mFlagStore.set(args[1], args[2], args[3]); - outPw.println("Flag " + args[1] + "." + args[2] + " is now " + args[3]); - outPw.flush(); - return 0; - } - - private int getCmd(String[] args, PrintWriter outPw, PrintWriter errPw) { - if (!validateNumArguments(args, 2, args[0], errPw)) { - errPw.println("Expected `" + args[0] + " <namespace> <name>`"); - errPw.flush(); - return -1; - } - - String value = mFlagStore.get(args[1], args[2]); - outPw.print(args[1] + "." + args[2] + " is "); - if (value == null || value.isEmpty()) { - outPw.println("<unset>"); - } else { - outPw.println("\"" + value.translateEscapes() + "\""); - } - outPw.flush(); - return 0; - } - - private int eraseCmd(String[] args, PrintWriter outPw, PrintWriter errPw) { - if (!validateNumArguments(args, 2, args[0], errPw)) { - errPw.println("Expected `" + args[0] + " <namespace> <name>`"); - errPw.flush(); - return -1; - } - mFlagStore.erase(args[1], args[2]); - outPw.println("Erased " + args[1] + "." + args[2]); - return 0; - } - - private int unknownCmd(PrintWriter outPw) { - outPw.println("This command is unknown."); - printHelp(outPw); - outPw.flush(); - return -1; - } - - private boolean validateNumArguments( - String[] args, int exactly, String cmdName, PrintWriter errPw) { - return validateNumArguments(args, exactly, exactly, cmdName, errPw); - } - - private boolean validateNumArguments( - String[] args, int min, int max, String cmdName, PrintWriter errPw) { - int len = args.length - 1; // Discount the command itself. - if (len < min) { - errPw.println( - "Less than " + min + " arguments provided for \"" + cmdName + "\" command."); - return false; - } else if (len > max) { - errPw.println( - "More than " + max + " arguments provided for \"" + cmdName + "\" command."); - return false; - } - - return true; - } -} diff --git a/services/flags/java/com/android/server/flags/GlobalSettingsProxy.java b/services/flags/java/com/android/server/flags/GlobalSettingsProxy.java deleted file mode 100644 index acb7bb5a49db..000000000000 --- a/services/flags/java/com/android/server/flags/GlobalSettingsProxy.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.flags; - -import android.content.ContentResolver; -import android.net.Uri; -import android.provider.Settings; - -class GlobalSettingsProxy implements SettingsProxy { - private final ContentResolver mContentResolver; - - GlobalSettingsProxy(ContentResolver contentResolver) { - mContentResolver = contentResolver; - } - - @Override - public ContentResolver getContentResolver() { - return mContentResolver; - } - - @Override - public Uri getUriFor(String name) { - return Settings.Global.getUriFor(name); - } - - @Override - public String getStringForUser(String name, int userHandle) { - return Settings.Global.getStringForUser(mContentResolver, name, userHandle); - } - - @Override - public boolean putString(String name, String value, boolean overrideableByRestore) { - throw new UnsupportedOperationException( - "This method only exists publicly for Settings.System and Settings.Secure"); - } - - @Override - public boolean putStringForUser(String name, String value, int userHandle) { - return Settings.Global.putStringForUser(mContentResolver, name, value, userHandle); - } - - @Override - public boolean putStringForUser(String name, String value, String tag, boolean makeDefault, - int userHandle, boolean overrideableByRestore) { - return Settings.Global.putStringForUser( - mContentResolver, name, value, tag, makeDefault, userHandle, - overrideableByRestore); - } - - @Override - public boolean putString(String name, String value, String tag, boolean makeDefault) { - return Settings.Global.putString(mContentResolver, name, value, tag, makeDefault); - } -} diff --git a/services/flags/java/com/android/server/flags/SettingsProxy.java b/services/flags/java/com/android/server/flags/SettingsProxy.java deleted file mode 100644 index c6e85d5d1dc8..000000000000 --- a/services/flags/java/com/android/server/flags/SettingsProxy.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.flags; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.content.ContentResolver; -import android.database.ContentObserver; -import android.net.Uri; -import android.provider.Settings; - -/** - * Wrapper class meant to enable hermetic testing of {@link Settings}. - * - * Implementations of this class are expected to be constructed with a {@link ContentResolver} or, - * otherwise have access to an implicit one. All the proxy methods in this class exclude - * {@link ContentResolver} from their signature and rely on an internally defined one instead. - * - * Most methods in the {@link Settings} classes have default implementations defined. - * Implementations of this interfac need only concern themselves with getting and putting Strings. - * They should also override any methods for a class they are proxying that _are not_ defined, and - * throw an appropriate {@link UnsupportedOperationException}. For instance, {@link Settings.Global} - * does not define {@link #putString(String, String, boolean)}, so an implementation of this - * interface that proxies through to it should throw an exception when that method is called. - * - * This class adds in the following helpers as well: - * - {@link #getBool(String)} - * - {@link #putBool(String, boolean)} - * - {@link #registerContentObserver(Uri, ContentObserver)} - * - * ... and similar variations for all of those. - */ -public interface SettingsProxy { - - /** - * Returns the {@link ContentResolver} this instance uses. - */ - ContentResolver getContentResolver(); - - /** - * Construct the content URI for a particular name/value pair, - * useful for monitoring changes with a ContentObserver. - * @param name to look up in the table - * @return the corresponding content URI, or null if not present - */ - Uri getUriFor(String name); - - /**See {@link Settings.Secure#getString(ContentResolver, String)} */ - String getStringForUser(String name, int userHandle); - - /**See {@link Settings.Secure#putString(ContentResolver, String, String, boolean)} */ - boolean putString(String name, String value, boolean overrideableByRestore); - - /** See {@link Settings.Secure#putStringForUser(ContentResolver, String, String, int)} */ - boolean putStringForUser(String name, String value, int userHandle); - - /** - * See {@link Settings.Secure#putStringForUser(ContentResolver, String, String, String, boolean, - * int, boolean)} - */ - boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag, - boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore); - - /** See {@link Settings.Secure#putString(ContentResolver, String, String, String, boolean)} */ - boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag, - boolean makeDefault); - - /** - * Returns the user id for the associated {@link ContentResolver}. - */ - default int getUserId() { - return getContentResolver().getUserId(); - } - - /** See {@link Settings.Secure#getString(ContentResolver, String)} */ - default String getString(String name) { - return getStringForUser(name, getUserId()); - } - - /** See {@link Settings.Secure#putString(ContentResolver, String, String)} */ - default boolean putString(String name, String value) { - return putStringForUser(name, value, getUserId()); - } - /** See {@link Settings.Secure#getIntForUser(ContentResolver, String, int, int)} */ - default int getIntForUser(String name, int def, int userHandle) { - String v = getStringForUser(name, userHandle); - try { - return v != null ? Integer.parseInt(v) : def; - } catch (NumberFormatException e) { - return def; - } - } - - /** See {@link Settings.Secure#getInt(ContentResolver, String)} */ - default int getInt(String name) throws Settings.SettingNotFoundException { - return getIntForUser(name, getUserId()); - } - - /** See {@link Settings.Secure#getIntForUser(ContentResolver, String, int)} */ - default int getIntForUser(String name, int userHandle) - throws Settings.SettingNotFoundException { - String v = getStringForUser(name, userHandle); - try { - return Integer.parseInt(v); - } catch (NumberFormatException e) { - throw new Settings.SettingNotFoundException(name); - } - } - - /** See {@link Settings.Secure#putInt(ContentResolver, String, int)} */ - default boolean putInt(String name, int value) { - return putIntForUser(name, value, getUserId()); - } - - /** See {@link Settings.Secure#putIntForUser(ContentResolver, String, int, int)} */ - default boolean putIntForUser(String name, int value, int userHandle) { - return putStringForUser(name, Integer.toString(value), userHandle); - } - - /** - * Convenience function for retrieving a single settings value - * as a boolean. Note that internally setting values are always - * stored as strings; this function converts the string to a boolean - * for you. The default value will be returned if the setting is - * not defined or not a boolean. - * - * @param name The name of the setting to retrieve. - * @param def Value to return if the setting is not defined. - * - * @return The setting's current value, or 'def' if it is not defined - * or not a valid boolean. - */ - default boolean getBool(String name, boolean def) { - return getBoolForUser(name, def, getUserId()); - } - - /** See {@link #getBool(String, boolean)}. */ - default boolean getBoolForUser(String name, boolean def, int userHandle) { - return getIntForUser(name, def ? 1 : 0, userHandle) != 0; - } - - /** - * Convenience function for retrieving a single settings value - * as a boolean. Note that internally setting values are always - * stored as strings; this function converts the string to a boolean - * for you. - * <p> - * This version does not take a default value. If the setting has not - * been set, or the string value is not a number, - * it throws {@link Settings.SettingNotFoundException}. - * - * @param name The name of the setting to retrieve. - * - * @throws Settings.SettingNotFoundException Thrown if a setting by the given - * name can't be found or the setting value is not a boolean. - * - * @return The setting's current value. - */ - default boolean getBool(String name) throws Settings.SettingNotFoundException { - return getBoolForUser(name, getUserId()); - } - - /** See {@link #getBool(String)}. */ - default boolean getBoolForUser(String name, int userHandle) - throws Settings.SettingNotFoundException { - return getIntForUser(name, userHandle) != 0; - } - - /** - * Convenience function for updating a single settings value as a - * boolean. This will either create a new entry in the table if the - * given name does not exist, or modify the value of the existing row - * with that name. Note that internally setting values are always - * stored as strings, so this function converts the given value to a - * string before storing it. - * - * @param name The name of the setting to modify. - * @param value The new value for the setting. - * @return true if the value was set, false on database errors - */ - default boolean putBool(String name, boolean value) { - return putBoolForUser(name, value, getUserId()); - } - - /** See {@link #putBool(String, boolean)}. */ - default boolean putBoolForUser(String name, boolean value, int userHandle) { - return putIntForUser(name, value ? 1 : 0, userHandle); - } - - /** See {@link Settings.Secure#getLong(ContentResolver, String, long)} */ - default long getLong(String name, long def) { - return getLongForUser(name, def, getUserId()); - } - - /** See {@link Settings.Secure#getLongForUser(ContentResolver, String, long, int)} */ - default long getLongForUser(String name, long def, int userHandle) { - String valString = getStringForUser(name, userHandle); - long value; - try { - value = valString != null ? Long.parseLong(valString) : def; - } catch (NumberFormatException e) { - value = def; - } - return value; - } - - /** See {@link Settings.Secure#getLong(ContentResolver, String)} */ - default long getLong(String name) throws Settings.SettingNotFoundException { - return getLongForUser(name, getUserId()); - } - - /** See {@link Settings.Secure#getLongForUser(ContentResolver, String, int)} */ - default long getLongForUser(String name, int userHandle) - throws Settings.SettingNotFoundException { - String valString = getStringForUser(name, userHandle); - try { - return Long.parseLong(valString); - } catch (NumberFormatException e) { - throw new Settings.SettingNotFoundException(name); - } - } - - /** See {@link Settings.Secure#putLong(ContentResolver, String, long)} */ - default boolean putLong(String name, long value) { - return putLongForUser(name, value, getUserId()); - } - - /** See {@link Settings.Secure#putLongForUser(ContentResolver, String, long, int)} */ - default boolean putLongForUser(String name, long value, int userHandle) { - return putStringForUser(name, Long.toString(value), userHandle); - } - - /** See {@link Settings.Secure#getFloat(ContentResolver, String, float)} */ - default float getFloat(String name, float def) { - return getFloatForUser(name, def, getUserId()); - } - - /** See {@link Settings.Secure#getFloatForUser(ContentResolver, String, int)} */ - default float getFloatForUser(String name, float def, int userHandle) { - String v = getStringForUser(name, userHandle); - try { - return v != null ? Float.parseFloat(v) : def; - } catch (NumberFormatException e) { - return def; - } - } - - - /** See {@link Settings.Secure#getFloat(ContentResolver, String)} */ - default float getFloat(String name) throws Settings.SettingNotFoundException { - return getFloatForUser(name, getUserId()); - } - - /** See {@link Settings.Secure#getFloatForUser(ContentResolver, String, int)} */ - default float getFloatForUser(String name, int userHandle) - throws Settings.SettingNotFoundException { - String v = getStringForUser(name, userHandle); - if (v == null) { - throw new Settings.SettingNotFoundException(name); - } - try { - return Float.parseFloat(v); - } catch (NumberFormatException e) { - throw new Settings.SettingNotFoundException(name); - } - } - - /** See {@link Settings.Secure#putFloat(ContentResolver, String, float)} */ - default boolean putFloat(String name, float value) { - return putFloatForUser(name, value, getUserId()); - } - - /** See {@link Settings.Secure#putFloatForUser(ContentResolver, String, float, int)} */ - default boolean putFloatForUser(String name, float value, int userHandle) { - return putStringForUser(name, Float.toString(value), userHandle); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.' - * - * Implicitly calls {@link #getUriFor(String)} on the passed in name. - */ - default void registerContentObserver(String name, ContentObserver settingsObserver) { - registerContentObserver(getUriFor(name), settingsObserver); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.' - */ - default void registerContentObserver(Uri uri, ContentObserver settingsObserver) { - registerContentObserverForUser(uri, settingsObserver, getUserId()); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}. - * - * Implicitly calls {@link #getUriFor(String)} on the passed in name. - */ - default void registerContentObserver(String name, boolean notifyForDescendants, - ContentObserver settingsObserver) { - registerContentObserver(getUriFor(name), notifyForDescendants, settingsObserver); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.' - */ - default void registerContentObserver(Uri uri, boolean notifyForDescendants, - ContentObserver settingsObserver) { - registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, getUserId()); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} - * - * Implicitly calls {@link #getUriFor(String)} on the passed in name. - */ - default void registerContentObserverForUser( - String name, ContentObserver settingsObserver, int userHandle) { - registerContentObserverForUser( - getUriFor(name), settingsObserver, userHandle); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} - */ - default void registerContentObserverForUser( - Uri uri, ContentObserver settingsObserver, int userHandle) { - registerContentObserverForUser( - uri, false, settingsObserver, userHandle); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} - * - * Implicitly calls {@link #getUriFor(String)} on the passed in name. - */ - default void registerContentObserverForUser( - String name, boolean notifyForDescendants, ContentObserver settingsObserver, - int userHandle) { - registerContentObserverForUser( - getUriFor(name), notifyForDescendants, settingsObserver, userHandle); - } - - /** - * Convenience wrapper around - * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} - */ - default void registerContentObserverForUser( - Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver, - int userHandle) { - getContentResolver().registerContentObserver( - uri, notifyForDescendants, settingsObserver, userHandle); - } - - /** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */ - default void unregisterContentObserver(ContentObserver settingsObserver) { - getContentResolver().unregisterContentObserver(settingsObserver); - } -} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 7de12790e935..6960da74dd1a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -132,7 +132,6 @@ import com.android.server.display.DisplayManagerService; import com.android.server.display.color.ColorDisplayService; import com.android.server.dreams.DreamManagerService; import com.android.server.emergency.EmergencyAffordanceService; -import com.android.server.flags.FeatureFlagsService; import com.android.server.gpu.GpuService; import com.android.server.grammaticalinflection.GrammaticalInflectionService; import com.android.server.graphics.fonts.FontManagerService; @@ -435,6 +434,10 @@ public final class SystemServer implements Dumpable { + "OnDevicePersonalizationSystemService$Lifecycle"; private static final String UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS = "com.android.server.deviceconfig.DeviceConfigInit$Lifecycle"; + private static final String DEVICE_LOCK_SERVICE_CLASS = + "com.android.server.devicelock.DeviceLockService"; + private static final String DEVICE_LOCK_APEX_PATH = + "/apex/com.android.devicelock/javalib/service-devicelock.jar"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; @@ -1112,12 +1115,6 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class); t.traceEnd(); - // Starts a service for reading runtime flag overrides, and keeping processes - // in sync with one another. - t.traceBegin("StartFeatureFlagsService"); - mSystemServiceManager.startService(FeatureFlagsService.class); - t.traceEnd(); - // Uri Grants Manager. t.traceBegin("UriGrantsManagerService"); mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class); @@ -2871,6 +2868,13 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(HEALTHCONNECT_MANAGER_SERVICE_CLASS); t.traceEnd(); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_LOCK)) { + t.traceBegin("DeviceLockService"); + mSystemServiceManager.startServiceFromJar(DEVICE_LOCK_SERVICE_CLASS, + DEVICE_LOCK_APEX_PATH); + t.traceEnd(); + } + // These are needed to propagate to the runnable below. final NetworkManagementService networkManagementF = networkManagement; final NetworkPolicyManagerService networkPolicyF = networkPolicy; diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index 4aba30a661ad..f660b42457db 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -16,7 +16,9 @@ package com.android.server.midi; +import android.Manifest; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; import android.content.BroadcastReceiver; @@ -48,6 +50,7 @@ import android.os.ParcelUuid; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.util.EventLog; import android.util.Log; @@ -81,6 +84,11 @@ import java.util.concurrent.atomic.AtomicLong; // 2. synchronized (mDeviceConnections) //TODO Introduce a single lock object to lock the whole state and avoid the requirement above. +// All users should be able to connect to USB and Bluetooth MIDI devices. +// All users can create can install an app that provides, a Virtual MIDI Device Service. +// Users can not open virtual MIDI devices created by other users. +// getDevices() surfaces devices that can be opened by that user. +// openDevice() rejects devices that are cannot be opened by that user. public class MidiService extends IMidiManager.Stub { public static class Lifecycle extends SystemService { @@ -97,10 +105,21 @@ public class MidiService extends IMidiManager.Stub { } @Override + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) + public void onUserStarting(@NonNull TargetUser user) { + mMidiService.onStartOrUnlockUser(user, false /* matchDirectBootUnaware */); + } + + @Override + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) public void onUserUnlocking(@NonNull TargetUser user) { - if (user.getUserIdentifier() == UserHandle.USER_SYSTEM) { - mMidiService.onUnlockUser(); - } + mMidiService.onStartOrUnlockUser(user, true /* matchDirectBootUnaware */); } } @@ -134,6 +153,7 @@ public class MidiService extends IMidiManager.Stub { private int mNextDeviceId = 1; private final PackageManager mPackageManager; + private final UserManager mUserManager; private static final String MIDI_LEGACY_STRING = "MIDI 1.0"; private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0"; @@ -159,21 +179,24 @@ public class MidiService extends IMidiManager.Stub { private final HashSet<ParcelUuid> mNonMidiUUIDs = new HashSet<ParcelUuid>(); // PackageMonitor for listening to package changes + // uid is the uid of the package so use getChangingUserId() to fetch the userId. private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) public void onPackageAdded(String packageName, int uid) { - addPackageDeviceServers(packageName); + addPackageDeviceServers(packageName, getChangingUserId()); } @Override + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) public void onPackageModified(String packageName) { - removePackageDeviceServers(packageName); - addPackageDeviceServers(packageName); + removePackageDeviceServers(packageName, getChangingUserId()); + addPackageDeviceServers(packageName, getChangingUserId()); } @Override public void onPackageRemoved(String packageName, int uid) { - removePackageDeviceServers(packageName); + removePackageDeviceServers(packageName, getChangingUserId()); } }; @@ -202,6 +225,10 @@ public class MidiService extends IMidiManager.Stub { return mUid; } + private int getUserId() { + return UserHandle.getUserId(mUid); + } + public void addListener(IMidiDeviceListener listener) { if (mListeners.size() >= MAX_LISTENERS_PER_CLIENT) { throw new SecurityException( @@ -219,8 +246,12 @@ public class MidiService extends IMidiManager.Stub { } } - public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback) { - Log.d(TAG, "addDeviceConnection() device:" + device); + @RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_PROFILES}) + public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback, + int userId) { + Log.d(TAG, "addDeviceConnection() device:" + device + " userId:" + userId); if (mDeviceConnections.size() >= MAX_CONNECTIONS_PER_CLIENT) { Log.i(TAG, "too many MIDI connections for UID = " + mUid); throw new SecurityException( @@ -228,7 +259,7 @@ public class MidiService extends IMidiManager.Stub { } DeviceConnection connection = new DeviceConnection(device, this, callback); mDeviceConnections.put(connection.getToken(), connection); - device.addDeviceConnection(connection); + device.addDeviceConnection(connection, userId); } // called from MidiService.closeDevice() @@ -251,8 +282,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceAdded(Device device) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; MidiDeviceInfo deviceInfo = device.getDeviceInfo(); try { @@ -265,8 +296,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceRemoved(Device device) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; MidiDeviceInfo deviceInfo = device.getDeviceInfo(); try { @@ -279,8 +310,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceStatusChanged(Device device, MidiDeviceStatus status) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; try { for (IMidiDeviceListener listener : mListeners.values()) { @@ -354,6 +385,8 @@ public class MidiService extends IMidiManager.Stub { private final ServiceInfo mServiceInfo; // UID of device implementation private final int mUid; + // User Id of the app. Only used for virtual devices + private final int mUserId; // ServiceConnection for implementing Service (virtual devices only) // mServiceConnection is non-null when connected or attempting to connect to the service @@ -375,19 +408,24 @@ public class MidiService extends IMidiManager.Stub { private AtomicInteger mTotalOutputBytes = new AtomicInteger(); public Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo, - ServiceInfo serviceInfo, int uid) { + ServiceInfo serviceInfo, int uid, int userId) { mDeviceInfo = deviceInfo; mServiceInfo = serviceInfo; mUid = uid; + mUserId = userId; mBluetoothDevice = (BluetoothDevice)deviceInfo.getProperties().getParcelable( MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE, android.bluetooth.BluetoothDevice.class);; setDeviceServer(server); } + @RequiresPermission(anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) public Device(BluetoothDevice bluetoothDevice) { mBluetoothDevice = bluetoothDevice; mServiceInfo = null; mUid = mBluetoothServiceUid; + mUserId = mUserManager.getMainUser().getIdentifier(); } private void setDeviceServer(IMidiDeviceServer server) { @@ -468,11 +506,22 @@ public class MidiService extends IMidiManager.Stub { return mUid; } + public int getUserId() { + return mUserId; + } + public boolean isUidAllowed(int uid) { return (!mDeviceInfo.isPrivate() || mUid == uid); } - public void addDeviceConnection(DeviceConnection connection) { + public boolean isUserIdAllowed(int userId) { + return (mDeviceInfo.getType() != MidiDeviceInfo.TYPE_VIRTUAL || mUserId == userId); + } + + @RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_PROFILES}) + public void addDeviceConnection(DeviceConnection connection, int userId) { Log.d(TAG, "addDeviceConnection() [A] connection:" + connection); synchronized (mDeviceConnections) { mDeviceConnectionsAdded.incrementAndGet(); @@ -537,8 +586,8 @@ public class MidiService extends IMidiManager.Stub { new ComponentName(mServiceInfo.packageName, mServiceInfo.name)); } - if (!mContext.bindService(intent, mServiceConnection, - Context.BIND_AUTO_CREATE)) { + if (!mContext.bindServiceAsUser(intent, mServiceConnection, + Context.BIND_AUTO_CREATE, UserHandle.of(mUserId))) { Log.e(TAG, "Unable to bind service: " + intent); setDeviceServer(null); mServiceConnection = null; @@ -886,6 +935,8 @@ public class MidiService extends IMidiManager.Stub { public MidiService(Context context) { mContext = context; mPackageManager = context.getPackageManager(); + mUserManager = mContext.getSystemService(UserManager.class); + mPackageMonitor.register(mContext, null, UserHandle.ALL, true); // TEMPORARY - Disable BTL-MIDI //FIXME - b/25689266 @@ -913,32 +964,41 @@ public class MidiService extends IMidiManager.Stub { // mNonMidiUUIDs.add(BluetoothUuid.BATTERY); } - private void onUnlockUser() { - mPackageMonitor.register(mContext, null, true); - + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) + private void onStartOrUnlockUser(TargetUser user, boolean matchDirectBootUnaware) { + Log.d(TAG, "onStartOrUnlockUser " + user.getUserIdentifier() + " matchDirectBootUnaware: " + + matchDirectBootUnaware); Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE); - List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServices(intent, - PackageManager.GET_META_DATA); + int resolveFlags = PackageManager.GET_META_DATA; + if (matchDirectBootUnaware) { + resolveFlags |= PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + } + List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServicesAsUser(intent, + resolveFlags, user.getUserIdentifier()); if (resolveInfos != null) { int count = resolveInfos.size(); for (int i = 0; i < count; i++) { ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo; if (serviceInfo != null) { - addPackageDeviceServer(serviceInfo); + addPackageDeviceServer(serviceInfo, user.getUserIdentifier()); } } } - PackageInfo info; - try { - info = mPackageManager.getPackageInfo(MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0); - } catch (PackageManager.NameNotFoundException e) { - info = null; - } - if (info != null && info.applicationInfo != null) { - mBluetoothServiceUid = info.applicationInfo.uid; - } else { - mBluetoothServiceUid = -1; + if (user.getUserIdentifier() == mUserManager.getMainUser().getIdentifier()) { + PackageInfo info; + try { + info = mPackageManager.getPackageInfoAsUser( + MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0, user.getUserIdentifier()); + } catch (PackageManager.NameNotFoundException e) { + info = null; + } + if (info != null && info.applicationInfo != null) { + mBluetoothServiceUid = info.applicationInfo.uid; + } } } @@ -960,10 +1020,11 @@ public class MidiService extends IMidiManager.Stub { // Inform listener of the status of all known devices. private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) { + int userId = UserHandle.getUserId(uid); synchronized (mDevicesByInfo) { for (Device device : mDevicesByInfo.values()) { - // ignore private devices that our client cannot access - if (device.isUidAllowed(uid)) { + // ignore devices that our client cannot access + if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) { try { MidiDeviceStatus status = device.getDeviceStatus(); if (status != null) { @@ -989,10 +1050,11 @@ public class MidiService extends IMidiManager.Stub { public MidiDeviceInfo[] getDevicesForTransport(int transport) { ArrayList<MidiDeviceInfo> deviceInfos = new ArrayList<MidiDeviceInfo>(); int uid = Binder.getCallingUid(); + int userId = getCallingUserId(); synchronized (mDevicesByInfo) { for (Device device : mDevicesByInfo.values()) { - if (device.isUidAllowed(uid)) { + if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) { // UMP devices have protocols that are not PROTOCOL_UNKNOWN if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) { if (device.getDeviceInfo().getDefaultProtocol() @@ -1029,6 +1091,9 @@ public class MidiService extends IMidiManager.Stub { if (!device.isUidAllowed(Binder.getCallingUid())) { throw new SecurityException("Attempt to open private device with wrong UID"); } + if (!device.isUserIdAllowed(getCallingUserId())) { + throw new SecurityException("Attempt to open virtual device with wrong user id"); + } } if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) { @@ -1044,7 +1109,7 @@ public class MidiService extends IMidiManager.Stub { final long identity = Binder.clearCallingIdentity(); try { Log.i(TAG, "addDeviceConnection() [B] device:" + device); - client.addDeviceConnection(device, callback); + client.addDeviceConnection(device, callback, getCallingUserId()); } finally { Binder.restoreCallingIdentity(identity); } @@ -1106,7 +1171,7 @@ public class MidiService extends IMidiManager.Stub { final long identity = Binder.clearCallingIdentity(); try { Log.i(TAG, "addDeviceConnection() [C] device:" + device); - client.addDeviceConnection(device, callback); + client.addDeviceConnection(device, callback, getCallingUserId()); } finally { Binder.restoreCallingIdentity(identity); } @@ -1124,6 +1189,7 @@ public class MidiService extends IMidiManager.Stub { int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, int type, int defaultProtocol) { int uid = Binder.getCallingUid(); + int userId = getCallingUserId(); if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) { throw new SecurityException("only system can create USB devices"); } else if (type == MidiDeviceInfo.TYPE_BLUETOOTH && uid != mBluetoothServiceUid) { @@ -1133,7 +1199,7 @@ public class MidiService extends IMidiManager.Stub { synchronized (mDevicesByInfo) { return addDeviceLocked(type, numInputPorts, numOutputPorts, inputPortNames, outputPortNames, properties, server, null, false, uid, - defaultProtocol); + defaultProtocol, userId); } } @@ -1210,7 +1276,8 @@ public class MidiService extends IMidiManager.Stub { private MidiDeviceInfo addDeviceLocked(int type, int numInputPorts, int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, IMidiDeviceServer server, ServiceInfo serviceInfo, - boolean isPrivate, int uid, int defaultProtocol) { + boolean isPrivate, int uid, int defaultProtocol, int userId) { + Log.d(TAG, "addDeviceLocked()" + uid + " type:" + type); // Limit the number of devices per app. int deviceCountForApp = 0; @@ -1250,7 +1317,7 @@ public class MidiService extends IMidiManager.Stub { } } if (device == null) { - device = new Device(server, deviceInfo, serviceInfo, uid); + device = new Device(server, deviceInfo, serviceInfo, uid, userId); } mDevicesByInfo.put(deviceInfo, device); if (bluetoothDevice != null) { @@ -1281,12 +1348,14 @@ public class MidiService extends IMidiManager.Stub { } } - private void addPackageDeviceServers(String packageName) { + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) + private void addPackageDeviceServers(String packageName, int userId) { PackageInfo info; try { - info = mPackageManager.getPackageInfo(packageName, - PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + info = mPackageManager.getPackageInfoAsUser(packageName, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e); return; @@ -1295,13 +1364,14 @@ public class MidiService extends IMidiManager.Stub { ServiceInfo[] services = info.services; if (services == null) return; for (int i = 0; i < services.length; i++) { - addPackageDeviceServer(services[i]); + addPackageDeviceServer(services[i], userId); } } private static final String[] EMPTY_STRING_ARRAY = new String[0]; - private void addPackageDeviceServer(ServiceInfo serviceInfo) { + private void addPackageDeviceServer(ServiceInfo serviceInfo, int userId) { + Log.d(TAG, "addPackageDeviceServer()" + userId); XmlResourceParser parser = null; try { @@ -1404,8 +1474,8 @@ public class MidiService extends IMidiManager.Stub { int uid; try { - ApplicationInfo appInfo = mPackageManager.getApplicationInfo( - serviceInfo.packageName, 0); + ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser( + serviceInfo.packageName, 0, userId); uid = appInfo.uid; } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "could not fetch ApplicationInfo for " @@ -1419,7 +1489,7 @@ public class MidiService extends IMidiManager.Stub { inputPortNames.toArray(EMPTY_STRING_ARRAY), outputPortNames.toArray(EMPTY_STRING_ARRAY), properties, null, serviceInfo, isPrivate, uid, - MidiDeviceInfo.PROTOCOL_UNKNOWN); + MidiDeviceInfo.PROTOCOL_UNKNOWN, userId); } // setting properties to null signals that we are no longer // processing a <device> @@ -1437,12 +1507,13 @@ public class MidiService extends IMidiManager.Stub { } } - private void removePackageDeviceServers(String packageName) { + private void removePackageDeviceServers(String packageName, int userId) { synchronized (mDevicesByInfo) { Iterator<Device> iterator = mDevicesByInfo.values().iterator(); while (iterator.hasNext()) { Device device = iterator.next(); - if (packageName.equals(device.getPackageName())) { + if (packageName.equals(device.getPackageName()) + && (device.getUserId() == userId)) { iterator.remove(); removeDeviceLocked(device); } @@ -1571,4 +1642,11 @@ public class MidiService extends IMidiManager.Stub { String extractUsbDeviceTag(String propertyName) { return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length()); } + + /** + * @return the user id of the calling user. + */ + private int getCallingUserId() { + return UserHandle.getUserId(Binder.getCallingUid()); + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/OWNERS b/services/tests/displayservicetests/OWNERS index 6ce1ee4d3de2..6ce1ee4d3de2 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/OWNERS +++ b/services/tests/displayservicetests/OWNERS diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java index a9e616d766c6..8497dabba67d 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java @@ -18,17 +18,23 @@ package com.android.server.display.brightness; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.content.ContentResolver; import android.content.Context; +import android.content.ContextWrapper; import android.content.res.Resources; import android.hardware.display.DisplayManagerInternal; import android.view.Display; +import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.display.brightness.strategy.BoostBrightnessStrategy; import com.android.server.display.brightness.strategy.DozeBrightnessStrategy; import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy; @@ -38,6 +44,7 @@ import com.android.server.display.brightness.strategy.ScreenOffBrightnessStrateg import com.android.server.display.brightness.strategy.TemporaryBrightnessStrategy; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -64,15 +71,20 @@ public final class DisplayBrightnessStrategySelectorTest { @Mock private FollowerBrightnessStrategy mFollowerBrightnessStrategy; @Mock - private Context mContext; - @Mock private Resources mResources; private DisplayBrightnessStrategySelector mDisplayBrightnessStrategySelector; + private Context mContext; + + @Rule + public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Before public void before() { MockitoAnnotations.initMocks(this); + mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); + ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContext); + when(mContext.getContentResolver()).thenReturn(contentResolver); when(mContext.getResources()).thenReturn(mResources); when(mInvalidBrightnessStrategy.getName()).thenReturn("InvalidBrightnessStrategy"); DisplayBrightnessStrategySelector.Injector injector = diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java new file mode 100644 index 000000000000..37d0f6250aaf --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.brightness.clamper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.hardware.display.DisplayManager; +import android.os.IThermalEventListener; +import android.os.IThermalService; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.Temperature; +import android.provider.DeviceConfig; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.annotations.Keep; +import com.android.server.display.DisplayDeviceConfig; +import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; +import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel; +import com.android.server.display.feature.DeviceConfigParameterProvider; +import com.android.server.testutils.FakeDeviceConfigInterface; +import com.android.server.testutils.TestHandler; + +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.List; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + +@RunWith(JUnitParamsRunner.class) +public class BrightnessThermalClamperTest { + + private static final float FLOAT_TOLERANCE = 0.001f; + + private static final String DISPLAY_ID = "displayId"; + @Mock + private IThermalService mMockThermalService; + @Mock + private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener; + + private final FakeDeviceConfigInterface mFakeDeviceConfigInterface = + new FakeDeviceConfigInterface(); + private final TestHandler mTestHandler = new TestHandler(null); + private BrightnessThermalClamper mClamper; + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mClamper = new BrightnessThermalClamper(new TestInjector(), mTestHandler, + mMockClamperChangeListener, new TestThermalData()); + mTestHandler.flush(); + } + + @Test + public void testTypeIsThermal() { + assertEquals(BrightnessClamper.Type.THERMAL, mClamper.getType()); + } + + @Test + public void testNoThrottlingData() { + assertFalse(mClamper.isActive()); + assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + } + + @Keep + private static Object[][] testThrottlingData() { + // throttlingLevels, throttlingStatus, expectedActive, expectedBrightness + return new Object[][] { + // no throttling data + {List.of(), Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX}, + // throttlingStatus < min throttling data + {List.of( + new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f), + new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)), + Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX}, + // throttlingStatus = min throttling data + {List.of( + new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f), + new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)), + Temperature.THROTTLING_MODERATE, true, 0.5f}, + // throttlingStatus between min and max throttling data + {List.of( + new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f), + new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)), + Temperature.THROTTLING_SEVERE, true, 0.5f}, + // throttlingStatus = max throttling data + {List.of( + new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f), + new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)), + Temperature.THROTTLING_CRITICAL, true, 0.1f}, + // throttlingStatus > max throttling data + {List.of( + new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f), + new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)), + Temperature.THROTTLING_EMERGENCY, true, 0.1f}, + }; + } + @Test + @Parameters(method = "testThrottlingData") + public void testNotifyThrottlingAfterOnDisplayChange(List<ThrottlingLevel> throttlingLevels, + @Temperature.ThrottlingStatus int throttlingStatus, + boolean expectedActive, float expectedBrightness) throws RemoteException { + IThermalEventListener thermalEventListener = captureThermalEventListener(); + mClamper.onDisplayChanged(new TestThermalData(throttlingLevels)); + mTestHandler.flush(); + assertFalse(mClamper.isActive()); + assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + + thermalEventListener.notifyThrottling(createTemperature(throttlingStatus)); + mTestHandler.flush(); + assertEquals(expectedActive, mClamper.isActive()); + assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + } + + @Test + @Parameters(method = "testThrottlingData") + public void testOnDisplayChangeAfterNotifyThrottlng(List<ThrottlingLevel> throttlingLevels, + @Temperature.ThrottlingStatus int throttlingStatus, + boolean expectedActive, float expectedBrightness) throws RemoteException { + IThermalEventListener thermalEventListener = captureThermalEventListener(); + thermalEventListener.notifyThrottling(createTemperature(throttlingStatus)); + mTestHandler.flush(); + assertFalse(mClamper.isActive()); + assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + + mClamper.onDisplayChanged(new TestThermalData(throttlingLevels)); + mTestHandler.flush(); + assertEquals(expectedActive, mClamper.isActive()); + assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + } + + @Test + public void testOverrideData() throws RemoteException { + IThermalEventListener thermalEventListener = captureThermalEventListener(); + thermalEventListener.notifyThrottling(createTemperature(Temperature.THROTTLING_SEVERE)); + mTestHandler.flush(); + assertFalse(mClamper.isActive()); + assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + + mClamper.onDisplayChanged(new TestThermalData( + List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 0.5f)))); + mTestHandler.flush(); + assertTrue(mClamper.isActive()); + assertEquals(0.5f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + + overrideThrottlingData("displayId,1,emergency,0.4"); + mClamper.onDeviceConfigChanged(); + mTestHandler.flush(); + + assertFalse(mClamper.isActive()); + assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + + overrideThrottlingData("displayId,1,moderate,0.4"); + mClamper.onDeviceConfigChanged(); + mTestHandler.flush(); + + assertTrue(mClamper.isActive()); + assertEquals(0.4f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + } + + private IThermalEventListener captureThermalEventListener() throws RemoteException { + ArgumentCaptor<IThermalEventListener> captor = ArgumentCaptor.forClass( + IThermalEventListener.class); + verify(mMockThermalService).registerThermalEventListenerWithType(captor.capture(), eq( + Temperature.TYPE_SKIN)); + return captor.getValue(); + } + + private Temperature createTemperature(@Temperature.ThrottlingStatus int status) { + return new Temperature(100, Temperature.TYPE_SKIN, "test_temperature", status); + } + + private void overrideThrottlingData(String data) { + mFakeDeviceConfigInterface.putProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA, data); + } + + private class TestInjector extends BrightnessThermalClamper.Injector { + @Override + IThermalService getThermalService() { + return mMockThermalService; + } + + @Override + DeviceConfigParameterProvider getDeviceConfigParameterProvider() { + return new DeviceConfigParameterProvider(mFakeDeviceConfigInterface); + } + } + + private static class TestThermalData implements BrightnessThermalClamper.ThermalData { + + private final String mUniqueDisplayId; + private final String mDataId; + private final ThermalBrightnessThrottlingData mData; + + private TestThermalData() { + this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null); + } + + private TestThermalData(List<ThrottlingLevel> data) { + this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data); + } + private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data) { + mUniqueDisplayId = uniqueDisplayId; + mDataId = dataId; + mData = ThermalBrightnessThrottlingData.create(data); + } + @NonNull + @Override + public String getUniqueDisplayId() { + return mUniqueDisplayId; + } + + @NonNull + @Override + public String getThermalThrottlingDataId() { + return mDataId; + } + + @Nullable + @Override + public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() { + return mData; + } + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/utils/DeviceConfigParsingUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/utils/DeviceConfigParsingUtilsTest.java new file mode 100644 index 000000000000..5e28e63cfa49 --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/utils/DeviceConfigParsingUtilsTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import android.os.PowerManager; +import android.util.Pair; + +import androidx.test.filters.SmallTest; + +import com.android.internal.annotations.Keep; +import com.android.server.display.DisplayDeviceConfig; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + +@SmallTest +@RunWith(JUnitParamsRunner.class) +public class DeviceConfigParsingUtilsTest { + private static final String VALID_DATA_STRING = "display1,1,key1,value1"; + private static final float FLOAT_TOLERANCE = 0.001f; + + private final BiFunction<String, String, Pair<String, String>> mDataPointToPair = Pair::create; + private final Function<List<Pair<String, String>>, List<Pair<String, String>>> + mDataSetIdentity = (dataSet) -> dataSet; + + @Keep + private static Object[][] parseDeviceConfigMapData() { + // dataString, expectedMap + return new Object[][]{ + // null + {null, Map.of()}, + // empty string + {"", Map.of()}, + // 1 display, 1 incomplete data point + {"display1,1,key1", Map.of()}, + // 1 display,2 data points required, only 1 present + {"display1,2,key1,value1", Map.of()}, + // 1 display, 1 data point, dataSetId and some extra data + {"display1,1,key1,value1,setId1,extraData", Map.of()}, + // 1 display, random string instead of number of data points + {"display1,one,key1,value1", Map.of()}, + // 1 display, 1 data point no dataSetId + {VALID_DATA_STRING, Map.of("display1", Map.of(DisplayDeviceConfig.DEFAULT_ID, + List.of(Pair.create("key1", "value1"))))}, + // 1 display, 1 data point, dataSetId + {"display1,1,key1,value1,setId1", Map.of("display1", Map.of("setId1", + List.of(Pair.create("key1", "value1"))))}, + // 1 display, 2 data point, dataSetId + {"display1,2,key1,value1,key2,value2,setId1", Map.of("display1", Map.of("setId1", + List.of(Pair.create("key1", "value1"), Pair.create("key2", "value2"))))}, + }; + } + + @Test + @Parameters(method = "parseDeviceConfigMapData") + public void testParseDeviceConfigMap(String dataString, + Map<String, Map<String, List<Pair<String, String>>>> expectedMap) { + Map<String, Map<String, List<Pair<String, String>>>> result = + DeviceConfigParsingUtils.parseDeviceConfigMap(dataString, mDataPointToPair, + mDataSetIdentity); + + assertEquals(expectedMap, result); + } + + @Test + public void testDataPointMapperReturnsNull() { + Map<String, Map<String, List<Pair<String, String>>>> result = + DeviceConfigParsingUtils.parseDeviceConfigMap(VALID_DATA_STRING, (s1, s2) -> null, + mDataSetIdentity); + + assertEquals(Map.of(), result); + } + + @Test + public void testDataSetMapperReturnsNull() { + Map<String, Map<String, List<Pair<String, String>>>> result = + DeviceConfigParsingUtils.parseDeviceConfigMap(VALID_DATA_STRING, mDataPointToPair, + (dataSet) -> null); + + assertEquals(Map.of(), result); + } + + @Keep + private static Object[][] parseThermalStatusData() { + // thermalStatusString, expectedThermalStatus + return new Object[][]{ + {"none", PowerManager.THERMAL_STATUS_NONE}, + {"light", PowerManager.THERMAL_STATUS_LIGHT}, + {"moderate", PowerManager.THERMAL_STATUS_MODERATE}, + {"severe", PowerManager.THERMAL_STATUS_SEVERE}, + {"critical", PowerManager.THERMAL_STATUS_CRITICAL}, + {"emergency", PowerManager.THERMAL_STATUS_EMERGENCY}, + {"shutdown", PowerManager.THERMAL_STATUS_SHUTDOWN}, + }; + } + + @Test + @Parameters(method = "parseThermalStatusData") + public void testParseThermalStatus(String thermalStatusString, + @PowerManager.ThermalStatus int expectedThermalStatus) { + int result = DeviceConfigParsingUtils.parseThermalStatus(thermalStatusString); + + assertEquals(expectedThermalStatus, result); + } + + @Test + public void testParseThermalStatus_illegalStatus() { + Throwable result = assertThrows(IllegalArgumentException.class, + () -> DeviceConfigParsingUtils.parseThermalStatus("invalid_status")); + + assertEquals("Invalid Thermal Status: invalid_status", result.getMessage()); + } + + @Test + public void testParseBrightness() { + float result = DeviceConfigParsingUtils.parseBrightness("0.65"); + + assertEquals(0.65, result, FLOAT_TOLERANCE); + } + + @Test + public void testParseBrightness_lessThanMin() { + Throwable result = assertThrows(IllegalArgumentException.class, + () -> DeviceConfigParsingUtils.parseBrightness("-0.65")); + + assertEquals("Brightness value out of bounds: -0.65", result.getMessage()); + } + + @Test + public void testParseBrightness_moreThanMax() { + Throwable result = assertThrows(IllegalArgumentException.class, + () -> DeviceConfigParsingUtils.parseBrightness("1.65")); + + assertEquals("Brightness value out of bounds: 1.65", result.getMessage()); + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/SensorUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/utils/SensorUtilsTest.java index 4494b0c412dc..4494b0c412dc 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/SensorUtilsTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/utils/SensorUtilsTest.java diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index 212a243c6a9e..cd3a78ed5e17 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -52,6 +52,7 @@ import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameModeListener; +import android.app.IGameStateListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; @@ -1578,6 +1579,71 @@ public class GameManagerServiceTests { assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); } + @Test + public void testAddGameStateListener() throws Exception { + mockModifyGameModeGranted(); + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + + IGameStateListener mockListener = Mockito.mock(IGameStateListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + gameManagerService.addGameStateListener(mockListener); + verify(binder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt()); + + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO); + GameState gameState = new GameState(true, GameState.MODE_NONE); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME); + gameState = new GameState(true, GameState.MODE_NONE); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener).onGameStateChanged(mPackageName, gameState, USER_ID_1); + reset(mockListener); + + gameState = new GameState(false, GameState.MODE_CONTENT); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + mTestLooper.dispatchAll(); + verify(mockListener).onGameStateChanged(mPackageName, gameState, USER_ID_1); + reset(mockListener); + + mDeathRecipientCaptor.getValue().binderDied(); + verify(binder).unlinkToDeath(eq(mDeathRecipientCaptor.getValue()), anyInt()); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + } + + @Test + public void testRemoveGameStateListener() throws Exception { + mockModifyGameModeGranted(); + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + + IGameStateListener mockListener = Mockito.mock(IGameStateListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + + gameManagerService.addGameStateListener(mockListener); + gameManagerService.removeGameStateListener(mockListener); + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME); + GameState gameState = new GameState(false, GameState.MODE_CONTENT); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + } + private List<String> readGameModeInterventionList() throws Exception { final File interventionFile = new File(InstrumentationRegistry.getContext().getFilesDir(), "system/game_mode_intervention.list"); diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayBrightnessStateTest.java index 50996d7199c8..95c62aeec19a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayBrightnessStateTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayBrightnessStateTest.java @@ -43,25 +43,50 @@ public class DisplayBrightnessStateTest { public void validateAllDisplayBrightnessStateFieldsAreSetAsExpected() { float brightness = 0.3f; float sdrBrightness = 0.2f; + boolean shouldUseAutoBrightness = true; BrightnessReason brightnessReason = new BrightnessReason(); brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC); brightnessReason.setModifier(BrightnessReason.MODIFIER_DIMMED); - DisplayBrightnessState displayBrightnessState = - mDisplayBrightnessStateBuilder.setBrightness(brightness).setSdrBrightness( - sdrBrightness).setBrightnessReason(brightnessReason).build(); + DisplayBrightnessState displayBrightnessState = mDisplayBrightnessStateBuilder + .setBrightness(brightness) + .setSdrBrightness(sdrBrightness) + .setBrightnessReason(brightnessReason) + .setShouldUseAutoBrightness(shouldUseAutoBrightness) + .build(); assertEquals(displayBrightnessState.getBrightness(), brightness, FLOAT_DELTA); assertEquals(displayBrightnessState.getSdrBrightness(), sdrBrightness, FLOAT_DELTA); assertEquals(displayBrightnessState.getBrightnessReason(), brightnessReason); + assertEquals(displayBrightnessState.getShouldUseAutoBrightness(), shouldUseAutoBrightness); assertEquals(displayBrightnessState.toString(), getString(displayBrightnessState)); } + @Test + public void testFrom() { + BrightnessReason reason = new BrightnessReason(); + reason.setReason(BrightnessReason.REASON_MANUAL); + reason.setModifier(BrightnessReason.MODIFIER_DIMMED); + DisplayBrightnessState state1 = new DisplayBrightnessState.Builder() + .setBrightnessReason(reason) + .setBrightness(0.26f) + .setSdrBrightness(0.23f) + .setShouldUseAutoBrightness(false) + .build(); + DisplayBrightnessState state2 = DisplayBrightnessState.Builder.from(state1).build(); + assertEquals(state1, state2); + } + private String getString(DisplayBrightnessState displayBrightnessState) { StringBuilder sb = new StringBuilder(); - sb.append("DisplayBrightnessState:"); - sb.append("\n brightness:" + displayBrightnessState.getBrightness()); - sb.append("\n sdrBrightness:" + displayBrightnessState.getSdrBrightness()); - sb.append("\n brightnessReason:" + displayBrightnessState.getBrightnessReason()); + sb.append("DisplayBrightnessState:") + .append("\n brightness:") + .append(displayBrightnessState.getBrightness()) + .append("\n sdrBrightness:") + .append(displayBrightnessState.getSdrBrightness()) + .append("\n brightnessReason:") + .append(displayBrightnessState.getBrightnessReason()) + .append("\n shouldUseAutoBrightness:") + .append(displayBrightnessState.getShouldUseAutoBrightness()); return sb.toString(); } } diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java index aaab4033d579..c710d1c3885d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java @@ -714,6 +714,8 @@ public final class DisplayPowerController2Test { Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); + DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_OFF; mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -751,6 +753,7 @@ public final class DisplayPowerController2Test { Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_DOZE; mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 0530f892a48e..ee3a773580a3 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -34,7 +34,6 @@ android_test { "services.core", "services.credentials", "services.devicepolicy", - "services.flags", "services.net", "services.people", "services.usage", diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index 8346050c3c89..0cfddd30d721 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -106,7 +106,7 @@ public class AuthSessionTest { @Mock private KeyStore mKeyStore; @Mock private AuthSession.ClientDeathReceiver mClientDeathReceiver; @Mock private BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger; - @Mock BiometricSensorPrivacy mBiometricSensorPrivacy; + @Mock private BiometricCameraManager mBiometricCameraManager; private Random mRandom; private IBinder mToken; @@ -609,7 +609,7 @@ public class AuthSessionTest { TEST_PACKAGE, checkDevicePolicyManager, mContext, - mBiometricSensorPrivacy); + mBiometricCameraManager); } private AuthSession createAuthSession(List<BiometricSensor> sensors, diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index 41f7dbcb0ff5..68217219e453 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -151,6 +151,8 @@ public class BiometricServiceTest { private AuthSessionCoordinator mAuthSessionCoordinator; @Mock private UserManager mUserManager; + @Mock + private BiometricCameraManager mBiometricCameraManager; BiometricContextProvider mBiometricContextProvider; @@ -177,6 +179,7 @@ public class BiometricServiceTest { when(mInjector.getDevicePolicyManager(any())).thenReturn(mDevicePolicyManager); when(mInjector.getRequestGenerator()).thenReturn(() -> TEST_REQUEST_ID); when(mInjector.getUserManager(any())).thenReturn(mUserManager); + when(mInjector.getBiometricCameraManager(any())).thenReturn(mBiometricCameraManager); when(mResources.getString(R.string.biometric_error_hw_unavailable)) .thenReturn(ERROR_HW_UNAVAILABLE); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java index 0c98c8d88d83..c2bdf501198e 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java @@ -67,7 +67,7 @@ public class PreAuthInfoTest { @Mock BiometricService.SettingObserver mSettingObserver; @Mock - BiometricSensorPrivacy mBiometricSensorPrivacyUtil; + BiometricCameraManager mBiometricCameraManager; @Before public void setup() throws RemoteException { @@ -79,11 +79,13 @@ public class PreAuthInfoTest { when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true); when(mFaceAuthenticator.getLockoutModeForUser(anyInt())) .thenReturn(LOCKOUT_NONE); + when(mBiometricCameraManager.isCameraPrivacyEnabled()).thenReturn(false); + when(mBiometricCameraManager.isAnyCameraUnavailable()).thenReturn(false); } @Test public void testFaceAuthentication_whenCameraPrivacyIsEnabled() throws Exception { - when(mBiometricSensorPrivacyUtil.isCameraPrivacyEnabled()).thenReturn(true); + when(mBiometricCameraManager.isCameraPrivacyEnabled()).thenReturn(true); BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FACE, TYPE_FACE, BiometricManager.Authenticators.BIOMETRIC_STRONG, mFaceAuthenticator) { @@ -104,15 +106,14 @@ public class PreAuthInfoTest { PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME, - false /* checkDevicePolicyManager */, mContext, mBiometricSensorPrivacyUtil); + false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager); assertThat(preAuthInfo.eligibleSensors).isEmpty(); } @Test - public void testFaceAuthentication_whenCameraPrivacyIsDisabled() throws Exception { - when(mBiometricSensorPrivacyUtil.isCameraPrivacyEnabled()).thenReturn(false); - + public void testFaceAuthentication_whenCameraPrivacyIsDisabledAndCameraIsAvailable() + throws Exception { BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FACE, TYPE_FACE, BiometricManager.Authenticators.BIOMETRIC_STRONG, mFaceAuthenticator) { @Override @@ -132,8 +133,35 @@ public class PreAuthInfoTest { PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME, - false /* checkDevicePolicyManager */, mContext, mBiometricSensorPrivacyUtil); + false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager); assertThat(preAuthInfo.eligibleSensors).hasSize(1); } + + @Test + public void testFaceAuthentication_whenCameraIsUnavailable() throws RemoteException { + when(mBiometricCameraManager.isAnyCameraUnavailable()).thenReturn(true); + BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FACE, TYPE_FACE, + BiometricManager.Authenticators.BIOMETRIC_STRONG, mFaceAuthenticator) { + @Override + boolean confirmationAlwaysRequired(int userId) { + return false; + } + + @Override + boolean confirmationSupported() { + return false; + } + }; + PromptInfo promptInfo = new PromptInfo(); + promptInfo.setConfirmationRequested(false /* requireConfirmation */); + promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG); + promptInfo.setDisallowBiometricsIfPolicyExists(false /* checkDevicePolicy */); + PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager, + mSettingObserver, List.of(sensor), + 0 /* userId */, promptInfo, TEST_PACKAGE_NAME, + false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager); + + assertThat(preAuthInfo.eligibleSensors).hasSize(0); + } } diff --git a/services/tests/servicestests/src/com/android/server/flags/FeatureFlagsServiceTest.java b/services/tests/servicestests/src/com/android/server/flags/FeatureFlagsServiceTest.java deleted file mode 100644 index df4731fb0bb7..000000000000 --- a/services/tests/servicestests/src/com/android/server/flags/FeatureFlagsServiceTest.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.flags; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; - -import static org.junit.Assert.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.flags.IFeatureFlagsCallback; -import android.flags.SyncableFlag; -import android.os.IBinder; -import android.os.RemoteException; -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.util.List; - -@Presubmit -@SmallTest -public class FeatureFlagsServiceTest { - private static final String NS = "ns"; - private static final String NAME = "name"; - private static final String PROP_NAME = FlagOverrideStore.getPropName(NS, NAME); - - @Rule - public final MockitoRule mockito = MockitoJUnit.rule(); - - @Mock - private FlagOverrideStore mFlagStore; - @Mock - private FlagsShellCommand mFlagCommand; - @Mock - private IFeatureFlagsCallback mIFeatureFlagsCallback; - @Mock - private IBinder mIFeatureFlagsCallbackAsBinder; - @Mock - private FeatureFlagsService.PermissionsChecker mPermissionsChecker; - - private FeatureFlagsBinder mFeatureFlagsService; - - @Before - public void setup() { - when(mIFeatureFlagsCallback.asBinder()).thenReturn(mIFeatureFlagsCallbackAsBinder); - mFeatureFlagsService = new FeatureFlagsBinder( - mFlagStore, mFlagCommand, mPermissionsChecker); - } - - @Test - public void testRegisterCallback() { - mFeatureFlagsService.registerCallback(mIFeatureFlagsCallback); - try { - verify(mIFeatureFlagsCallbackAsBinder).linkToDeath(any(), eq(0)); - } catch (RemoteException e) { - fail("Our mock threw a Remote Exception?"); - } - } - - @Test - public void testOverrideFlag_requiresWritePermission() { - SecurityException exc = new SecurityException("not allowed"); - doThrow(exc).when(mPermissionsChecker).assertWritePermission(); - - SyncableFlag f = new SyncableFlag(NS, "a", "false", false); - - try { - mFeatureFlagsService.overrideFlag(f); - fail("Should have thrown exception"); - } catch (SecurityException e) { - assertThat(exc).isEqualTo(e); - } catch (Exception e) { - fail("should have thrown a security exception"); - } - } - - @Test - public void testResetFlag_requiresWritePermission() { - SecurityException exc = new SecurityException("not allowed"); - doThrow(exc).when(mPermissionsChecker).assertWritePermission(); - - SyncableFlag f = new SyncableFlag(NS, "a", "false", false); - - try { - mFeatureFlagsService.resetFlag(f); - fail("Should have thrown exception"); - } catch (SecurityException e) { - assertThat(exc).isEqualTo(e); - } catch (Exception e) { - fail("should have thrown a security exception"); - } - } - - @Test - public void testSyncFlags_noOverrides() { - List<SyncableFlag> inputFlags = List.of( - new SyncableFlag(NS, "a", "false", false), - new SyncableFlag(NS, "b", "true", false), - new SyncableFlag(NS, "c", "false", false) - ); - - List<SyncableFlag> outputFlags = mFeatureFlagsService.syncFlags(inputFlags); - - assertThat(inputFlags.size()).isEqualTo(outputFlags.size()); - - for (SyncableFlag inpF: inputFlags) { - boolean found = false; - for (SyncableFlag outF : outputFlags) { - if (compareSyncableFlagsNames(inpF, outF)) { - found = true; - break; - } - } - assertWithMessage("Failed to find input flag " + inpF + " in the output") - .that(found).isTrue(); - } - } - - @Test - public void testSyncFlags_withSomeOverrides() { - List<SyncableFlag> inputFlags = List.of( - new SyncableFlag(NS, "a", "false", false), - new SyncableFlag(NS, "b", "true", false), - new SyncableFlag(NS, "c", "false", false) - ); - - assertThat(mFlagStore).isNotNull(); - when(mFlagStore.get(NS, "c")).thenReturn("true"); - List<SyncableFlag> outputFlags = mFeatureFlagsService.syncFlags(inputFlags); - - assertThat(inputFlags.size()).isEqualTo(outputFlags.size()); - - for (SyncableFlag inpF: inputFlags) { - boolean found = false; - for (SyncableFlag outF : outputFlags) { - if (compareSyncableFlagsNames(inpF, outF)) { - found = true; - - // Once we've found "c", do an extra check - if (outF.getName().equals("c")) { - assertWithMessage("Flag " + outF + "was not returned with an override") - .that(outF.getValue()).isEqualTo("true"); - } - break; - } - } - assertWithMessage("Failed to find input flag " + inpF + " in the output") - .that(found).isTrue(); - } - } - - @Test - public void testSyncFlags_twoCallsWithDifferentDefaults() { - List<SyncableFlag> inputFlagsFirst = List.of( - new SyncableFlag(NS, "a", "false", false) - ); - List<SyncableFlag> inputFlagsSecond = List.of( - new SyncableFlag(NS, "a", "true", false), - new SyncableFlag(NS, "b", "false", false) - ); - - List<SyncableFlag> outputFlagsFirst = mFeatureFlagsService.syncFlags(inputFlagsFirst); - List<SyncableFlag> outputFlagsSecond = mFeatureFlagsService.syncFlags(inputFlagsSecond); - - assertThat(inputFlagsFirst.size()).isEqualTo(outputFlagsFirst.size()); - assertThat(inputFlagsSecond.size()).isEqualTo(outputFlagsSecond.size()); - - // This test only cares that the "a" flag passed in the second time came out with the - // same value that was passed in the first time. - - boolean found = false; - for (SyncableFlag second : outputFlagsSecond) { - if (compareSyncableFlagsNames(second, inputFlagsFirst.get(0))) { - found = true; - assertThat(second.getValue()).isEqualTo(inputFlagsFirst.get(0).getValue()); - break; - } - } - - assertWithMessage( - "Failed to find flag " + inputFlagsFirst.get(0) + " in the second calls output") - .that(found).isTrue(); - } - - @Test - public void testQueryFlags_onlyOnce() { - List<SyncableFlag> inputFlags = List.of( - new SyncableFlag(NS, "a", "false", false), - new SyncableFlag(NS, "b", "true", false), - new SyncableFlag(NS, "c", "false", false) - ); - - List<SyncableFlag> outputFlags = mFeatureFlagsService.queryFlags(inputFlags); - - assertThat(inputFlags.size()).isEqualTo(outputFlags.size()); - - for (SyncableFlag inpF: inputFlags) { - boolean found = false; - for (SyncableFlag outF : outputFlags) { - if (compareSyncableFlagsNames(inpF, outF)) { - found = true; - break; - } - } - assertWithMessage("Failed to find input flag " + inpF + " in the output") - .that(found).isTrue(); - } - } - - @Test - public void testQueryFlags_twoCallsWithDifferentDefaults() { - List<SyncableFlag> inputFlagsFirst = List.of( - new SyncableFlag(NS, "a", "false", false) - ); - List<SyncableFlag> inputFlagsSecond = List.of( - new SyncableFlag(NS, "a", "true", false), - new SyncableFlag(NS, "b", "false", false) - ); - - List<SyncableFlag> outputFlagsFirst = mFeatureFlagsService.queryFlags(inputFlagsFirst); - List<SyncableFlag> outputFlagsSecond = mFeatureFlagsService.queryFlags(inputFlagsSecond); - - assertThat(inputFlagsFirst.size()).isEqualTo(outputFlagsFirst.size()); - assertThat(inputFlagsSecond.size()).isEqualTo(outputFlagsSecond.size()); - - // This test only cares that the "a" flag passed in the second time came out with the - // same value that was passed in (i.e. it wasn't cached). - - boolean found = false; - for (SyncableFlag second : outputFlagsSecond) { - if (compareSyncableFlagsNames(second, inputFlagsSecond.get(0))) { - found = true; - assertThat(second.getValue()).isEqualTo(inputFlagsSecond.get(0).getValue()); - break; - } - } - - assertWithMessage( - "Failed to find flag " + inputFlagsSecond.get(0) + " in the second calls output") - .that(found).isTrue(); - } - - @Test - public void testOverrideFlag() { - SyncableFlag f = new SyncableFlag(NS, "a", "false", false); - - mFeatureFlagsService.overrideFlag(f); - - verify(mFlagStore).set(f.getNamespace(), f.getName(), f.getValue()); - } - - @Test - public void testResetFlag() { - SyncableFlag f = new SyncableFlag(NS, "a", "false", false); - - mFeatureFlagsService.resetFlag(f); - - verify(mFlagStore).erase(f.getNamespace(), f.getName()); - } - - - private static boolean compareSyncableFlagsNames(SyncableFlag a, SyncableFlag b) { - return a.getNamespace().equals(b.getNamespace()) - && a.getName().equals(b.getName()) - && a.isDynamic() == b.isDynamic(); - } -} diff --git a/services/tests/servicestests/src/com/android/server/flags/FlagCacheTest.java b/services/tests/servicestests/src/com/android/server/flags/FlagCacheTest.java deleted file mode 100644 index c2cf540d1d62..000000000000 --- a/services/tests/servicestests/src/com/android/server/flags/FlagCacheTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.flags; - -import static com.google.common.truth.Truth.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public class FlagCacheTest { - private static final String NS = "ns"; - private static final String NAME = "name"; - - FlagCache mFlagCache = new FlagCache(); - - @Test - public void testGetOrNull_unset() { - assertThat(mFlagCache.getOrNull(NS, NAME)).isNull(); - } - - @Test - public void testGetOrSet_unset() { - assertThat(mFlagCache.getOrSet(NS, NAME, "value")).isEqualTo("value"); - } - - @Test - public void testGetOrSet_alreadySet() { - mFlagCache.setIfChanged(NS, NAME, "value"); - assertThat(mFlagCache.getOrSet(NS, NAME, "newvalue")).isEqualTo("value"); - } - - @Test - public void testSetIfChanged_unset() { - assertThat(mFlagCache.setIfChanged(NS, NAME, "value")).isTrue(); - } - - @Test - public void testSetIfChanged_noChange() { - mFlagCache.setIfChanged(NS, NAME, "value"); - assertThat(mFlagCache.setIfChanged(NS, NAME, "value")).isFalse(); - } - - @Test - public void testSetIfChanged_changing() { - mFlagCache.setIfChanged(NS, NAME, "value"); - assertThat(mFlagCache.setIfChanged(NS, NAME, "newvalue")).isTrue(); - } - - @Test - public void testContainsNamespace_unset() { - assertThat(mFlagCache.containsNamespace(NS)).isFalse(); - } - - @Test - public void testContainsNamespace_set() { - mFlagCache.setIfChanged(NS, NAME, "value"); - assertThat(mFlagCache.containsNamespace(NS)).isTrue(); - } - - @Test - public void testContains_unset() { - assertThat(mFlagCache.contains(NS, NAME)).isFalse(); - } - - @Test - public void testContains_set() { - mFlagCache.setIfChanged(NS, NAME, "value"); - assertThat(mFlagCache.contains(NS, NAME)).isTrue(); - } -} diff --git a/services/tests/servicestests/src/com/android/server/flags/FlagOverrideStoreTest.java b/services/tests/servicestests/src/com/android/server/flags/FlagOverrideStoreTest.java deleted file mode 100644 index 6cc3acfb6125..000000000000 --- a/services/tests/servicestests/src/com/android/server/flags/FlagOverrideStoreTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.flags; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -@Presubmit -@SmallTest -public class FlagOverrideStoreTest { - private static final String NS = "ns"; - private static final String NAME = "name"; - private static final String PROP_NAME = FlagOverrideStore.getPropName(NS, NAME); - - @Rule - public final MockitoRule mockito = MockitoJUnit.rule(); - - @Mock - private SettingsProxy mSettingsProxy; - @Mock - private FlagOverrideStore.FlagChangeCallback mCallback; - - private FlagOverrideStore mFlagStore; - - @Before - public void setup() { - mFlagStore = new FlagOverrideStore(mSettingsProxy); - mFlagStore.setChangeCallback(mCallback); - } - - @Test - public void testSet_unset() { - mFlagStore.set(NS, NAME, "value"); - verify(mSettingsProxy).putString(PROP_NAME, "value"); - } - - @Test - public void testSet_setTwice() { - mFlagStore.set(NS, NAME, "value"); - mFlagStore.set(NS, NAME, "newvalue"); - verify(mSettingsProxy).putString(PROP_NAME, "value"); - verify(mSettingsProxy).putString(PROP_NAME, "newvalue"); - } - - @Test - public void testGet_unset() { - assertThat(mFlagStore.get(NS, NAME)).isNull(); - } - - @Test - public void testGet_set() { - when(mSettingsProxy.getString(PROP_NAME)).thenReturn("value"); - assertThat(mFlagStore.get(NS, NAME)).isEqualTo("value"); - } - - @Test - public void testErase() { - mFlagStore.erase(NS, NAME); - verify(mSettingsProxy).putString(PROP_NAME, null); - } - - @Test - public void testContains_unset() { - assertThat(mFlagStore.contains(NS, NAME)).isFalse(); - } - - @Test - public void testContains_set() { - when(mSettingsProxy.getString(PROP_NAME)).thenReturn("value"); - assertThat(mFlagStore.contains(NS, NAME)).isTrue(); - } - - @Test - public void testCallback_onSet() { - mFlagStore.set(NS, NAME, "value"); - verify(mCallback).onFlagChanged(NS, NAME, "value"); - } - - @Test - public void testCallback_onErase() { - mFlagStore.erase(NS, NAME); - verify(mCallback).onFlagChanged(NS, NAME, null); - } -} diff --git a/services/tests/servicestests/src/com/android/server/flags/OWNERS b/services/tests/servicestests/src/com/android/server/flags/OWNERS deleted file mode 100644 index 7ed369e37106..000000000000 --- a/services/tests/servicestests/src/com/android/server/flags/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /services/flags/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java index 0b13f9a35c1f..5f84e9ee54d0 100644 --- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java @@ -30,6 +30,7 @@ import android.provider.DeviceConfig; import android.provider.Settings.Global; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; +import android.test.suitebuilder.annotation.Suppress; import android.util.ArrayMap; import com.android.frameworks.servicestests.R; @@ -115,6 +116,7 @@ public class BatterySaverPolicyTest extends AndroidTestCase { testServiceDefaultValue_On(ServiceType.NULL); } + @Suppress @SmallTest public void testGetBatterySaverPolicy_PolicyVibration_DefaultValueCorrect() { testServiceDefaultValue_Off(ServiceType.VIBRATION); @@ -200,6 +202,7 @@ public class BatterySaverPolicyTest extends AndroidTestCase { testServiceDefaultValue_On(ServiceType.QUICK_DOZE); } + @Suppress @SmallTest public void testUpdateConstants_getCorrectData() { mBatterySaverPolicy.updateConstantsLocked(BATTERY_SAVER_CONSTANTS, ""); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 7e81ef22eb65..3c882dc871fd 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -405,6 +405,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { UriGrantsManagerInternal mUgmInternal; @Mock AppOpsManager mAppOpsManager; + private AppOpsManager.OnOpChangedListener mOnPermissionChangeListener; @Mock private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback; @@ -604,6 +605,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { tr.addOverride(com.android.internal.R.string.config_defaultSearchSelectorPackageName, SEARCH_SELECTOR_PKG); + doAnswer(invocation -> { + mOnPermissionChangeListener = invocation.getArgument(2); + return null; + }).when(mAppOpsManager).startWatchingMode(eq(AppOpsManager.OP_POST_NOTIFICATION), any(), + any()); + mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper())); mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient, mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr, @@ -2295,8 +2302,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel, 1, "group", true); notif.getNotification().flags |= Notification.FLAG_NO_CLEAR; mService.addNotification(notif); - mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, true, - notif.getUserId(), 0, null); + mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, + notif.getUserId(), 0); waitForIdle(); StatusBarNotification[] notifs = mBinderService.getActiveNotifications(notif.getSbn().getPackageName()); @@ -3034,7 +3041,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { notif.getNotification().flags |= Notification.FLAG_NO_CLEAR; mService.addNotification(notif); mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, - Notification.FLAG_ONGOING_EVENT, true, notif.getUserId(), 0, null); + Notification.FLAG_ONGOING_EVENT, notif.getUserId(), 0); waitForIdle(); StatusBarNotification[] notifs = mBinderService.getActiveNotifications(notif.getSbn().getPackageName()); @@ -3061,8 +3068,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel, 1, "group", true); notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT; mService.addNotification(notif); - mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, true, - notif.getUserId(), 0, null); + mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, + notif.getUserId(), 0); waitForIdle(); StatusBarNotification[] notifs = mBinderService.getActiveNotifications(notif.getSbn().getPackageName()); @@ -3216,48 +3223,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testUpdateAppNotifyCreatorBlock() throws Exception { - when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); - - mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false); - Thread.sleep(500); - waitForIdle(); - - ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); - verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null)); - - assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED, - captor.getValue().getAction()); - assertEquals(PKG, captor.getValue().getPackage()); - assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)); - } - - @Test - public void testUpdateAppNotifyCreatorBlock_notIfMatchesExistingSetting() throws Exception { - when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); - - mBinderService.setNotificationsEnabledForPackage(PKG, 0, false); - verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null)); - } - - @Test - public void testUpdateAppNotifyCreatorUnblock() throws Exception { - when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); - - mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true); - Thread.sleep(500); - waitForIdle(); - - ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); - verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null)); - - assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED, - captor.getValue().getAction()); - assertEquals(PKG, captor.getValue().getPackage()); - assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)); - } - - @Test public void testUpdateChannelNotifyCreatorBlock() throws Exception { mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(), @@ -12173,6 +12138,134 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { any(), eq(FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER | FLAG_SERVICE_SENDER)); } + @Test + public void onOpChanged_permissionRevoked_cancelsAllNotificationsFromPackage() + throws RemoteException { + // Have preexisting posted notifications from revoked package and other packages. + mService.addNotification(new NotificationRecord(mContext, + generateSbn("revoked", 1001, 1, 0), mTestNotificationChannel)); + mService.addNotification(new NotificationRecord(mContext, + generateSbn("other", 1002, 2, 0), mTestNotificationChannel)); + // Have preexisting enqueued notifications from revoked package and other packages. + mService.addEnqueuedNotification(new NotificationRecord(mContext, + generateSbn("revoked", 1001, 3, 0), mTestNotificationChannel)); + mService.addEnqueuedNotification(new NotificationRecord(mContext, + generateSbn("other", 1002, 4, 0), mTestNotificationChannel)); + assertThat(mService.mNotificationList).hasSize(2); + assertThat(mService.mEnqueuedNotifications).hasSize(2); + + when(mPackageManagerInternal.getPackageUid("revoked", 0, 0)).thenReturn(1001); + when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false); + + mOnPermissionChangeListener.onOpChanged( + AppOpsManager.OPSTR_POST_NOTIFICATION, "revoked", 0); + waitForIdle(); + + assertThat(mService.mNotificationList).hasSize(1); + assertThat(mService.mNotificationList.get(0).getSbn().getPackageName()).isEqualTo("other"); + assertThat(mService.mEnqueuedNotifications).hasSize(1); + assertThat(mService.mEnqueuedNotifications.get(0).getSbn().getPackageName()).isEqualTo( + "other"); + } + + @Test + public void onOpChanged_permissionStillGranted_notificationsAreNotAffected() + throws RemoteException { + // NOTE: This combination (receiving the onOpChanged broadcast for a package, the permission + // being now granted, AND having previously posted notifications from said package) should + // never happen (if we trust the broadcasts are correct). So this test is for a what-if + // scenario, to verify we still handle it reasonably. + + // Have preexisting posted notifications from specific package and other packages. + mService.addNotification(new NotificationRecord(mContext, + generateSbn("granted", 1001, 1, 0), mTestNotificationChannel)); + mService.addNotification(new NotificationRecord(mContext, + generateSbn("other", 1002, 2, 0), mTestNotificationChannel)); + // Have preexisting enqueued notifications from specific package and other packages. + mService.addEnqueuedNotification(new NotificationRecord(mContext, + generateSbn("granted", 1001, 3, 0), mTestNotificationChannel)); + mService.addEnqueuedNotification(new NotificationRecord(mContext, + generateSbn("other", 1002, 4, 0), mTestNotificationChannel)); + assertThat(mService.mNotificationList).hasSize(2); + assertThat(mService.mEnqueuedNotifications).hasSize(2); + + when(mPackageManagerInternal.getPackageUid("granted", 0, 0)).thenReturn(1001); + when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true); + + mOnPermissionChangeListener.onOpChanged( + AppOpsManager.OPSTR_POST_NOTIFICATION, "granted", 0); + waitForIdle(); + + assertThat(mService.mNotificationList).hasSize(2); + assertThat(mService.mEnqueuedNotifications).hasSize(2); + } + + @Test + public void onOpChanged_permissionGranted_notifiesAppUnblocked() throws Exception { + when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001); + when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true); + + mOnPermissionChangeListener.onOpChanged( + AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0); + waitForIdle(); + Thread.sleep(600); + waitForIdle(); + + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null)); + assertThat(captor.getValue().getAction()).isEqualTo( + NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED); + assertThat(captor.getValue().getPackage()).isEqualTo(PKG); + assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)).isFalse(); + } + + @Test + public void onOpChanged_permissionRevoked_notifiesAppBlocked() throws Exception { + when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001); + when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false); + + mOnPermissionChangeListener.onOpChanged( + AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0); + waitForIdle(); + Thread.sleep(600); + waitForIdle(); + + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null)); + assertThat(captor.getValue().getAction()).isEqualTo( + NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED); + assertThat(captor.getValue().getPackage()).isEqualTo(PKG); + assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false)).isTrue(); + } + + @Test + public void setNotificationsEnabledForPackage_disabling_clearsNotifications() throws Exception { + mService.addNotification(new NotificationRecord(mContext, + generateSbn("package", 1001, 1, 0), mTestNotificationChannel)); + assertThat(mService.mNotificationList).hasSize(1); + when(mPackageManagerInternal.getPackageUid("package", 0, 0)).thenReturn(1001); + when(mPermissionHelper.hasRequestedPermission(any(), eq("package"), anyInt())).thenReturn( + true); + + // Start with granted permission and simulate effect of revoking it. + when(mPermissionHelper.hasPermission(1001)).thenReturn(true); + doAnswer(invocation -> { + when(mPermissionHelper.hasPermission(1001)).thenReturn(false); + mOnPermissionChangeListener.onOpChanged( + AppOpsManager.OPSTR_POST_NOTIFICATION, "package", 0); + return null; + }).when(mPermissionHelper).setNotificationPermission("package", 0, false, true); + + mBinderService.setNotificationsEnabledForPackage("package", 1001, false); + waitForIdle(); + + assertThat(mService.mNotificationList).hasSize(0); + + Thread.sleep(600); + waitForIdle(); + verify(mContext).sendBroadcastAsUser(any(), eq(UserHandle.of(0)), eq(null)); + } + private static <T extends Parcelable> T parcelAndUnparcel(T source, Parcelable.Creator<T> creator) { Parcel parcel = Parcel.obtain(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 99ab715ab987..54b935132957 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -26,6 +26,7 @@ import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS; @@ -1590,6 +1591,46 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { assertEquals(taskFragmentBounds, mTaskFragment.getBounds()); } + @Test + public void testApplyTransaction_reorderTaskFragmentToFront() { + final Task task = createTask(mDisplayContent); + // Create a TaskFragment. + final IBinder token0 = new Binder(); + final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(token0) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + // Create another TaskFragment + final IBinder token1 = new Binder(); + final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(token1) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + // Create a non-embedded Activity on top. + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(task) + .build(); + mWindowOrganizerController.mLaunchTaskFragments.put(token0, tf0); + mWindowOrganizerController.mLaunchTaskFragments.put(token1, tf1); + + // Reorder TaskFragment to front + final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( + OP_TYPE_REORDER_TO_FRONT).build(); + mTransaction.addTaskFragmentOperation(token0, operation); + assertApplyTransactionAllowed(mTransaction); + + // Ensure the non-embedded activity still on top. + assertEquals(topActivity, task.getTopChild().asActivityRecord()); + + // Ensure the TaskFragment is moved to front. + final TaskFragment frontMostTaskFragment = task.getTaskFragment(tf -> tf.asTask() == null); + assertEquals(frontMostTaskFragment, tf0); + } + /** * Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls * {@link WindowOrganizerController#applyTransaction(WindowContainerTransaction)} to apply the |