summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java3
-rw-r--r--cmds/bootanimation/BootAnimation.cpp2
-rw-r--r--core/api/module-lib-current.txt8
-rw-r--r--core/java/android/animation/OWNERS1
-rw-r--r--core/java/android/app/Activity.java4
-rw-r--r--core/java/android/app/Dialog.java4
-rw-r--r--core/java/android/app/OWNERS2
-rw-r--r--core/java/android/net/NetworkPolicyManager.java38
-rw-r--r--core/java/android/net/flags.aconfig10
-rw-r--r--core/java/android/os/IBinder.java37
-rw-r--r--core/java/android/service/dreams/DreamService.java4
-rw-r--r--core/java/android/view/View.java4
-rw-r--r--core/java/android/view/Window.java4
-rw-r--r--core/tests/coretests/src/android/animation/OWNERS1
-rw-r--r--data/etc/platform.xml2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java144
-rw-r--r--errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java13
-rw-r--r--errorprone/tests/res/android/content/Context.java11
-rw-r--r--errorprone/tests/res/android/content/Intent.java4
-rw-r--r--media/java/android/media/AudioSystem.java4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java2
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java8
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java15
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java5
-rw-r--r--telephony/OWNERS2
-rw-r--r--tools/aapt2/cmd/Compile.cpp22
-rw-r--r--tools/aapt2/cmd/Compile.h9
-rw-r--r--tools/aapt2/cmd/Link.h5
-rw-r--r--tools/aapt2/cmd/Util.cpp30
-rw-r--r--tools/aapt2/cmd/Util.h12
-rw-r--r--tools/aapt2/cmd/Util_test.cpp24
-rw-r--r--tools/aapt2/link/FeatureFlagsFilter.cpp7
-rw-r--r--tools/aapt2/link/FeatureFlagsFilter_test.cpp31
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());