diff options
84 files changed, 547 insertions, 4928 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/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/DynamicFlag.java b/core/java/android/flags/DynamicFlag.java deleted file mode 100644 index 68819c58c064..000000000000 --- a/core/java/android/flags/DynamicFlag.java +++ /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; - -/** - * 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; - } -} 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/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index b99996ff83c8..70b72c809524 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -433,7 +433,7 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, - @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) { + int flags, ResultReceiver resultReceiver) { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER); mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_SHOW_SOFT_INPUT, flags, showInputToken, resultReceiver, statsToken)); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index e1d2c32fe101..a9c4818393a8 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -607,7 +607,6 @@ public class InputMethodService extends AbstractInputMethodService { InputConnection mStartedInputConnection; EditorInfo mInputEditorInfo; - @InputMethod.ShowFlags int mShowInputFlags; boolean mShowInputRequested; boolean mLastShowInputRequested; @@ -932,9 +931,8 @@ public class InputMethodService extends AbstractInputMethodService { */ @MainThread @Override - public void showSoftInputWithToken(@InputMethod.ShowFlags int flags, - ResultReceiver resultReceiver, IBinder showInputToken, - @Nullable ImeTracker.Token statsToken) { + public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver, + IBinder showInputToken, @Nullable ImeTracker.Token statsToken) { mSystemCallingShowSoftInput = true; mCurShowInputToken = showInputToken; mCurStatsToken = statsToken; @@ -952,7 +950,7 @@ public class InputMethodService extends AbstractInputMethodService { */ @MainThread @Override - public void showSoftInput(@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) { + public void showSoftInput(int flags, ResultReceiver resultReceiver) { ImeTracker.forLogging().onProgress( mCurStatsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT); if (DEBUG) Log.v(TAG, "showSoftInput()"); @@ -1323,8 +1321,7 @@ public class InputMethodService extends AbstractInputMethodService { * InputMethodService#requestShowSelf} or {@link InputMethodService#requestHideSelf} */ @Deprecated - public void toggleSoftInput(@InputMethodManager.ShowFlags int showFlags, - @InputMethodManager.HideFlags int hideFlags) { + public void toggleSoftInput(int showFlags, int hideFlags) { InputMethodService.this.onToggleSoftInput(showFlags, hideFlags); } @@ -2796,16 +2793,18 @@ public class InputMethodService extends AbstractInputMethodService { * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()}, * and the current configuration to decide whether the input view should * be shown at this point. - * + * + * @param flags Provides additional information about the show request, + * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}. * @param configChange This is true if we are re-showing due to a * configuration change. * @return Returns true to indicate that the window should be shown. */ - public boolean onShowInputRequested(@InputMethod.ShowFlags int flags, boolean configChange) { + public boolean onShowInputRequested(int flags, boolean configChange) { if (!onEvaluateInputViewShown()) { return false; } - if ((flags & InputMethod.SHOW_EXPLICIT) == 0) { + if ((flags&InputMethod.SHOW_EXPLICIT) == 0) { if (!configChange && onEvaluateFullscreenMode() && !isInputViewShown()) { // Don't show if this is not explicitly requested by the user and // the input method is fullscreen unless it is already shown. That @@ -2831,14 +2830,14 @@ public class InputMethodService extends AbstractInputMethodService { * exposed to IME authors as an overridable public method without {@code @CallSuper}, we have * to have this method to ensure that those internal states are always updated no matter how * {@link #onShowInputRequested(int, boolean)} is overridden by the IME author. - * + * @param flags Provides additional information about the show request, + * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}. * @param configChange This is true if we are re-showing due to a * configuration change. * @return Returns true to indicate that the window should be shown. * @see #onShowInputRequested(int, boolean) */ - private boolean dispatchOnShowInputRequested(@InputMethod.ShowFlags int flags, - boolean configChange) { + private boolean dispatchOnShowInputRequested(int flags, boolean configChange) { final boolean result = onShowInputRequested(flags, configChange); mInlineSuggestionSessionController.notifyOnShowInputRequested(result); if (result) { @@ -3271,13 +3270,16 @@ public class InputMethodService extends AbstractInputMethodService { * * The input method will continue running, but the user can no longer use it to generate input * by touching the screen. + * + * @see InputMethodManager#HIDE_IMPLICIT_ONLY + * @see InputMethodManager#HIDE_NOT_ALWAYS + * @param flags Provides additional operating flags. */ - public void requestHideSelf(@InputMethodManager.HideFlags int flags) { + public void requestHideSelf(int flags) { requestHideSelf(flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME); } - private void requestHideSelf(@InputMethodManager.HideFlags int flags, - @SoftInputShowHideReason int reason) { + private void requestHideSelf(int flags, @SoftInputShowHideReason int reason) { ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper, null /* icProto */); mPrivOps.hideMySoftInput(flags, reason); @@ -3286,8 +3288,12 @@ public class InputMethodService extends AbstractInputMethodService { /** * Show the input method's soft input area, so the user sees the input method window and can * interact with it. + * + * @see InputMethodManager#SHOW_IMPLICIT + * @see InputMethodManager#SHOW_FORCED + * @param flags Provides additional operating flags. */ - public final void requestShowSelf(@InputMethodManager.ShowFlags int flags) { + public final void requestShowSelf(int flags) { ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper, null /* icProto */); mPrivOps.showMySoftInput(flags); @@ -3447,8 +3453,7 @@ public class InputMethodService extends AbstractInputMethodService { /** * Handle a request by the system to toggle the soft input area. */ - private void onToggleSoftInput(@InputMethodManager.ShowFlags int showFlags, - @InputMethodManager.HideFlags int hideFlags) { + private void onToggleSoftInput(int showFlags, int hideFlags) { if (DEBUG) Log.v(TAG, "toggleSoftInput()"); if (isInputViewShown()) { requestHideSelf( 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/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java index 467daa028afd..ce2c18080b91 100644 --- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java +++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java @@ -295,8 +295,8 @@ final class IInputMethodManagerGlobalInvoker { @AnyThread static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken, - @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, - int lastClickToolType, @Nullable ResultReceiver resultReceiver, + @Nullable ImeTracker.Token statsToken, int flags, int lastClickToolType, + @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { final IInputMethodManager service = getService(); if (service == null) { @@ -312,7 +312,7 @@ final class IInputMethodManagerGlobalInvoker { @AnyThread static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken, - @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, + @Nullable ImeTracker.Token statsToken, int flags, @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { final IInputMethodManager service = getService(); if (service == null) { diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index 9340f46b257f..92380ed7a7bc 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -17,7 +17,6 @@ package android.view.inputmethod; import android.annotation.DurationMillisLong; -import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; @@ -37,8 +36,6 @@ import com.android.internal.inputmethod.IInputMethod; import com.android.internal.inputmethod.InlineSuggestionsRequestInfo; import com.android.internal.inputmethod.InputMethodNavButtonFlags; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.List; /** @@ -272,14 +269,6 @@ public interface InputMethod { */ @MainThread public void revokeSession(InputMethodSession session); - - /** @hide */ - @IntDef(flag = true, prefix = { "SHOW_" }, value = { - SHOW_EXPLICIT, - SHOW_FORCED, - }) - @Retention(RetentionPolicy.SOURCE) - @interface ShowFlags {} /** * Flag for {@link #showSoftInput}: this show has been explicitly @@ -299,6 +288,8 @@ public interface InputMethod { /** * Request that any soft input part of the input method be shown to the user. * + * @param flags Provides additional information about the show request. + * Currently may be 0 or have the bit {@link #SHOW_EXPLICIT} set. * @param resultReceiver The client requesting the show may wish to * be told the impact of their request, which should be supplied here. * The result code should be @@ -313,7 +304,7 @@ public interface InputMethod { * @hide */ @MainThread - public default void showSoftInputWithToken(@ShowFlags int flags, ResultReceiver resultReceiver, + public default void showSoftInputWithToken(int flags, ResultReceiver resultReceiver, IBinder showInputToken, @Nullable ImeTracker.Token statsToken) { showSoftInput(flags, resultReceiver); } @@ -321,6 +312,8 @@ public interface InputMethod { /** * Request that any soft input part of the input method be shown to the user. * + * @param flags Provides additional information about the show request. + * Currently may be 0 or have the bit {@link #SHOW_EXPLICIT} set. * @param resultReceiver The client requesting the show may wish to * be told the impact of their request, which should be supplied here. * The result code should be @@ -330,12 +323,11 @@ public interface InputMethod { * {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}. */ @MainThread - public void showSoftInput(@ShowFlags int flags, ResultReceiver resultReceiver); + public void showSoftInput(int flags, ResultReceiver resultReceiver); /** * Request that any soft input part of the input method be hidden from the user. - * - * @param flags Provides additional information about the hide request. + * @param flags Provides additional information about the show request. * Currently always 0. * @param resultReceiver The client requesting the show may wish to * be told the impact of their request, which should be supplied here. @@ -358,8 +350,7 @@ public interface InputMethod { /** * Request that any soft input part of the input method be hidden from the user. - * - * @param flags Provides additional information about the hide request. + * @param flags Provides additional information about the show request. * Currently always 0. * @param resultReceiver The client requesting the show may wish to * be told the impact of their request, which should be supplied here. diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 52b7d1d41727..3f308e6fccee 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -39,7 +39,6 @@ import android.Manifest; import android.annotation.DisplayContext; import android.annotation.DrawableRes; import android.annotation.DurationMillisLong; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; @@ -122,8 +121,6 @@ import com.android.internal.view.IInputMethodManager; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collections; @@ -2007,14 +2004,6 @@ public final class InputMethodManager { } } - /** @hide */ - @IntDef(flag = true, prefix = { "SHOW_" }, value = { - SHOW_IMPLICIT, - SHOW_FORCED, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ShowFlags {} - /** * Flag for {@link #showSoftInput} to indicate that this is an implicit * request to show the input window, not as the result of a direct request @@ -2046,8 +2035,10 @@ public final class InputMethodManager { * {@link View#isFocused view focus}, and its containing window has * {@link View#hasWindowFocus window focus}. Otherwise the call fails and * returns {@code false}. + * @param flags Provides additional operating flags. Currently may be + * 0 or have the {@link #SHOW_IMPLICIT} bit set. */ - public boolean showSoftInput(View view, @ShowFlags int flags) { + public boolean showSoftInput(View view, int flags) { // Re-dispatch if there is a context mismatch. final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); if (fallbackImm != null) { @@ -2110,20 +2101,21 @@ public final class InputMethodManager { * {@link View#isFocused view focus}, and its containing window has * {@link View#hasWindowFocus window focus}. Otherwise the call fails and * returns {@code false}. + * @param flags Provides additional operating flags. Currently may be + * 0 or have the {@link #SHOW_IMPLICIT} bit set. * @param resultReceiver If non-null, this will be called by the IME when * it has processed your request to tell you what it has done. The result * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or * {@link #RESULT_HIDDEN}. */ - public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) { + public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) { return showSoftInput(view, null /* statsToken */, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT); } - private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken, - @ShowFlags int flags, ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason) { + private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken, int flags, + ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { if (statsToken == null) { statsToken = ImeTracker.forLogging().onRequestShow(null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, reason); @@ -2177,7 +2169,7 @@ public final class InputMethodManager { */ @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768499) - public void showSoftInputUnchecked(@ShowFlags int flags, ResultReceiver resultReceiver) { + public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) { synchronized (mH) { final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestShow( null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, @@ -2208,14 +2200,6 @@ public final class InputMethodManager { } } - /** @hide */ - @IntDef(flag = true, prefix = { "HIDE_" }, value = { - HIDE_IMPLICIT_ONLY, - HIDE_NOT_ALWAYS, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface HideFlags {} - /** * Flag for {@link #hideSoftInputFromWindow} and {@link InputMethodService#requestHideSelf(int)} * to indicate that the soft input window should only be hidden if it was not explicitly shown @@ -2237,8 +2221,10 @@ public final class InputMethodManager { * * @param windowToken The token of the window that is making the request, * as returned by {@link View#getWindowToken() View.getWindowToken()}. + * @param flags Provides additional operating flags. Currently may be + * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. */ - public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) { + public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) { return hideSoftInputFromWindow(windowToken, flags, null); } @@ -2260,19 +2246,21 @@ public final class InputMethodManager { * * @param windowToken The token of the window that is making the request, * as returned by {@link View#getWindowToken() View.getWindowToken()}. + * @param flags Provides additional operating flags. Currently may be + * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. * @param resultReceiver If non-null, this will be called by the IME when * it has processed your request to tell you what it has done. The result * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or * {@link #RESULT_HIDDEN}. */ - public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags, + public boolean hideSoftInputFromWindow(IBinder windowToken, int flags, ResultReceiver resultReceiver) { return hideSoftInputFromWindow(windowToken, flags, resultReceiver, SoftInputShowHideReason.HIDE_SOFT_INPUT); } - private boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags, + private boolean hideSoftInputFromWindow(IBinder windowToken, int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide( null /* component */, Process.myUid(), @@ -2475,6 +2463,12 @@ public final class InputMethodManager { * If not the input window will be displayed. * @param windowToken The token of the window that is making the request, * as returned by {@link View#getWindowToken() View.getWindowToken()}. + * @param showFlags Provides additional operating flags. May be + * 0 or have the {@link #SHOW_IMPLICIT}, + * {@link #SHOW_FORCED} bit set. + * @param hideFlags Provides additional operating flags. May be + * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, + * {@link #HIDE_NOT_ALWAYS} bit set. * * @deprecated Use {@link #showSoftInput(View, int)} or * {@link #hideSoftInputFromWindow(IBinder, int)} explicitly instead. @@ -2483,8 +2477,7 @@ public final class InputMethodManager { * has an effect if the calling app is the current IME focus. */ @Deprecated - public void toggleSoftInputFromWindow(IBinder windowToken, @ShowFlags int showFlags, - @HideFlags int hideFlags) { + public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) { ImeTracing.getInstance().triggerClientDump( "InputMethodManager#toggleSoftInputFromWindow", InputMethodManager.this, null /* icProto */); @@ -2502,6 +2495,12 @@ public final class InputMethodManager { * * If the input window is already displayed, it gets hidden. * If not the input window will be displayed. + * @param showFlags Provides additional operating flags. May be + * 0 or have the {@link #SHOW_IMPLICIT}, + * {@link #SHOW_FORCED} bit set. + * @param hideFlags Provides additional operating flags. May be + * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, + * {@link #HIDE_NOT_ALWAYS} bit set. * * @deprecated Use {@link #showSoftInput(View, int)} or * {@link #hideSoftInputFromWindow(IBinder, int)} explicitly instead. @@ -2510,7 +2509,7 @@ public final class InputMethodManager { * has an effect if the calling app is the current IME focus. */ @Deprecated - public void toggleSoftInput(@ShowFlags int showFlags, @HideFlags int hideFlags) { + public void toggleSoftInput(int showFlags, int hideFlags) { ImeTracing.getInstance().triggerClientDump( "InputMethodManager#toggleSoftInput", InputMethodManager.this, null /* icProto */); @@ -3523,12 +3522,15 @@ public final class InputMethodManager { * @param token Supplies the identifying token given to an input method * when it was started, which allows it to perform this operation on * itself. + * @param flags Provides additional operating flags. Currently may be + * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, + * {@link #HIDE_NOT_ALWAYS} bit set. * @deprecated Use {@link InputMethodService#requestHideSelf(int)} instead. This method was * intended for IME developers who should be accessing APIs through the service. APIs in this * class are intended for app developers interacting with the IME. */ @Deprecated - public void hideSoftInputFromInputMethod(IBinder token, @HideFlags int flags) { + public void hideSoftInputFromInputMethod(IBinder token, int flags) { InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput( flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION); } @@ -3542,12 +3544,15 @@ public final class InputMethodManager { * @param token Supplies the identifying token given to an input method * when it was started, which allows it to perform this operation on * itself. + * @param flags Provides additional operating flags. Currently may be + * 0 or have the {@link #SHOW_IMPLICIT} or + * {@link #SHOW_FORCED} bit set. * @deprecated Use {@link InputMethodService#requestShowSelf(int)} instead. This method was * intended for IME developers who should be accessing APIs through the service. APIs in this * class are intended for app developers interacting with the IME. */ @Deprecated - public void showSoftInputFromInputMethod(IBinder token, @ShowFlags int flags) { + public void showSoftInputFromInputMethod(IBinder token, int flags) { InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags); } diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java index 4f48cb684e8c..af6af14472db 100644 --- a/core/java/android/view/inputmethod/InputMethodSession.java +++ b/core/java/android/view/inputmethod/InputMethodSession.java @@ -169,6 +169,12 @@ public interface InputMethodSession { /** * Toggle the soft input window. * Applications can toggle the state of the soft input window. + * @param showFlags Provides additional operating flags. May be + * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT}, + * {@link InputMethodManager#SHOW_FORCED} bit set. + * @param hideFlags Provides additional operating flags. May be + * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY}, + * {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set. * * @deprecated Starting in {@link android.os.Build.VERSION_CODES#S} the system no longer invokes * this method, instead it explicitly shows or hides the IME. An {@code InputMethodService} @@ -176,8 +182,7 @@ public interface InputMethodSession { * InputMethodService#requestShowSelf} or {@link InputMethodService#requestHideSelf} */ @Deprecated - public void toggleSoftInput(@InputMethodManager.ShowFlags int showFlags, - @InputMethodManager.HideFlags int hideFlags); + public void toggleSoftInput(int showFlags, int hideFlags); /** * This method is called when the cursor and/or the character position relevant to text input 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/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index 8a5c7ef18621..66e3333acf7c 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -26,7 +26,6 @@ import android.os.RemoteException; import android.util.Log; import android.view.View; import android.view.inputmethod.ImeTracker; -import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.GuardedBy; @@ -254,11 +253,13 @@ public final class InputMethodPrivilegedOperations { /** * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, int, AndroidFuture)} * + * @param flags additional operating flags * @param reason the reason to hide soft input + * @see android.view.inputmethod.InputMethodManager#HIDE_IMPLICIT_ONLY + * @see android.view.inputmethod.InputMethodManager#HIDE_NOT_ALWAYS */ @AnyThread - public void hideMySoftInput(@InputMethodManager.HideFlags int flags, - @SoftInputShowHideReason int reason) { + public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason) { final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); if (ops == null) { return; @@ -274,9 +275,13 @@ public final class InputMethodPrivilegedOperations { /** * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int, AndroidFuture)} + * + * @param flags additional operating flags + * @see android.view.inputmethod.InputMethodManager#SHOW_IMPLICIT + * @see android.view.inputmethod.InputMethodManager#SHOW_FORCED */ @AnyThread - public void showMySoftInput(@InputMethodManager.ShowFlags int flags) { + public void showMySoftInput(int flags) { final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); if (ops == null) { return; 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/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/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/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/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java index 8b220242f7ad..6a5749cc5571 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java @@ -30,9 +30,8 @@ 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; @@ -130,7 +129,6 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> TunerService tunerService, @Main Handler mainHandler, ContentResolver contentResolver, - FeatureFlags featureFlags, BatteryController batteryController) { super(view); mLocation = location; @@ -142,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/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index f4da08d765c4..83c1c7100761 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -372,22 +372,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") diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt index 023bfaa2997c..8ec8d115de78 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt @@ -282,7 +282,6 @@ abstract class ShadeModule { tunerService: TunerService, @Main mainHandler: Handler, contentResolver: ContentResolver, - featureFlags: FeatureFlags, batteryController: BatteryController, ): BatteryMeterViewController { return BatteryMeterViewController( @@ -293,7 +292,6 @@ abstract class ShadeModule { tunerService, mainHandler, contentResolver, - featureFlags, batteryController, ) } 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/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/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/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/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/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/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/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java index 87811fb8fac0..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,9 +33,8 @@ 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; @@ -62,7 +61,6 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { private Handler mHandler; @Mock private ContentResolver mContentResolver; - private FakeFeatureFlags mFeatureFlags; @Mock private BatteryController mBatteryController; @@ -75,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 @@ -135,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(); @@ -144,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(); @@ -160,7 +160,6 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { mTunerService, mHandler, mContentResolver, - mFeatureFlags, mBatteryController ); } 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/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/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/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java index f1698dd0f025..a1b67e105dd4 100644 --- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java @@ -37,7 +37,6 @@ import android.os.ResultReceiver; import android.util.EventLog; import android.util.Slog; import android.view.inputmethod.ImeTracker; -import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodManager; import com.android.internal.annotations.GuardedBy; @@ -76,8 +75,7 @@ final class DefaultImeVisibilityApplier implements ImeVisibilityApplier { @GuardedBy("ImfLock.class") @Override public void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, - @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason) { + int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { final IInputMethodInvoker curMethod = mService.getCurMethodLocked(); if (curMethod != null) { if (DEBUG) { diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java index b12a816738da..c53f1a52306d 100644 --- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java +++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java @@ -30,7 +30,6 @@ import android.view.MotionEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ImeTracker; import android.view.inputmethod.InputBinding; -import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSubtype; import android.window.ImeOnBackInvokedDispatcher; @@ -199,8 +198,8 @@ final class IInputMethodInvoker { // TODO(b/192412909): Convert this back to void method @AnyThread - boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, - @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) { + boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, int flags, + ResultReceiver resultReceiver) { try { mTarget.showSoftInput(showInputToken, statsToken, flags, resultReceiver); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java index 29fa36982351..27f6a89a73b3 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.os.IBinder; import android.os.ResultReceiver; import android.view.inputmethod.ImeTracker; -import android.view.inputmethod.InputMethod; import com.android.internal.inputmethod.SoftInputShowHideReason; @@ -35,13 +34,13 @@ interface ImeVisibilityApplier { * * @param showInputToken A token that represents the requester to show IME. * @param statsToken A token that tracks the progress of an IME request. + * @param showFlags Provides additional operating flags to show IME. * @param resultReceiver If non-null, this will be called back to the caller when * it has processed request to tell what it has done. * @param reason The reason for requesting to show IME. */ default void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, - @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason) {} + int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {} /** * Performs hiding IME to the given window diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java index 9ad4628596fc..f012d917b05e 100644 --- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java +++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java @@ -221,21 +221,17 @@ public final class ImeVisibilityStateComputer { /** * Called when {@link InputMethodManagerService} is processing the show IME request. - * - * @param statsToken The token for tracking this show request. - * @return {@code true} when the show request can proceed. + * @param statsToken The token for tracking this show request + * @param showFlags The additional operation flags to indicate whether this show request mode is + * implicit or explicit. + * @return {@code true} when the computer has proceed this show request operation. */ - boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken, - @InputMethodManager.ShowFlags int showFlags) { + boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken, int showFlags) { if (mPolicy.mA11yRequestingNoSoftKeyboard || mPolicy.mImeHiddenByDisplayPolicy) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY); return false; } ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY); - // We only "set" the state corresponding to the flags, as this will be reset - // in clearImeShowFlags during a hide request. - // Thus, we keep the strongest values set (e.g. an implicit show right after - // an explicit show will still be considered explicit, likewise for forced). if ((showFlags & InputMethodManager.SHOW_FORCED) != 0) { mRequestedShowExplicitly = true; mShowForced = true; @@ -247,12 +243,12 @@ public final class ImeVisibilityStateComputer { /** * Called when {@link InputMethodManagerService} is processing the hide IME request. - * - * @param statsToken The token for tracking this hide request. - * @return {@code true} when the hide request can proceed. + * @param statsToken The token for tracking this hide request + * @param hideFlags The additional operation flags to indicate whether this hide request mode is + * implicit or explicit. + * @return {@code true} when the computer has proceed this hide request operations. */ - boolean canHideIme(@NonNull ImeTracker.Token statsToken, - @InputMethodManager.HideFlags int hideFlags) { + boolean canHideIme(@NonNull ImeTracker.Token statsToken, int hideFlags) { if ((hideFlags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 && (mRequestedShowExplicitly || mShowForced)) { if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide"); @@ -268,31 +264,13 @@ public final class ImeVisibilityStateComputer { return true; } - /** - * Returns the show flags for IME. This translates from {@link InputMethodManager.ShowFlags} - * to {@link InputMethod.ShowFlags}. - */ - @InputMethod.ShowFlags - int getShowFlagsForInputMethodServiceOnly() { + int getImeShowFlags() { int flags = 0; if (mShowForced) { flags |= InputMethod.SHOW_FORCED | InputMethod.SHOW_EXPLICIT; } else if (mRequestedShowExplicitly) { flags |= InputMethod.SHOW_EXPLICIT; - } - return flags; - } - - /** - * Returns the show flags for IMM. This translates from {@link InputMethod.ShowFlags} - * to {@link InputMethodManager.ShowFlags}. - */ - @InputMethodManager.ShowFlags - int getShowFlags() { - int flags = 0; - if (mShowForced) { - flags |= InputMethodManager.SHOW_FORCED; - } else if (!mRequestedShowExplicitly) { + } else { flags |= InputMethodManager.SHOW_IMPLICIT; } return flags; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0bf282059066..02ee96a04b1f 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2462,7 +2462,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final ImeTracker.Token statsToken = mCurStatsToken; mCurStatsToken = null; showCurrentInputLocked(mCurFocusedWindow, statsToken, - mVisibilityStateComputer.getShowFlags(), + mVisibilityStateComputer.getImeShowFlags(), null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT); } @@ -3398,9 +3398,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, - @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, - int lastClickTooType, ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason) { + @Nullable ImeTracker.Token statsToken, int flags, int lastClickTooType, + ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput"); int uid = Binder.getCallingUid(); ImeTracing.getInstance().triggerManagerServiceDump( @@ -3573,17 +3572,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") boolean showCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken, - @InputMethodManager.ShowFlags int flags, ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason) { + int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { return showCurrentInputLocked(windowToken, statsToken, flags, MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason); } @GuardedBy("ImfLock.class") private boolean showCurrentInputLocked(IBinder windowToken, - @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags, - int lastClickToolType, ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason) { + @Nullable ImeTracker.Token statsToken, int flags, int lastClickToolType, + ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { // Create statsToken is none exists. if (statsToken == null) { statsToken = createStatsTokenForFocusedClient(true /* show */, @@ -3614,8 +3611,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub curMethod.updateEditorToolType(lastClickToolType); } mVisibilityApplier.performShowIme(windowToken, statsToken, - mVisibilityStateComputer.getShowFlagsForInputMethodServiceOnly(), - resultReceiver, reason); + mVisibilityStateComputer.getImeShowFlags(), resultReceiver, reason); mVisibilityStateComputer.setInputShown(true); return true; } else { @@ -3627,8 +3623,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken, - @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags, - ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { + @Nullable ImeTracker.Token statsToken, int flags, ResultReceiver resultReceiver, + @SoftInputShowHideReason int reason) { int uid = Binder.getCallingUid(); ImeTracing.getInstance().triggerManagerServiceDump( "InputMethodManagerService#hideSoftInput"); @@ -3658,8 +3654,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") boolean hideCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken, - @InputMethodManager.HideFlags int flags, ResultReceiver resultReceiver, - @SoftInputShowHideReason int reason) { + int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { // Create statsToken is none exists. if (statsToken == null) { statsToken = createStatsTokenForFocusedClient(false /* show */, @@ -4846,7 +4841,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread - private void hideMySoftInput(@NonNull IBinder token, @InputMethodManager.HideFlags int flags, + private void hideMySoftInput(@NonNull IBinder token, int flags, @SoftInputShowHideReason int reason) { try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput"); @@ -4868,7 +4863,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread - private void showMySoftInput(@NonNull IBinder token, @InputMethodManager.ShowFlags int flags) { + private void showMySoftInput(@NonNull IBinder token, int flags) { try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput"); synchronized (ImfLock.class) { @@ -6827,8 +6822,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override - public void hideMySoftInput(@InputMethodManager.HideFlags int flags, - @SoftInputShowHideReason int reason, AndroidFuture future /* T=Void */) { + public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason, + AndroidFuture future /* T=Void */) { @SuppressWarnings("unchecked") final AndroidFuture<Void> typedFuture = future; try { @@ -6841,8 +6836,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override - public void showMySoftInput(@InputMethodManager.ShowFlags int flags, - AndroidFuture future /* T=Void */) { + public void showMySoftInput(int flags, AndroidFuture future /* T=Void */) { @SuppressWarnings("unchecked") final AndroidFuture<Void> typedFuture = future; try { 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/InputMethodSystemServerTests/AndroidManifest.xml b/services/tests/InputMethodSystemServerTests/AndroidManifest.xml index bef56cec3385..212ec14b4939 100644 --- a/services/tests/InputMethodSystemServerTests/AndroidManifest.xml +++ b/services/tests/InputMethodSystemServerTests/AndroidManifest.xml @@ -17,6 +17,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.inputmethodtests"> + <uses-sdk android:targetSdkVersion="31" /> <queries> <intent> <action android:name="android.view.InputMethod" /> diff --git a/services/tests/InputMethodSystemServerTests/TEST_MAPPING b/services/tests/InputMethodSystemServerTests/TEST_MAPPING index cedbfd2b2dde..77e32a776d57 100644 --- a/services/tests/InputMethodSystemServerTests/TEST_MAPPING +++ b/services/tests/InputMethodSystemServerTests/TEST_MAPPING @@ -9,16 +9,5 @@ {"exclude-annotation": "org.junit.Ignore"} ] } - ], - "postsubmit": [ - { - "name": "FrameworksImeTests", - "options": [ - {"include-filter": "com.android.inputmethodservice"}, - {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, - {"exclude-annotation": "androidx.test.filters.FlakyTest"}, - {"exclude-annotation": "org.junit.Ignore"} - ] - } ] } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml index b7de74987eb8..0104f7142bea 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml @@ -18,6 +18,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.inputmethod.imetests"> + <uses-sdk android:targetSdkVersion="31" /> + <!-- Permissions required for granting and logging --> <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java index e8acb067f625..898658e759c0 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java @@ -20,8 +20,6 @@ import static com.android.compatibility.common.util.SystemUtil.eventually; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; - import android.app.Instrumentation; import android.content.Context; import android.content.res.Configuration; @@ -47,7 +45,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -59,9 +56,9 @@ public class InputMethodServiceTest { private static final String EDIT_TEXT_DESC = "Input box"; private static final long TIMEOUT_IN_SECONDS = 3; private static final String ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD = - "settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD + " 1"; + "settings put secure show_ime_with_hard_keyboard 1"; private static final String DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD = - "settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD + " 0"; + "settings put secure show_ime_with_hard_keyboard 0"; private Instrumentation mInstrumentation; private UiDevice mUiDevice; @@ -85,19 +82,29 @@ public class InputMethodServiceTest { mUiDevice.freezeRotation(); mUiDevice.setOrientationNatural(); // Waits for input binding ready. - eventually(() -> { - mInputMethodService = - InputMethodServiceWrapper.getInputMethodServiceWrapperForTesting(); - assertThat(mInputMethodService).isNotNull(); - - // The editor won't bring up keyboard by default. - assertThat(mInputMethodService.getCurrentInputStarted()).isTrue(); - assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse(); - }); - // Save the original value of show_ime_with_hard_keyboard from Settings. + eventually( + () -> { + mInputMethodService = + InputMethodServiceWrapper.getInputMethodServiceWrapperForTesting(); + assertThat(mInputMethodService).isNotNull(); + + // The editor won't bring up keyboard by default. + assertThat(mInputMethodService.getCurrentInputStarted()).isTrue(); + assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse(); + }); + // Save the original value of show_ime_with_hard_keyboard in Settings. mShowImeWithHardKeyboardEnabled = Settings.Secure.getInt( mInputMethodService.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0; + // Disable showing Ime with hard keyboard because it is the precondition the for most test + // cases + if (mShowImeWithHardKeyboardEnabled) { + executeShellCommand(DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD); + } + mInputMethodService.getResources().getConfiguration().keyboard = + Configuration.KEYBOARD_NOKEYS; + mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = + Configuration.HARDKEYBOARDHIDDEN_YES; } @After @@ -105,141 +112,82 @@ public class InputMethodServiceTest { mUiDevice.unfreezeRotation(); executeShellCommand("ime disable " + mInputMethodId); // Change back the original value of show_ime_with_hard_keyboard in Settings. - executeShellCommand(mShowImeWithHardKeyboardEnabled - ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD + executeShellCommand(mShowImeWithHardKeyboardEnabled ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD : DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD); } - /** - * This checks that the IME can be shown and hidden by user actions - * (i.e. tapping on an EditText, tapping the Home button). - */ @Test - public void testShowHideKeyboard_byUserAction() throws Exception { - setShowImeWithHardKeyboard(true /* enabled */); - + public void testShowHideKeyboard_byUserAction() throws InterruptedException { // Performs click on editor box to bring up the soft keyboard. Log.i(TAG, "Click on EditText."); - verifyInputViewStatus( - () -> clickOnEditorText(), - true /* expected */, - true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + verifyInputViewStatus(() -> clickOnEditorText(), true /* inputViewStarted */); - // Press home key to hide soft keyboard. - Log.i(TAG, "Press home"); + // Press back key to hide soft keyboard. + Log.i(TAG, "Press back"); verifyInputViewStatus( - () -> assertThat(mUiDevice.pressHome()).isTrue(), - true /* expected */, - false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + () -> assertThat(mUiDevice.pressHome()).isTrue(), false /* inputViewStarted */); } - /** - * This checks that the IME can be shown and hidden using the WindowInsetsController APIs. - */ @Test - public void testShowHideKeyboard_byApi() throws Exception { - setShowImeWithHardKeyboard(true /* enabled */); - + public void testShowHideKeyboard_byApi() throws InterruptedException { // Triggers to show IME via public API. verifyInputViewStatus( () -> assertThat(mActivity.showImeWithWindowInsetsController()).isTrue(), - true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); // Triggers to hide IME via public API. verifyInputViewStatusOnMainSync( - () -> assertThat(mActivity.hideImeWithWindowInsetsController()).isTrue(), - true /* expected */, + () -> assertThat(mActivity.hideImeWithInputMethodManager(0 /* flags */)).isTrue(), false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); } - /** - * This checks the result of calling IMS#requestShowSelf and IMS#requestHideSelf. - */ @Test - public void testShowHideSelf() throws Exception { - setShowImeWithHardKeyboard(true /* enabled */); - - // IME request to show itself without any flags, expect shown. + public void testShowHideSelf() throws InterruptedException { + // IME requests to show itself without any flags: expect shown. Log.i(TAG, "Call IMS#requestShowSelf(0)"); verifyInputViewStatusOnMainSync( - () -> mInputMethodService.requestShowSelf(0 /* flags */), - true /* expected */, - true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); + () -> mInputMethodService.requestShowSelf(0), true /* inputViewStarted */); - // IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect not hide (shown). + // IME requests to hide itself with flag: HIDE_IMPLICIT_ONLY, expect not hide (shown). Log.i(TAG, "Call IMS#requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY)"); verifyInputViewStatusOnMainSync( () -> mInputMethodService.requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY), - false /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - // IME request to hide itself without any flags, expect hidden. + // IME request to hide itself without any flags: expect hidden. Log.i(TAG, "Call IMS#requestHideSelf(0)"); verifyInputViewStatusOnMainSync( - () -> mInputMethodService.requestHideSelf(0 /* flags */), - true /* expected */, - false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + () -> mInputMethodService.requestHideSelf(0), false /* inputViewStarted */); - // IME request to show itself with flag SHOW_IMPLICIT, expect shown. + // IME request to show itself with flag SHOW_IMPLICIT: expect shown. Log.i(TAG, "Call IMS#requestShowSelf(InputMethodManager.SHOW_IMPLICIT)"); verifyInputViewStatusOnMainSync( () -> mInputMethodService.requestShowSelf(InputMethodManager.SHOW_IMPLICIT), - true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - // IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect hidden. + // IME request to hide itself with flag: HIDE_IMPLICIT_ONLY, expect hidden. Log.i(TAG, "Call IMS#requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY)"); verifyInputViewStatusOnMainSync( () -> mInputMethodService.requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY), - true /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); } - /** - * This checks the return value of IMS#onEvaluateInputViewShown, - * when show_ime_with_hard_keyboard is enabled. - */ @Test public void testOnEvaluateInputViewShown_showImeWithHardKeyboard() throws Exception { - setShowImeWithHardKeyboard(true /* enabled */); + executeShellCommand(ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD); + mInstrumentation.waitForIdleSync(); + // Simulate connecting a hard keyboard mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; - eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); - - mInputMethodService.getResources().getConfiguration().keyboard = - Configuration.KEYBOARD_NOKEYS; - mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = - Configuration.HARDKEYBOARDHIDDEN_NO; - eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); - mInputMethodService.getResources().getConfiguration().keyboard = - Configuration.KEYBOARD_QWERTY; - mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = - Configuration.HARDKEYBOARDHIDDEN_YES; eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); } - /** - * This checks the return value of IMSonEvaluateInputViewShown, - * when show_ime_with_hard_keyboard is disabled. - */ @Test - public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() throws Exception { - setShowImeWithHardKeyboard(false /* enabled */); - + public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() { mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = @@ -248,8 +196,6 @@ public class InputMethodServiceTest { mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_NOKEYS; - mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = - Configuration.HARDKEYBOARDHIDDEN_NO; eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); mInputMethodService.getResources().getConfiguration().keyboard = @@ -259,386 +205,149 @@ public class InputMethodServiceTest { eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue()); } - /** - * This checks that any (implicit or explicit) show request, - * when IMS#onEvaluateInputViewShown returns false, results in the IME not being shown. - */ @Test public void testShowSoftInput_disableShowImeWithHardKeyboard() throws Exception { - setShowImeWithHardKeyboard(false /* enabled */); - - // Simulate connecting a hard keyboard. + // Simulate connecting a hard keyboard mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; - // When InputMethodService#onEvaluateInputViewShown() returns false, the Ime should not be // shown no matter what the show flag is. verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), - false /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); - verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), - false /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); } - /** - * This checks that an explicit show request results in the IME being shown. - */ @Test public void testShowSoftInputExplicitly() throws Exception { - setShowImeWithHardKeyboard(true /* enabled */); - // When InputMethodService#onEvaluateInputViewShown() returns true and flag is EXPLICIT, the // Ime should be shown. verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), - true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); } - /** - * This checks that an implicit show request results in the IME being shown. - */ @Test public void testShowSoftInputImplicitly() throws Exception { - setShowImeWithHardKeyboard(true /* enabled */); - - // When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT, - // the IME should be shown. + // When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT, the + // Ime should be shown. verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), - true /* expected */, - true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - } - - /** - * This checks that an explicit show request when the IME is not previously shown, - * and it should be shown in fullscreen mode, results in the IME being shown. - */ - @Test - public void testShowSoftInputExplicitly_fullScreenMode() throws Exception { - setShowImeWithHardKeyboard(true /* enabled */); - - // Set orientation landscape to enable fullscreen mode. - setOrientation(2); - eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse()); - // Wait for the TestActivity to be recreated. - eventually(() -> - assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity)); - // Get the new TestActivity. - mActivity = TestActivity.getLastCreatedInstance(); - assertThat(mActivity).isNotNull(); - InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); - // Wait for the new EditText to be served by InputMethodManager. - eventually(() -> assertThat( - imm.hasActiveInputConnection(mActivity.getEditText())).isTrue()); - - verifyInputViewStatusOnMainSync(() -> assertThat( - mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), - true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); } - /** - * This checks that an implicit show request when the IME is not previously shown, - * and it should be shown in fullscreen mode, results in the IME not being shown. - */ @Test public void testShowSoftInputImplicitly_fullScreenMode() throws Exception { - setShowImeWithHardKeyboard(true /* enabled */); - - // Set orientation landscape to enable fullscreen mode. + // When keyboard is off, InputMethodService#onEvaluateInputViewShown returns true, flag is + // IMPLICIT and InputMethodService#onEvaluateFullScreenMode returns true, the Ime should not + // be shown. setOrientation(2); eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse()); - // Wait for the TestActivity to be recreated. + // Wait for the TestActivity to be recreated eventually(() -> assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity)); - // Get the new TestActivity. + // Get the new TestActivity mActivity = TestActivity.getLastCreatedInstance(); assertThat(mActivity).isNotNull(); InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); - // Wait for the new EditText to be served by InputMethodManager. - eventually(() -> assertThat( - imm.hasActiveInputConnection(mActivity.getEditText())).isTrue()); - + // Wait for the new EditText to be served by InputMethodManager + eventually(() -> + assertThat(imm.hasActiveInputConnection(mActivity.getEditText())).isTrue()); verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), - false /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); } - /** - * This checks that an explicit show request when a hard keyboard is connected, - * results in the IME being shown. - */ - @Test - public void testShowSoftInputExplicitly_withHardKeyboard() throws Exception { - setShowImeWithHardKeyboard(false /* enabled */); - - // Simulate connecting a hard keyboard. - mInputMethodService.getResources().getConfiguration().keyboard = - Configuration.KEYBOARD_QWERTY; - mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = - Configuration.HARDKEYBOARDHIDDEN_YES; - - verifyInputViewStatusOnMainSync(() -> assertThat( - mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), - true /* expected */, - true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - } - - /** - * This checks that an implicit show request when a hard keyboard is connected, - * results in the IME not being shown. - */ @Test public void testShowSoftInputImplicitly_withHardKeyboard() throws Exception { - setShowImeWithHardKeyboard(false /* enabled */); - - // Simulate connecting a hard keyboard. mInputMethodService.getResources().getConfiguration().keyboard = Configuration.KEYBOARD_QWERTY; - mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = - Configuration.HARDKEYBOARDHIDDEN_YES; - + // When connecting to a hard keyboard and the flag is IMPLICIT, the Ime should not be shown. verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), - false /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); } - /** - * This checks that an explicit show request followed by connecting a hard keyboard - * and a configuration change, still results in the IME being shown. - */ @Test - public void testShowSoftInputExplicitly_thenConfigurationChanged() throws Exception { - setShowImeWithHardKeyboard(false /* enabled */); - - // Start with no hard keyboard. - mInputMethodService.getResources().getConfiguration().keyboard = - Configuration.KEYBOARD_NOKEYS; - mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = - Configuration.HARDKEYBOARDHIDDEN_YES; - + public void testConfigurationChanged_withKeyboardShownExplicitly() throws InterruptedException { verifyInputViewStatusOnMainSync( () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), - true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - - // Simulate connecting a hard keyboard. - mInputMethodService.getResources().getConfiguration().keyboard = - Configuration.KEYBOARD_QWERTY; - mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = - Configuration.HARDKEYBOARDHIDDEN_YES; - // Simulate a fake configuration change to avoid triggering the recreation of TestActivity. mInputMethodService.getResources().getConfiguration().orientation = Configuration.ORIENTATION_LANDSCAPE; - verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged( mInputMethodService.getResources().getConfiguration()), - true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); } - /** - * This checks that an implicit show request followed by connecting a hard keyboard - * and a configuration change, does not trigger IMS#onFinishInputView, - * but results in the IME being hidden. - */ @Test - public void testShowSoftInputImplicitly_thenConfigurationChanged() throws Exception { - setShowImeWithHardKeyboard(false /* enabled */); - - // Start with no hard keyboard. - mInputMethodService.getResources().getConfiguration().keyboard = - Configuration.KEYBOARD_NOKEYS; - mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = - Configuration.HARDKEYBOARDHIDDEN_YES; - + public void testConfigurationChanged_withKeyboardShownImplicitly() throws InterruptedException { verifyInputViewStatusOnMainSync(() -> assertThat( mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), - true /* expected */, true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - - // Simulate connecting a hard keyboard. - mInputMethodService.getResources().getConfiguration().keyboard = - Configuration.KEYBOARD_QWERTY; - mInputMethodService.getResources().getConfiguration().keyboard = - Configuration.HARDKEYBOARDHIDDEN_YES; - // Simulate a fake configuration change to avoid triggering the recreation of TestActivity. mInputMethodService.getResources().getConfiguration().orientation = Configuration.ORIENTATION_LANDSCAPE; + mInputMethodService.getResources().getConfiguration().keyboard = + Configuration.KEYBOARD_QWERTY; // Normally, IMS#onFinishInputView will be called when finishing the input view by the user. // But if IMS#hideWindow is called when receiving a new configuration change, we don't // expect that it's user-driven to finish the lifecycle of input view with // IMS#onFinishInputView, because the input view will be re-initialized according to the - // last #mShowInputRequested state. So in this case we treat the input view as still alive. + // last mShowSoftRequested state. So in this case we treat the input view is still alive. verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged( - mInputMethodService.getResources().getConfiguration()), - true /* expected */, + mInputMethodService.getResources().getConfiguration()), true /* inputViewStarted */); assertThat(mInputMethodService.isInputViewShown()).isFalse(); } - /** - * This checks that an explicit show request directly followed by an implicit show request, - * while a hardware keyboard is connected, still results in the IME being shown - * (i.e. the implicit show request is treated as explicit). - */ - @Test - public void testShowSoftInputExplicitly_thenShowSoftInputImplicitly_withHardKeyboard() - throws Exception { - setShowImeWithHardKeyboard(false /* enabled */); - - // Simulate connecting a hard keyboard. - mInputMethodService.getResources().getConfiguration().keyboard = - Configuration.KEYBOARD_QWERTY; - mInputMethodService.getResources().getConfiguration().hardKeyboardHidden = - Configuration.HARDKEYBOARDHIDDEN_YES; - - // Explicit show request. - verifyInputViewStatusOnMainSync(() -> assertThat( - mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), - true /* expected */, - true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - - // Implicit show request. - verifyInputViewStatusOnMainSync(() -> assertThat( - mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(), - false /* expected */, - true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - - // Simulate a fake configuration change to avoid triggering the recreation of TestActivity. - // This should now consider the implicit show request, but keep the state from the - // explicit show request, and thus not hide the keyboard. - verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged( - mInputMethodService.getResources().getConfiguration()), - true /* expected */, - true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - } - - /** - * This checks that a forced show request directly followed by an explicit show request, - * and then a hide not always request, still results in the IME being shown - * (i.e. the explicit show request retains the forced state). - */ - @Test - public void testShowSoftInputForced_testShowSoftInputExplicitly_thenHideSoftInputNotAlways() - throws Exception { - setShowImeWithHardKeyboard(true /* enabled */); - - verifyInputViewStatusOnMainSync(() -> assertThat( - mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_FORCED)).isTrue(), - true /* expected */, - true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - - verifyInputViewStatusOnMainSync(() -> assertThat( - mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(), - false /* expected */, - true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - - verifyInputViewStatusOnMainSync(() -> - mActivity.hideImeWithInputMethodManager(InputMethodManager.HIDE_NOT_ALWAYS), - false /* expected */, - true /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isTrue(); - } - - /** - * This checks that the IME fullscreen mode state is updated after changing orientation. - */ - @Test - public void testFullScreenMode() throws Exception { - setShowImeWithHardKeyboard(true /* enabled */); - - Log.i(TAG, "Set orientation natural"); - verifyFullscreenMode(() -> setOrientation(0), - false /* expected */, - true /* orientationPortrait */); - - Log.i(TAG, "Set orientation left"); - verifyFullscreenMode(() -> setOrientation(1), - true /* expected */, - false /* orientationPortrait */); - - Log.i(TAG, "Set orientation right"); - verifyFullscreenMode(() -> setOrientation(2), - false /* expected */, - false /* orientationPortrait */); - } - - private void verifyInputViewStatus( - Runnable runnable, boolean expected, boolean inputViewStarted) + private void verifyInputViewStatus(Runnable runnable, boolean inputViewStarted) throws InterruptedException { - verifyInputViewStatusInternal(runnable, expected, inputViewStarted, - false /* runOnMainSync */); + verifyInputViewStatusInternal(runnable, inputViewStarted, false /*runOnMainSync*/); } - private void verifyInputViewStatusOnMainSync( - Runnable runnable, boolean expected, boolean inputViewStarted) + private void verifyInputViewStatusOnMainSync(Runnable runnable, boolean inputViewStarted) throws InterruptedException { - verifyInputViewStatusInternal(runnable, expected, inputViewStarted, - true /* runOnMainSync */); + verifyInputViewStatusInternal(runnable, inputViewStarted, true /*runOnMainSync*/); } - /** - * Verifies the status of the Input View after executing the given runnable. - * - * @param runnable the runnable to execute for showing or hiding the IME. - * @param expected whether the runnable is expected to trigger the signal. - * @param inputViewStarted the expected state of the Input View after executing the runnable. - * @param runOnMainSync whether to execute the runnable on the main thread. - */ private void verifyInputViewStatusInternal( - Runnable runnable, boolean expected, boolean inputViewStarted, boolean runOnMainSync) + Runnable runnable, boolean inputViewStarted, boolean runOnMainSync) throws InterruptedException { CountDownLatch signal = new CountDownLatch(1); mInputMethodService.setCountDownLatchForTesting(signal); - // Runnable to trigger onStartInputView() / onFinishInputView() / onConfigurationChanged() + // Runnable to trigger onStartInputView()/ onFinishInputView() if (runOnMainSync) { mInstrumentation.runOnMainSync(runnable); } else { runnable.run(); } + // Waits for onStartInputView() to finish. mInstrumentation.waitForIdleSync(); - boolean completed = signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS); - if (expected && !completed) { - fail("Timed out waiting for" - + " onStartInputView() / onFinishInputView() / onConfigurationChanged()"); - } else if (!expected && completed) { - fail("Unexpected call" - + " onStartInputView() / onFinishInputView() / onConfigurationChanged()"); - } + signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS); // Input is not finished. assertThat(mInputMethodService.getCurrentInputStarted()).isTrue(); assertThat(mInputMethodService.getCurrentInputViewStarted()).isEqualTo(inputViewStarted); } + @Test + public void testFullScreenMode() throws Exception { + Log.i(TAG, "Set orientation natural"); + verifyFullscreenMode(() -> setOrientation(0), true /* orientationPortrait */); + + Log.i(TAG, "Set orientation left"); + verifyFullscreenMode(() -> setOrientation(1), false /* orientationPortrait */); + + Log.i(TAG, "Set orientation right"); + verifyFullscreenMode(() -> setOrientation(2), false /* orientationPortrait */); + } + private void setOrientation(int orientation) { // Simple wrapper for catching RemoteException. try { @@ -657,15 +366,7 @@ public class InputMethodServiceTest { } } - /** - * Verifies the IME fullscreen mode state after executing the given runnable. - * - * @param runnable the runnable to execute for setting the orientation. - * @param expected whether the runnable is expected to trigger the signal. - * @param orientationPortrait whether the orientation is expected to be portrait. - */ - private void verifyFullscreenMode( - Runnable runnable, boolean expected, boolean orientationPortrait) + private void verifyFullscreenMode(Runnable runnable, boolean orientationPortrait) throws InterruptedException { CountDownLatch signal = new CountDownLatch(1); mInputMethodService.setCountDownLatchForTesting(signal); @@ -678,12 +379,7 @@ public class InputMethodServiceTest { } // Waits for onConfigurationChanged() to finish. mInstrumentation.waitForIdleSync(); - boolean completed = signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS); - if (expected && !completed) { - fail("Timed out waiting for onConfigurationChanged()"); - } else if (!expected && completed) { - fail("Unexpected call onConfigurationChanged()"); - } + signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS); clickOnEditorText(); eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isTrue()); @@ -720,21 +416,7 @@ public class InputMethodServiceTest { return mTargetPackageName + "/" + INPUT_METHOD_SERVICE_NAME; } - /** - * Sets the value of show_ime_with_hard_keyboard, only if it is different to the default value. - * - * @param enabled the value to be set. - */ - private void setShowImeWithHardKeyboard(boolean enabled) throws IOException { - if (mShowImeWithHardKeyboardEnabled != enabled) { - executeShellCommand(enabled - ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD - : DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD); - mInstrumentation.waitForIdleSync(); - } - } - - private String executeShellCommand(String cmd) throws IOException { + private String executeShellCommand(String cmd) throws Exception { Log.i(TAG, "Run command: " + cmd); return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) .executeShellCommand(cmd); diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java index 3199e062418f..869497c28def 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java @@ -40,6 +40,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.view.Display; +import android.view.inputmethod.InputMethodManager; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -76,9 +77,9 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe public void testPerformShowIme() throws Exception { synchronized (ImfLock.class) { mVisibilityApplier.performShowIme(new Binder() /* showInputToken */, - null /* statsToken */, 0 /* showFlags */, null, SHOW_SOFT_INPUT); + null /* statsToken */, InputMethodManager.SHOW_IMPLICIT, null, SHOW_SOFT_INPUT); } - verifyShowSoftInput(false, true, 0 /* showFlags */); + verifyShowSoftInput(false, true, InputMethodManager.SHOW_IMPLICIT); } @Test @@ -125,7 +126,7 @@ public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTe @Test public void testApplyImeVisibility_showImeImplicit() throws Exception { mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME_IMPLICIT); - verifyShowSoftInput(true, true, 0 /* showFlags */); + verifyShowSoftInput(true, true, InputMethodManager.SHOW_IMPLICIT); } @Test diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java index fae5f86e4007..a38c1626aea1 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java @@ -106,7 +106,7 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes @Test public void testRequestImeVisibility_showExplicit() { initImeTargetWindowState(mWindowToken); - boolean res = mComputer.onImeShowFlags(null, 0 /* showFlags */); + boolean res = mComputer.onImeShowFlags(null, 0 /* show explicit */); mComputer.requestImeVisibility(mWindowToken, res); final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken); @@ -118,34 +118,6 @@ public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTes assertThat(mComputer.mRequestedShowExplicitly).isTrue(); } - /** - * This checks that the state after an explicit show request does not get reset during - * a subsequent implicit show request, without an intermediary hide request. - */ - @Test - public void testRequestImeVisibility_showExplicit_thenShowImplicit() { - initImeTargetWindowState(mWindowToken); - mComputer.onImeShowFlags(null, 0 /* showFlags */); - assertThat(mComputer.mRequestedShowExplicitly).isTrue(); - - mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT); - assertThat(mComputer.mRequestedShowExplicitly).isTrue(); - } - - /** - * This checks that the state after a forced show request does not get reset during - * a subsequent explicit show request, without an intermediary hide request. - */ - @Test - public void testRequestImeVisibility_showForced_thenShowExplicit() { - initImeTargetWindowState(mWindowToken); - mComputer.onImeShowFlags(null, InputMethodManager.SHOW_FORCED); - assertThat(mComputer.mShowForced).isTrue(); - - mComputer.onImeShowFlags(null, 0 /* showFlags */); - assertThat(mComputer.mShowForced).isTrue(); - } - @Test public void testRequestImeVisibility_showImplicit_a11yNoImePolicy() { // Precondition: set AccessibilityService#SHOW_MODE_HIDDEN policy diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java index e87a34ea17d7..42d373b9bf3e 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java @@ -19,7 +19,6 @@ package com.android.server.inputmethod; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; -import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; @@ -163,10 +162,7 @@ public class InputMethodBindingControllerTest extends InputMethodManagerServiceT assertThat(mBindingController.getCurToken()).isNotNull(); } // Wait for onServiceConnected() - boolean completed = mCountDownLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS); - if (!completed) { - fail("Timed out waiting for onServiceConnected()"); - } + mCountDownLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS); // Verify onServiceConnected() is called and bound successfully. synchronized (ImfLock.class) { diff --git a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp index e1fd2b34d881..8d0e0c4260e8 100644 --- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp +++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp @@ -43,8 +43,6 @@ android_test_helper_app { }, export_package_resources: true, sdk_version: "current", - - certificate: "platform", } android_library { diff --git a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml index cf7d660a68ef..996322de2c5e 100644 --- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml +++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml @@ -18,6 +18,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.apps.inputmethod.simpleime"> + <uses-sdk android:targetSdkVersion="31" /> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <application android:debuggable="true" 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/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/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 |