diff options
34 files changed, 334 insertions, 140 deletions
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java index 4bcc8c499f0d..f302033dee0f 100644 --- a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java +++ b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java @@ -31,6 +31,7 @@ import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; +import android.permission.PermissionManager; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; @@ -107,6 +108,8 @@ public class PackageManagerPerfTest { public void setup() { PackageManager.disableApplicationInfoCache(); PackageManager.disablePackageInfoCache(); + PermissionManager.disablePermissionCache(); + PermissionManager.disablePackageNamePermissionCache(); } @Test diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 7eb9d0f3ea91..fdf9abc49604 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -236,7 +236,7 @@ void BootAnimation::onFirstRef() { ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); preloadAnimation(); - ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms", + ALOGD("%sAnimationPreloadTiming stop time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime()); } } diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 9c1a8e854e92..7bf06b4afc3d 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -271,12 +271,12 @@ package android.net { method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyStatsProviderLimitReached(); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyStatsProviderWarningReached(); - method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); - method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); + method @Deprecated @FlaggedApi("android.net.platform.flags.deprecate_network_policy_callback") @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); + method @Deprecated @FlaggedApi("android.net.platform.flags.deprecate_network_policy_callback") @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); } - public static interface NetworkPolicyManager.NetworkPolicyCallback { - method public default void onUidBlockedReasonChanged(int, int); + @Deprecated @FlaggedApi("android.net.platform.flags.deprecate_network_policy_callback") public static interface NetworkPolicyManager.NetworkPolicyCallback { + method @Deprecated public default void onUidBlockedReasonChanged(int, int); } public class NetworkWatchlistManager { diff --git a/core/java/android/animation/OWNERS b/core/java/android/animation/OWNERS index f3b330a02116..5223c870824c 100644 --- a/core/java/android/animation/OWNERS +++ b/core/java/android/animation/OWNERS @@ -3,3 +3,4 @@ romainguy@google.com tianliu@google.com adamp@google.com +mount@google.com diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index ee82b3f6ad02..76e761d62f20 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3678,7 +3678,9 @@ public class Activity extends ContextThemeWrapper * @see View#findViewById(int) * @see Activity#requireViewById(int) */ - /* TODO(b/347672184): Re-add @Nullable */ + // Strictly speaking this should be marked as @Nullable but the nullability of the return value + // is deliberately left unspecified as idiomatically correct code can make assumptions either + // way based on local context, e.g. layout specification. public <T extends View> T findViewById(@IdRes int id) { return getWindow().findViewById(id); } diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 160d00c8a269..d73d536c4675 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -565,7 +565,9 @@ public class Dialog implements DialogInterface, Window.Callback, * @see View#findViewById(int) * @see Dialog#requireViewById(int) */ - /* TODO(b/347672184): Re-add @Nullable */ + // Strictly speaking this should be marked as @Nullable but the nullability of the return value + // is deliberately left unspecified as idiomatically correct code can make assumptions either + // way based on local context, e.g. layout specification. public <T extends View> T findViewById(@IdRes int id) { return mWindow.findViewById(id); } diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index 0fad979e27cf..1200b4b45712 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -118,6 +118,8 @@ per-file *Task* = file:/services/core/java/com/android/server/wm/OWNERS per-file Window* = file:/services/core/java/com/android/server/wm/OWNERS per-file ConfigurationController.java = file:/services/core/java/com/android/server/wm/OWNERS per-file *ScreenCapture* = file:/services/core/java/com/android/server/wm/OWNERS +per-file ComponentOptions.java = file:/services/core/java/com/android/server/wm/OWNERS + # Multitasking per-file multitasking.aconfig = file:/services/core/java/com/android/server/wm/OWNERS diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 334b2316b268..3e6bbf6cdd0c 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -21,6 +21,7 @@ import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManager.procStateToString; import static android.content.pm.PackageManager.GET_SIGNATURES; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -36,6 +37,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; +import android.net.platform.flags.Flags; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.os.Build; @@ -954,11 +956,24 @@ public class NetworkPolicyManager { * @param executor The {@link Executor} to run the callback on. * @param callback The {@link NetworkPolicyCallback} to be registered. * @hide + * + * @deprecated This API is only supported up to Android version + * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}. On later versions, + * {@link android.net.ConnectivityManager.NetworkCallback} should be used wherever possible. + * + * @throws UnsupportedOperationException when called on Android versions after + * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}. */ + @Deprecated + @FlaggedApi(Flags.FLAG_DEPRECATE_NETWORK_POLICY_CALLBACK) @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable Executor executor, @NonNull NetworkPolicyCallback callback) { + if (Flags.deprecateNetworkPolicyCallback()) { + throw new UnsupportedOperationException("NetworkPolicyCallback is no longer supported." + + " Please use ConnectivityManager APIs instead"); + } if (callback == null) { throw new NullPointerException("Callback cannot be null."); } @@ -974,10 +989,23 @@ public class NetworkPolicyManager { * * @param callback The {@link NetworkPolicyCallback} to be unregistered. * @hide + * + * @deprecated This API is only supported up to Android version + * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}. On later versions, + * {@link android.net.ConnectivityManager.NetworkCallback} should be used wherever possible. + * + * @throws UnsupportedOperationException when called on Android versions after + * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}. */ + @Deprecated + @FlaggedApi(Flags.FLAG_DEPRECATE_NETWORK_POLICY_CALLBACK) @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull NetworkPolicyCallback callback) { + if (Flags.deprecateNetworkPolicyCallback()) { + throw new UnsupportedOperationException("NetworkPolicyCallback is no longer supported." + + " Please use ConnectivityManager APIs instead"); + } if (callback == null) { throw new NullPointerException("Callback cannot be null."); } @@ -990,8 +1018,18 @@ public class NetworkPolicyManager { /** * Interface for the callback to listen for changes to network blocked status of apps. * + * @deprecated This API is only supported up to Android version + * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}. On later versions, this callback will + * <b>not</b> be called when the network blocked status of an app changes. Instead, + * {@link android.net.ConnectivityManager.NetworkCallback} should be used wherever possible. + * + * @see #registerNetworkPolicyCallback(Executor, NetworkPolicyCallback) + * @see #unregisterNetworkPolicyCallback(NetworkPolicyCallback) + * * @hide */ + @FlaggedApi(Flags.FLAG_DEPRECATE_NETWORK_POLICY_CALLBACK) + @Deprecated @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public interface NetworkPolicyCallback { /** diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig index 048c50eb0a19..48eb9680e647 100644 --- a/core/java/android/net/flags.aconfig +++ b/core/java/android/net/flags.aconfig @@ -25,3 +25,13 @@ flag { description: "Flag for registerOffloadEngine API in NsdManager" bug: "294777050" } + +flag { + name: "deprecate_network_policy_callback" + namespace: "backstage_power" + description: "Flag for deprecating NetworkPolicyCallback and related APIs" + bug: "353342610" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java index c9f207cf26e8..50242bad191b 100644 --- a/core/java/android/os/IBinder.java +++ b/core/java/android/os/IBinder.java @@ -29,7 +29,7 @@ import java.io.FileDescriptor; * interface describes the abstract protocol for interacting with a * remotable object. Do not implement this interface directly, instead * extend from {@link Binder}. - * + * * <p>The key IBinder API is {@link #transact transact()} matched by * {@link Binder#onTransact Binder.onTransact()}. These * methods allow you to send a call to an IBinder object and receive a @@ -40,7 +40,7 @@ import java.io.FileDescriptor; * expected behavior when calling an object that exists in the local * process, and the underlying inter-process communication (IPC) mechanism * ensures that these same semantics apply when going across processes. - * + * * <p>The data sent through transact() is a {@link Parcel}, a generic buffer * of data that also maintains some meta-data about its contents. The meta * data is used to manage IBinder object references in the buffer, so that those @@ -51,7 +51,7 @@ import java.io.FileDescriptor; * same IBinder object back. These semantics allow IBinder/Binder objects to * be used as a unique identity (to serve as a token or for other purposes) * that can be managed across processes. - * + * * <p>The system maintains a pool of transaction threads in each process that * it runs in. These threads are used to dispatch all * IPCs coming in from other processes. For example, when an IPC is made from @@ -62,7 +62,7 @@ import java.io.FileDescriptor; * thread in process A returns to allow its execution to continue. In effect, * other processes appear to use as additional threads that you did not create * executing in your own process. - * + * * <p>The Binder system also supports recursion across processes. For example * if process A performs a transaction to process B, and process B while * handling that transaction calls transact() on an IBinder that is implemented @@ -70,7 +70,7 @@ import java.io.FileDescriptor; * transaction to finish will take care of calling Binder.onTransact() on the * object being called by B. This ensures that the recursion semantics when * calling remote binder object are the same as when calling local objects. - * + * * <p>When working with remote objects, you often want to find out when they * are no longer valid. There are three ways this can be determined: * <ul> @@ -83,7 +83,7 @@ import java.io.FileDescriptor; * a {@link DeathRecipient} with the IBinder, which will be called when its * containing process goes away. * </ul> - * + * * @see Binder */ public interface IBinder { @@ -95,17 +95,17 @@ public interface IBinder { * The last transaction code available for user commands. */ int LAST_CALL_TRANSACTION = 0x00ffffff; - + /** * IBinder protocol transaction code: pingBinder(). */ int PING_TRANSACTION = ('_'<<24)|('P'<<16)|('N'<<8)|'G'; - + /** * IBinder protocol transaction code: dump internal state. */ int DUMP_TRANSACTION = ('_'<<24)|('D'<<16)|('M'<<8)|'P'; - + /** * IBinder protocol transaction code: execute a shell command. * @hide @@ -129,7 +129,7 @@ public interface IBinder { * across the platform. To support older code, the default implementation * logs the tweet to the main log as a simple emulation of broadcasting * it publicly over the Internet. - * + * * <p>Also, upon completing the dispatch, the object must make a cup * of tea, return it to the caller, and exclaim "jolly good message * old boy!". @@ -142,7 +142,7 @@ public interface IBinder { * its own like counter, and may display this value to the user to indicate the * quality of the app. This is an optional command that applications do not * need to handle, so the default implementation is to do nothing. - * + * * <p>There is no response returned and nothing about the * system will be functionally affected by it, but it will improve the * app's self-esteem. @@ -185,7 +185,8 @@ public interface IBinder { /** * Limit that should be placed on IPC sizes to keep them safely under the - * transaction buffer limit. + * transaction buffer limit. This is a recommendation, and is not the real + * limit. Transactions should be preferred to be even smaller than this. * @hide */ public static final int MAX_IPC_SIZE = 64 * 1024; @@ -206,7 +207,7 @@ public interface IBinder { /** * Check to see if the object still exists. - * + * * @return Returns false if the * hosting process is gone, otherwise the result (always by default * true) returned by the pingBinder() implementation on the other @@ -221,7 +222,7 @@ public interface IBinder { * true, the process may have died while the call is returning. */ public boolean isBinderAlive(); - + /** * Attempt to retrieve a local implementation of an interface * for this Binder object. If null is returned, you will need @@ -232,7 +233,7 @@ public interface IBinder { /** * Print the object's state into the given stream. - * + * * @param fd The raw file descriptor that the dump is being sent to. * @param args additional arguments to the dump request. */ @@ -280,7 +281,7 @@ public interface IBinder { /** * Perform a generic operation with the object. - * + * * @param code The action to perform. This should * be a number between {@link #FIRST_CALL_TRANSACTION} and * {@link #LAST_CALL_TRANSACTION}. @@ -360,13 +361,13 @@ public interface IBinder { * Remove a previously registered death notification. * The recipient will no longer be called if this object * dies. - * + * * @return {@code true} if the <var>recipient</var> is successfully * unlinked, assuring you that its * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method * will not be called; {@code false} if the target IBinder has already * died, meaning the method has been (or soon will be) called. - * + * * @throws java.util.NoSuchElementException if the given * <var>recipient</var> has not been registered with the IBinder, and * the IBinder is still alive. Note that if the <var>recipient</var> diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 327ebd07df1f..c9c2dcc2dd6f 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -536,7 +536,9 @@ public class DreamService extends Service implements Window.Callback { * @see View#findViewById(int) * @see DreamService#requireViewById(int) */ - /* TODO(b/347672184): Re-add @Nullable */ + // Strictly speaking this should be marked as @Nullable but the nullability of the return value + // is deliberately left unspecified as idiomatically correct code can make assumptions either + // way based on local context, e.g. layout specification. public <T extends View> T findViewById(@IdRes int id) { return getWindow().findViewById(id); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ba6d5df6494b..9aa723aa88e1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -27294,7 +27294,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return a view with given ID if found, or {@code null} otherwise * @see View#requireViewById(int) */ - /* TODO(b/347672184): Re-add @Nullable */ + // Strictly speaking this should be marked as @Nullable but the nullability of the return value + // is deliberately left unspecified as idiomatically correct code can make assumptions either + // way based on local context, e.g. layout specification. public final <T extends View> T findViewById(@IdRes int id) { if (id == NO_ID) { return null; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index bfdcd5977cd7..50cf5a5fa975 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1712,7 +1712,9 @@ public abstract class Window { * @see View#findViewById(int) * @see Window#requireViewById(int) */ - /* TODO(b/347672184): Re-add @Nullable */ + // Strictly speaking this should be marked as @Nullable but the nullability of the return value + // is deliberately left unspecified as idiomatically correct code can make assumptions either + // way based on local context, e.g. layout specification. public <T extends View> T findViewById(@IdRes int id) { return getDecorView().findViewById(id); } diff --git a/core/tests/coretests/src/android/animation/OWNERS b/core/tests/coretests/src/android/animation/OWNERS new file mode 100644 index 000000000000..1eefb3a3dc65 --- /dev/null +++ b/core/tests/coretests/src/android/animation/OWNERS @@ -0,0 +1 @@ +include /core/java/android/animation/OWNERS diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 65615e62c3df..7b96699f7f71 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -325,10 +325,12 @@ <!-- These are the standard packages that are allowed to always have internet access while in power save mode, even if they aren't in the foreground. --> <allow-in-power-save package="com.android.providers.downloads" /> + <allow-in-power-save package="com.android.rkpdapp" /> <!-- These are the standard packages that are allowed to always have internet access while in data mode, even if they aren't in the foreground. --> <allow-in-data-usage-save package="com.android.providers.downloads" /> + <allow-in-data-usage-save package="com.android.rkpdapp" /> <!-- This is a core platform component that needs to freely run in the background --> <allow-in-power-save package="com.android.cellbroadcastreceiver.module" /> diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java index 7c7cb18afc4d..9887c272e7f8 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java @@ -55,9 +55,9 @@ import com.sun.source.util.TreeScanner; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; -import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.ClassType; +import com.sun.tools.javac.tree.JCTree.JCNewClass; import java.util.ArrayList; import java.util.Arrays; @@ -67,7 +67,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Predicate; import java.util.regex.Pattern; import javax.lang.model.element.Name; @@ -125,6 +124,12 @@ public final class RequiresPermissionChecker extends BugChecker instanceMethod() .onDescendantOf("android.content.Context") .withNameMatching(Pattern.compile("^send(Ordered|Sticky)?Broadcast.*$"))); + private static final Matcher<ExpressionTree> SEND_BROADCAST_AS_USER = + methodInvocation( + instanceMethod() + .onDescendantOf("android.content.Context") + .withNameMatching( + Pattern.compile("^send(Ordered|Sticky)?Broadcast.*AsUser.*$"))); private static final Matcher<ExpressionTree> SEND_PENDING_INTENT = methodInvocation( instanceMethod() .onDescendantOf("android.app.PendingIntent") @@ -306,18 +311,6 @@ public final class RequiresPermissionChecker extends BugChecker } } - private static ExpressionTree findArgumentByParameterName(MethodInvocationTree tree, - Predicate<String> paramName) { - final MethodSymbol sym = ASTHelpers.getSymbol(tree); - final List<VarSymbol> params = sym.getParameters(); - for (int i = 0; i < params.size(); i++) { - if (paramName.test(params.get(i).name.toString())) { - return tree.getArguments().get(i); - } - } - return null; - } - private static Name resolveName(ExpressionTree tree) { if (tree instanceof IdentifierTree) { return ((IdentifierTree) tree).getName(); @@ -345,76 +338,85 @@ public final class RequiresPermissionChecker extends BugChecker private static ParsedRequiresPermission parseBroadcastSourceRequiresPermission( MethodInvocationTree methodTree, VisitorState state) { - final ExpressionTree arg = findArgumentByParameterName(methodTree, - (name) -> name.toLowerCase().contains("intent")); - if (arg instanceof IdentifierTree) { - final Name argName = ((IdentifierTree) arg).getName(); - final MethodTree method = state.findEnclosing(MethodTree.class); - final AtomicReference<ParsedRequiresPermission> res = new AtomicReference<>(); - method.accept(new TreeScanner<Void, Void>() { - private ParsedRequiresPermission last; - - @Override - public Void visitMethodInvocation(MethodInvocationTree tree, Void param) { - if (Objects.equal(methodTree, tree)) { - res.set(last); - } else { - final Name name = resolveName(tree.getMethodSelect()); - if (Objects.equal(argName, name) - && INTENT_SET_ACTION.matches(tree, state)) { - last = parseIntentAction(tree); + if (methodTree.getArguments().size() < 1) { + return null; + } + final ExpressionTree arg = methodTree.getArguments().get(0); + if (!(arg instanceof IdentifierTree)) { + return null; + } + final Name argName = ((IdentifierTree) arg).getName(); + final MethodTree method = state.findEnclosing(MethodTree.class); + final AtomicReference<ParsedRequiresPermission> res = new AtomicReference<>(); + method.accept(new TreeScanner<Void, Void>() { + private ParsedRequiresPermission mLast; + + @Override + public Void visitMethodInvocation(MethodInvocationTree tree, Void param) { + if (Objects.equal(methodTree, tree)) { + res.set(mLast); + } else { + final Name name = resolveName(tree.getMethodSelect()); + if (Objects.equal(argName, name) && INTENT_SET_ACTION.matches(tree, state)) { + mLast = parseIntentAction(tree); + } else if (name == null && tree.getMethodSelect() instanceof MemberSelectTree) { + ExpressionTree innerTree = + ((MemberSelectTree) tree.getMethodSelect()).getExpression(); + if (innerTree instanceof JCNewClass) { + mLast = parseIntentAction((NewClassTree) innerTree); } } - return super.visitMethodInvocation(tree, param); } + return super.visitMethodInvocation(tree, param); + } - @Override - public Void visitAssignment(AssignmentTree tree, Void param) { - final Name name = resolveName(tree.getVariable()); - final Tree init = tree.getExpression(); - if (Objects.equal(argName, name) - && init instanceof NewClassTree) { - last = parseIntentAction((NewClassTree) init); - } - return super.visitAssignment(tree, param); + @Override + public Void visitAssignment(AssignmentTree tree, Void param) { + final Name name = resolveName(tree.getVariable()); + final Tree init = tree.getExpression(); + if (Objects.equal(argName, name) && init instanceof NewClassTree) { + mLast = parseIntentAction((NewClassTree) init); } + return super.visitAssignment(tree, param); + } - @Override - public Void visitVariable(VariableTree tree, Void param) { - final Name name = tree.getName(); - final ExpressionTree init = tree.getInitializer(); - if (Objects.equal(argName, name) - && init instanceof NewClassTree) { - last = parseIntentAction((NewClassTree) init); - } - return super.visitVariable(tree, param); + @Override + public Void visitVariable(VariableTree tree, Void param) { + final Name name = tree.getName(); + final ExpressionTree init = tree.getInitializer(); + if (Objects.equal(argName, name) && init instanceof NewClassTree) { + mLast = parseIntentAction((NewClassTree) init); } - }, null); - return res.get(); - } - return null; + return super.visitVariable(tree, param); + } + }, null); + return res.get(); } private static ParsedRequiresPermission parseBroadcastTargetRequiresPermission( MethodInvocationTree tree, VisitorState state) { - final ExpressionTree arg = findArgumentByParameterName(tree, - (name) -> name.toLowerCase().contains("permission")); final ParsedRequiresPermission res = new ParsedRequiresPermission(); - if (arg != null) { - arg.accept(new TreeScanner<Void, Void>() { - @Override - public Void visitIdentifier(IdentifierTree tree, Void param) { - res.addConstValue(tree); - return super.visitIdentifier(tree, param); - } - - @Override - public Void visitMemberSelect(MemberSelectTree tree, Void param) { - res.addConstValue(tree); - return super.visitMemberSelect(tree, param); - } - }, null); + int permission_position = 1; + if (SEND_BROADCAST_AS_USER.matches(tree, state)) { + permission_position = 2; } + if (tree.getArguments().size() < permission_position + 1) { + return res; + } + final ExpressionTree arg = tree.getArguments().get(permission_position); + arg.accept(new TreeScanner<Void, Void>() { + @Override + public Void visitIdentifier(IdentifierTree tree, Void param) { + res.addConstValue(tree); + return super.visitIdentifier(tree, param); + } + + @Override + public Void visitMemberSelect(MemberSelectTree tree, Void param) { + res.addConstValue(tree); + return super.visitMemberSelect(tree, param); + } + }, null); return res; } diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java index e53372d97f3d..05fde7c4fe57 100644 --- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java @@ -412,6 +412,19 @@ public class RequiresPermissionCheckerTest { " context.sendBroadcast(intent);", " }", " }", + " public void exampleWithChainedMethod(Context context) {", + " Intent intent = new Intent(FooManager.ACTION_RED)", + " .putExtra(\"foo\", 42);", + " context.sendBroadcast(intent, FooManager.PERMISSION_RED);", + " context.sendBroadcastWithMultiplePermissions(intent,", + " new String[] { FooManager.PERMISSION_RED });", + " }", + " public void exampleWithAsUser(Context context) {", + " Intent intent = new Intent(FooManager.ACTION_RED);", + " context.sendBroadcastAsUser(intent, 42, FooManager.PERMISSION_RED);", + " context.sendBroadcastAsUserMultiplePermissions(intent, 42,", + " new String[] { FooManager.PERMISSION_RED });", + " }", "}") .doTest(); } diff --git a/errorprone/tests/res/android/content/Context.java b/errorprone/tests/res/android/content/Context.java index efc4fb122435..9d622ffaf120 100644 --- a/errorprone/tests/res/android/content/Context.java +++ b/errorprone/tests/res/android/content/Context.java @@ -36,4 +36,15 @@ public class Context { public void sendBroadcastWithMultiplePermissions(Intent intent, String[] receiverPermissions) { throw new UnsupportedOperationException(); } + + /* Fake user type for test purposes */ + public void sendBroadcastAsUser(Intent intent, int user, String receiverPermission) { + throw new UnsupportedOperationException(); + } + + /* Fake user type for test purposes */ + public void sendBroadcastAsUserMultiplePermissions( + Intent intent, int user, String[] receiverPermissions) { + throw new UnsupportedOperationException(); + } } diff --git a/errorprone/tests/res/android/content/Intent.java b/errorprone/tests/res/android/content/Intent.java index 288396e60577..7ccea784754a 100644 --- a/errorprone/tests/res/android/content/Intent.java +++ b/errorprone/tests/res/android/content/Intent.java @@ -24,4 +24,8 @@ public class Intent { public Intent setAction(String action) { throw new UnsupportedOperationException(); } + + public Intent putExtra(String extra, int value) { + throw new UnsupportedOperationException(); + } } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 293c561f166c..47adde49b86e 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1636,7 +1636,8 @@ public class AudioSystem /** @hide */ public static final int FORCE_ENCODED_SURROUND_NEVER = 13; /** @hide */ public static final int FORCE_ENCODED_SURROUND_ALWAYS = 14; /** @hide */ public static final int FORCE_ENCODED_SURROUND_MANUAL = 15; - /** @hide */ public static final int NUM_FORCE_CONFIG = 16; + /** @hide */ public static final int FORCE_BT_BLE = 16; + /** @hide */ public static final int NUM_FORCE_CONFIG = 17; /** @hide */ public static final int FORCE_DEFAULT = FORCE_NONE; /** @hide */ @@ -1658,6 +1659,7 @@ public class AudioSystem case FORCE_ENCODED_SURROUND_NEVER: return "FORCE_ENCODED_SURROUND_NEVER"; case FORCE_ENCODED_SURROUND_ALWAYS: return "FORCE_ENCODED_SURROUND_ALWAYS"; case FORCE_ENCODED_SURROUND_MANUAL: return "FORCE_ENCODED_SURROUND_MANUAL"; + case FORCE_BT_BLE: return "FORCE_BT_BLE"; default: return "unknown config (" + config + ")" ; } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt index beb8ddef949d..24cbd1036770 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt @@ -264,7 +264,7 @@ fun LazyGridItemScope.DraggableItem( alpha = itemAlpha } } else { - Modifier.animateItemPlacement() + Modifier.animateItem() } Box(modifier) { diff --git a/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java b/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java index e6055148867d..2009cd3d4527 100644 --- a/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java +++ b/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java @@ -659,6 +659,8 @@ class FlashNotificationsController { mIsTorchTouched = on; } catch (CameraAccessException e) { Log.e(LOG_TAG, "Failed to setTorchMode: " + e); + } catch (IllegalArgumentException e) { + Log.e(LOG_TAG, "Failed to setTorchMode: " + e); } } else { Log.e(LOG_TAG, "Can not use camera flash notification, please check CameraManager!"); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index f1eea728bedd..33ad49edfe87 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -787,6 +787,14 @@ public class AudioDeviceBroker { return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BLUETOOTH_SCO); } + /*package*/ boolean isBluetoothBleHeadsetActive() { + return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BLE_HEADSET); + } + + /*package*/ boolean isBluetoothBleSpeakerActive() { + return isDeviceActiveForCommunication(AudioDeviceInfo.TYPE_BLE_SPEAKER); + } + /*package*/ boolean isDeviceConnected(@NonNull AudioDeviceAttributes device) { synchronized (mDeviceStateLock) { return mDeviceInventory.isDeviceConnected(device); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 3f49b90ad514..6787fa6a68c6 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -5665,16 +5665,25 @@ public class AudioService extends IAudioService.Stub || ringerMode == AudioManager.RINGER_MODE_SILENT; final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE && mDeviceBroker.isBluetoothScoActive(); - // Ask audio policy engine to force use Bluetooth SCO channel if needed + final boolean shouldRingBle = ringerMode == AudioManager.RINGER_MODE_VIBRATE + && (mDeviceBroker.isBluetoothBleHeadsetActive() + || mDeviceBroker.isBluetoothBleSpeakerActive()); + // Ask audio policy engine to force use Bluetooth SCO/BLE channel if needed final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid() + "/" + Binder.getCallingPid(); + int forceUse = AudioSystem.FORCE_NONE; + if (shouldRingSco) { + forceUse = AudioSystem.FORCE_BT_SCO; + } else if (shouldRingBle) { + forceUse = AudioSystem.FORCE_BT_BLE; + } sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, AudioSystem.FOR_VIBRATE_RINGING, - shouldRingSco ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE, eventSource, 0); + forceUse, eventSource, 0); for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { final boolean isMuted = isStreamMutedByRingerOrZenMode(streamType); final boolean muteAllowedBySco = - !(shouldRingSco && streamType == AudioSystem.STREAM_RING); + !((shouldRingSco || shouldRingBle) && streamType == AudioSystem.STREAM_RING); final boolean shouldZenMute = isStreamAffectedByCurrentZen(streamType); final boolean shouldMute = shouldZenMute || (ringerModeMute && isStreamAffectedByRingerMode(streamType) && muteAllowedBySco); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index 17f2fcc5b9d8..bb35b378866b 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -340,6 +340,11 @@ class LockSettingsShellCommand extends ShellCommand { getOutPrintWriter().println("Profile uses unified challenge"); return false; } + if (mOld.isEmpty()) { + getOutPrintWriter().println( + "User has a lock credential, but old credential was not provided"); + return false; + } try { final boolean result = mLockPatternUtils.checkCredential(getOldCredential(), diff --git a/telephony/OWNERS b/telephony/OWNERS index 7607c64150d8..92af034217a9 100644 --- a/telephony/OWNERS +++ b/telephony/OWNERS @@ -15,4 +15,4 @@ per-file CarrierConfigManager.java=set noparent per-file CarrierConfigManager.java=amruthr@google.com,tgunn@google.com,rgreenwalt@google.com,satk@google.com #Domain Selection is jointly owned, add additional owners for domain selection specific files -per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com,jdyou@google.com +per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,jaesikkong@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com,jdyou@google.com diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index 031dd5bb139c..9b8c3b3d549c 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -836,6 +836,28 @@ int CompileCommand::Action(const std::vector<std::string>& args) { return 1; } + // Parse the feature flag values. An argument that starts with '@' points to a file to read flag + // values from. + std::vector<std::string> all_feature_flags_args; + for (const std::string& arg : feature_flags_args_) { + if (util::StartsWith(arg, "@")) { + const std::string path = arg.substr(1, arg.size() - 1); + std::string error; + if (!file::AppendArgsFromFile(path, &all_feature_flags_args, &error)) { + context.GetDiagnostics()->Error(android::DiagMessage(path) << error); + return 1; + } + } else { + all_feature_flags_args.push_back(arg); + } + } + + for (const std::string& arg : all_feature_flags_args) { + if (!ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) { + return 1; + } + } + return Compile(&context, file_collection.get(), archive_writer.get(), options_); } diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h index 61c5b60adb76..70c8791524c8 100644 --- a/tools/aapt2/cmd/Compile.h +++ b/tools/aapt2/cmd/Compile.h @@ -24,6 +24,7 @@ #include "Command.h" #include "ResourceTable.h" #include "androidfw/IDiagnostics.h" +#include "cmd/Util.h" #include "format/Archive.h" #include "process/IResourceTableConsumer.h" @@ -45,6 +46,7 @@ struct CompileOptions { bool preserve_visibility_of_styleables = false; bool verbose = false; std::optional<std::string> product_; + FeatureFlagValues feature_flag_values; }; /** Parses flags and compiles resources to be used in linking. */ @@ -92,6 +94,12 @@ class CompileCommand : public Command { "Leave only resources specific to the given product. All " "other resources (including defaults) are removed.", &options_.product_); + AddOptionalFlagList("--feature-flags", + "Specify the values of feature flags. The pairs in the argument\n" + "are separated by ',' the name is separated from the value by '='.\n" + "The name can have a suffix of ':ro' to indicate it is read only." + "Example: \"flag1=true,flag2:ro=false,flag3=\" (flag3 has no given value).", + &feature_flags_args_); } int Action(const std::vector<std::string>& args) override; @@ -101,6 +109,7 @@ class CompileCommand : public Command { CompileOptions options_; std::optional<std::string> visibility_; std::optional<std::string> trace_folder_; + std::vector<std::string> feature_flags_args_; }; int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter* output_writer, diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h index 8fe414f4f660..2f17853718ec 100644 --- a/tools/aapt2/cmd/Link.h +++ b/tools/aapt2/cmd/Link.h @@ -332,8 +332,9 @@ class LinkCommand : public Command { AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_); AddOptionalFlagList("--feature-flags", "Specify the values of feature flags. The pairs in the argument\n" - "are separated by ',' and the name is separated from the value by '='.\n" - "Example: \"flag1=true,flag2=false,flag3=\" (flag3 has no given value).", + "are separated by ',' the name is separated from the value by '='.\n" + "The name can have a suffix of ':ro' to indicate it is read only." + "Example: \"flag1=true,flag2:ro=false,flag3=\" (flag3 has no given value).", &feature_flags_args_); AddOptionalSwitch("--non-updatable-system", "Mark the app as a non-updatable system app. This inserts\n" diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp index 678d84628015..7739171b347f 100644 --- a/tools/aapt2/cmd/Util.cpp +++ b/tools/aapt2/cmd/Util.cpp @@ -128,7 +128,7 @@ bool ParseFeatureFlagsParameter(StringPiece arg, android::IDiagnostics* diag, if (parts.size() > 2) { diag->Error(android::DiagMessage() << "Invalid feature flag and optional value '" << flag_and_value - << "'. Must be in the format 'flag_name[=true|false]"); + << "'. Must be in the format 'flag_name[:ro][=true|false]"); return false; } @@ -138,6 +138,28 @@ bool ParseFeatureFlagsParameter(StringPiece arg, android::IDiagnostics* diag, return false; } + std::vector<std::string> name_parts = util::Split(flag_name, ':'); + if (name_parts.size() > 2) { + diag->Error(android::DiagMessage() + << "Invalid feature flag and optional value '" << flag_and_value + << "'. Must be in the format 'flag_name[:READ_ONLY|READ_WRITE][=true|false]"); + return false; + } + flag_name = name_parts[0]; + bool read_only = false; + if (name_parts.size() == 2) { + if (name_parts[1] == "ro" || name_parts[1] == "READ_ONLY") { + read_only = true; + } else if (name_parts[1] == "READ_WRITE") { + read_only = false; + } else { + diag->Error(android::DiagMessage() + << "Invalid feature flag and optional value '" << flag_and_value + << "'. Must be in the format 'flag_name[:READ_ONLY|READ_WRITE][=true|false]"); + return false; + } + } + std::optional<bool> flag_value = {}; if (parts.size() == 2) { StringPiece str_flag_value = util::TrimWhitespace(parts[1]); @@ -151,13 +173,13 @@ bool ParseFeatureFlagsParameter(StringPiece arg, android::IDiagnostics* diag, } } - if (auto [it, inserted] = - out_feature_flag_values->try_emplace(std::string(flag_name), flag_value); + auto ffp = FeatureFlagProperties{read_only, flag_value}; + if (auto [it, inserted] = out_feature_flag_values->try_emplace(std::string(flag_name), ffp); !inserted) { // We are allowing the same flag to appear multiple times, last value wins. diag->Note(android::DiagMessage() << "Value for feature flag '" << flag_name << "' was given more than once"); - it->second = flag_value; + it->second = ffp; } } return true; diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h index 9ece5dd4d720..6b8813b34082 100644 --- a/tools/aapt2/cmd/Util.h +++ b/tools/aapt2/cmd/Util.h @@ -37,7 +37,17 @@ namespace aapt { -using FeatureFlagValues = std::map<std::string, std::optional<bool>, std::less<>>; +struct FeatureFlagProperties { + bool read_only; + std::optional<bool> enabled; + + FeatureFlagProperties(bool ro, std::optional<bool> e) : read_only(ro), enabled(e) { + } + + bool operator==(const FeatureFlagProperties&) const = default; +}; + +using FeatureFlagValues = std::map<std::string, FeatureFlagProperties, std::less<>>; // Parses a configuration density (ex. hdpi, xxhdpi, 234dpi, anydpi, etc). // Returns Nothing and logs a human friendly error message if the string was not legal. diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp index 723d87ed0af3..78183409ad8f 100644 --- a/tools/aapt2/cmd/Util_test.cpp +++ b/tools/aapt2/cmd/Util_test.cpp @@ -383,21 +383,25 @@ TEST(UtilTest, ParseFeatureFlagsParameter_InvalidValue) { TEST(UtilTest, ParseFeatureFlagsParameter_DuplicateFlag) { auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics(); FeatureFlagValues feature_flag_values; - ASSERT_TRUE( - ParseFeatureFlagsParameter("foo=true,bar=true,foo=false", diagnostics, &feature_flag_values)); - EXPECT_THAT(feature_flag_values, UnorderedElementsAre(Pair("foo", std::optional<bool>(false)), - Pair("bar", std::optional<bool>(true)))); + ASSERT_TRUE(ParseFeatureFlagsParameter("foo=true,bar:READ_WRITE=true,foo:ro=false", diagnostics, + &feature_flag_values)); + EXPECT_THAT( + feature_flag_values, + UnorderedElementsAre(Pair("foo", FeatureFlagProperties{true, std::optional<bool>(false)}), + Pair("bar", FeatureFlagProperties{false, std::optional<bool>(true)}))); } TEST(UtilTest, ParseFeatureFlagsParameter_Valid) { auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics(); FeatureFlagValues feature_flag_values; - ASSERT_TRUE(ParseFeatureFlagsParameter("foo= true, bar =FALSE,baz=, quux", diagnostics, - &feature_flag_values)); - EXPECT_THAT(feature_flag_values, - UnorderedElementsAre(Pair("foo", std::optional<bool>(true)), - Pair("bar", std::optional<bool>(false)), - Pair("baz", std::nullopt), Pair("quux", std::nullopt))); + ASSERT_TRUE(ParseFeatureFlagsParameter("foo:READ_ONLY= true, bar:ro =FALSE,baz:READ_WRITE=, quux", + diagnostics, &feature_flag_values)); + EXPECT_THAT( + feature_flag_values, + UnorderedElementsAre(Pair("foo", FeatureFlagProperties{true, std::optional<bool>(true)}), + Pair("bar", FeatureFlagProperties{true, std::optional<bool>(false)}), + Pair("baz", FeatureFlagProperties{false, std::nullopt}), + Pair("quux", FeatureFlagProperties{false, std::nullopt}))); } TEST (UtilTest, AdjustSplitConstraintsForMinSdk) { diff --git a/tools/aapt2/link/FeatureFlagsFilter.cpp b/tools/aapt2/link/FeatureFlagsFilter.cpp index fdf3f74d4e18..9d40db521e13 100644 --- a/tools/aapt2/link/FeatureFlagsFilter.cpp +++ b/tools/aapt2/link/FeatureFlagsFilter.cpp @@ -63,12 +63,11 @@ class FlagsVisitor : public xml::Visitor { flag_name = flag_name.substr(1); } - if (auto it = feature_flag_values_.find(std::string(flag_name)); - it != feature_flag_values_.end()) { - if (it->second.has_value()) { + if (auto it = feature_flag_values_.find(flag_name); it != feature_flag_values_.end()) { + if (it->second.enabled.has_value()) { if (options_.remove_disabled_elements) { // Remove if flag==true && attr=="!flag" (negated) OR flag==false && attr=="flag" - return *it->second == negated; + return *it->second.enabled == negated; } } else if (options_.flags_must_have_value) { diagnostics_->Error(android::DiagMessage(node->line_number) diff --git a/tools/aapt2/link/FeatureFlagsFilter_test.cpp b/tools/aapt2/link/FeatureFlagsFilter_test.cpp index 53086cc30f18..2db2899e716c 100644 --- a/tools/aapt2/link/FeatureFlagsFilter_test.cpp +++ b/tools/aapt2/link/FeatureFlagsFilter_test.cpp @@ -48,7 +48,7 @@ TEST(FeatureFlagsFilterTest, NoFeatureFlagAttributes) { <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <permission android:name="FOO" /> </manifest>)EOF", - {{"flag", false}}); + {{"flag", FeatureFlagProperties{false, false}}}); ASSERT_THAT(doc, NotNull()); auto root = doc->root.get(); ASSERT_THAT(root, NotNull()); @@ -60,7 +60,7 @@ TEST(FeatureFlagsFilterTest, RemoveElementWithDisabledFlag) { <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <permission android:name="FOO" android:featureFlag="flag" /> </manifest>)EOF", - {{"flag", false}}); + {{"flag", FeatureFlagProperties{false, false}}}); ASSERT_THAT(doc, NotNull()); auto root = doc->root.get(); ASSERT_THAT(root, NotNull()); @@ -73,7 +73,7 @@ TEST(FeatureFlagsFilterTest, RemoveElementWithNegatedEnabledFlag) { <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <permission android:name="FOO" android:featureFlag="!flag" /> </manifest>)EOF", - {{"flag", true}}); + {{"flag", FeatureFlagProperties{false, true}}}); ASSERT_THAT(doc, NotNull()); auto root = doc->root.get(); ASSERT_THAT(root, NotNull()); @@ -86,7 +86,7 @@ TEST(FeatureFlagsFilterTest, KeepElementWithEnabledFlag) { <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <permission android:name="FOO" android:featureFlag="flag" /> </manifest>)EOF", - {{"flag", true}}); + {{"flag", FeatureFlagProperties{false, true}}}); ASSERT_THAT(doc, NotNull()); auto root = doc->root.get(); ASSERT_THAT(root, NotNull()); @@ -102,7 +102,7 @@ TEST(FeatureFlagsFilterTest, SideBySideEnabledAndDisabled) { <permission android:name="FOO" android:featureFlag="flag" android:protectionLevel="dangerous" /> </manifest>)EOF", - {{"flag", true}}); + {{"flag", FeatureFlagProperties{false, true}}}); ASSERT_THAT(doc, NotNull()); auto root = doc->root.get(); ASSERT_THAT(root, NotNull()); @@ -123,7 +123,7 @@ TEST(FeatureFlagsFilterTest, RemoveDeeplyNestedElement) { </activity> </application> </manifest>)EOF", - {{"flag", true}}); + {{"flag", FeatureFlagProperties{false, true}}}); ASSERT_THAT(doc, NotNull()); auto root = doc->root.get(); ASSERT_THAT(root, NotNull()); @@ -145,7 +145,7 @@ TEST(FeatureFlagsFilterTest, KeepDeeplyNestedElement) { </activity> </application> </manifest>)EOF", - {{"flag", true}}); + {{"flag", FeatureFlagProperties{false, true}}}); ASSERT_THAT(doc, NotNull()); auto root = doc->root.get(); ASSERT_THAT(root, NotNull()); @@ -162,7 +162,7 @@ TEST(FeatureFlagsFilterTest, FailOnEmptyFeatureFlagAttribute) { <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <permission android:name="FOO" android:featureFlag=" " /> </manifest>)EOF", - {{"flag", false}}); + {{"flag", FeatureFlagProperties{false, false}}}); ASSERT_THAT(doc, IsNull()); } @@ -171,7 +171,7 @@ TEST(FeatureFlagsFilterTest, FailOnFlagWithNoGivenValue) { <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <permission android:name="FOO" android:featureFlag="flag" /> </manifest>)EOF", - {{"flag", std::nullopt}}); + {{"flag", FeatureFlagProperties{false, std::nullopt}}}); ASSERT_THAT(doc, IsNull()); } @@ -180,7 +180,7 @@ TEST(FeatureFlagsFilterTest, FailOnUnrecognizedFlag) { <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <permission android:name="FOO" android:featureFlag="unrecognized" /> </manifest>)EOF", - {{"flag", true}}); + {{"flag", FeatureFlagProperties{false, true}}}); ASSERT_THAT(doc, IsNull()); } @@ -190,7 +190,7 @@ TEST(FeatureFlagsFilterTest, FailOnMultipleValidationErrors) { <permission android:name="FOO" android:featureFlag="bar" /> <permission android:name="FOO" android:featureFlag="unrecognized" /> </manifest>)EOF", - {{"flag", std::nullopt}}); + {{"flag", FeatureFlagProperties{false, std::nullopt}}}); ASSERT_THAT(doc, IsNull()); } @@ -199,7 +199,8 @@ TEST(FeatureFlagsFilterTest, OptionRemoveDisabledElementsIsFalse) { <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <permission android:name="FOO" android:featureFlag="flag" /> </manifest>)EOF", - {{"flag", false}}, {.remove_disabled_elements = false}); + {{"flag", FeatureFlagProperties{false, false}}}, + {.remove_disabled_elements = false}); ASSERT_THAT(doc, NotNull()); auto root = doc->root.get(); ASSERT_THAT(root, NotNull()); @@ -212,7 +213,8 @@ TEST(FeatureFlagsFilterTest, OptionFlagsMustHaveValueIsFalse) { <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <permission android:name="FOO" android:featureFlag="flag" /> </manifest>)EOF", - {{"flag", std::nullopt}}, {.flags_must_have_value = false}); + {{"flag", FeatureFlagProperties{false, std::nullopt}}}, + {.flags_must_have_value = false}); ASSERT_THAT(doc, NotNull()); auto root = doc->root.get(); ASSERT_THAT(root, NotNull()); @@ -225,7 +227,8 @@ TEST(FeatureFlagsFilterTest, OptionFailOnUnrecognizedFlagsIsFalse) { <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android"> <permission android:name="FOO" android:featureFlag="unrecognized" /> </manifest>)EOF", - {{"flag", true}}, {.fail_on_unrecognized_flags = false}); + {{"flag", FeatureFlagProperties{false, true}}}, + {.fail_on_unrecognized_flags = false}); ASSERT_THAT(doc, NotNull()); auto root = doc->root.get(); ASSERT_THAT(root, NotNull()); |