diff options
27 files changed, 1026 insertions, 95 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index bd693850f45e..59a1cbc38216 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -18,6 +18,7 @@ aconfig_srcjars = [ ":android.content.pm.flags-aconfig-java{.generated_srcjars}", ":android.content.res.flags-aconfig-java{.generated_srcjars}", ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}", + ":android.media.playback.flags-aconfig-java{.generated_srcjars}", ":android.net.vcn.flags-aconfig-java{.generated_srcjars}", ":android.nfc.flags-aconfig-java{.generated_srcjars}", ":android.os.flags-aconfig-java{.generated_srcjars}", @@ -30,6 +31,25 @@ aconfig_srcjars = [ // !!! KEEP THIS LIST ALPHABETICAL !!! ] +stubs_defaults { + name: "framework-minus-apex-aconfig-declarations", + aconfig_declarations: [ + "android.content.pm.flags-aconfig", + "android.content.res.flags-aconfig", + "android.hardware.biometrics.flags-aconfig", + "android.media.playback.flags-aconfig", + "android.net.vcn.flags-aconfig", + "android.nfc.flags-aconfig", + "android.os.flags-aconfig", + "android.security.flags-aconfig", + "com.android.hardware.camera2-aconfig", + "com.android.hardware.input.input-aconfig", + "com.android.window.flags.window-aconfig", + "com.android.text.flags-aconfig", + "com.android.net.flags-aconfig", + ], +} + filegroup { name: "framework-minus-apex-aconfig-srcjars", srcs: aconfig_srcjars, diff --git a/api/Android.bp b/api/Android.bp index 7dd13e3f8a09..7fb427eb715a 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -310,6 +310,7 @@ packages_to_document = [ // classpath (or sources) somehow. stubs_defaults { name: "android-non-updatable-stubs-defaults", + defaults: ["framework-minus-apex-aconfig-declarations"], srcs: [":android-non-updatable-stub-sources"], sdk_version: "none", system_modules: "none", diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 365f9130ddd7..594ec18d9996 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -16,6 +16,7 @@ package android.net; +import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManager.procStateToString; import static android.content.pm.PackageManager.GET_SIGNATURES; @@ -170,6 +171,8 @@ public class NetworkPolicyManager { public static final String FIREWALL_CHAIN_NAME_RESTRICTED = "restricted"; /** @hide */ public static final String FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY = "low_power_standby"; + /** @hide */ + public static final String FIREWALL_CHAIN_NAME_BACKGROUND = "background"; private static final boolean ALLOW_PLATFORM_APP_POLICY = true; @@ -180,6 +183,9 @@ public class NetworkPolicyManager { /** @hide */ public static final int TOP_THRESHOLD_STATE = ActivityManager.PROCESS_STATE_BOUND_TOP; + /** @hide */ + public static final int BACKGROUND_THRESHOLD_STATE = ActivityManager.PROCESS_STATE_TOP_SLEEPING; + /** * {@link Intent} extra that indicates which {@link NetworkTemplate} rule it * applies to. @@ -264,6 +270,16 @@ public class NetworkPolicyManager { * @hide */ public static final int ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST = 1 << 6; + + /** + * Flag to indicate that the app is exempt from always-on background network restrictions. + * Note that this is explicitly different to the flag NOT_FOREGROUND which is used to grant + * shared exception to apps from power restrictions like doze, battery saver and app-standby. + * + * @hide + */ + public static final int ALLOWED_REASON_NOT_IN_BACKGROUND = 1 << 7; + /** * Flag to indicate that app is exempt from certain metered network restrictions because user * explicitly exempted it. @@ -822,6 +838,21 @@ public class NetworkPolicyManager { } /** + * This is currently only used as an implementation detail for + * {@link com.android.server.net.NetworkPolicyManagerService}. + * Only put here to be together with other isProcStateAllowed* methods. + * + * @hide + */ + public static boolean isProcStateAllowedNetworkWhileBackground(@Nullable UidState uidState) { + if (uidState == null) { + return false; + } + return uidState.procState < BACKGROUND_THRESHOLD_STATE + || (uidState.capability & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0; + } + + /** * Returns true if {@param procState} is considered foreground and as such will be allowed * to access network when the device is in data saver mode. Otherwise, false. * @hide diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig index 1994058441d5..43163b3b9051 100644 --- a/core/java/android/security/flags.aconfig +++ b/core/java/android/security/flags.aconfig @@ -58,3 +58,10 @@ flag { bug: "290312729" is_fixed_read_only: true } + +flag { + name: "report_primary_auth_attempts" + namespace: "biometrics" + description: "Report primary auth attempts from LockSettingsService" + bug: "285053096" +} diff --git a/core/java/com/android/internal/widget/ILockSettingsStateListener.aidl b/core/java/com/android/internal/widget/ILockSettingsStateListener.aidl new file mode 100644 index 000000000000..25e30034fe8f --- /dev/null +++ b/core/java/com/android/internal/widget/ILockSettingsStateListener.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +/** + * Callback interface between LockSettingService and other system services to be notified about the + * state of primary authentication (i.e. PIN/pattern/password). + * @hide + */ +oneway interface ILockSettingsStateListener { + /** + * Defines behavior in response to a successful authentication + * @param userId The user Id for the requested authentication + */ + void onAuthenticationSucceeded(int userId); + + /** + * Defines behavior in response to a failed authentication + * @param userId The user Id for the requested authentication + */ + void onAuthenticationFailed(int userId); +}
\ No newline at end of file diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java index 8114e1fd3bb0..627e8779f9d0 100644 --- a/core/java/com/android/internal/widget/LockSettingsInternal.java +++ b/core/java/com/android/internal/widget/LockSettingsInternal.java @@ -166,4 +166,16 @@ public abstract class LockSettingsInternal { * Refreshes pending strong auth timeout with the latest admin requirement set by device policy. */ public abstract void refreshStrongAuthTimeout(int userId); + + /** + * Register a LockSettingsStateListener + * @param listener The listener to be registered + */ + public abstract void registerLockSettingsStateListener(ILockSettingsStateListener listener); + + /** + * Unregister a LockSettingsStateListener + * @param listener The listener to be unregistered + */ + public abstract void unregisterLockSettingsStateListener(ILockSettingsStateListener listener); } diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 7af69f2dff08..6a640a5ab23b 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -28,6 +28,7 @@ #include <meminfo/sysmeminfo.h> #include <processgroup/processgroup.h> #include <processgroup/sched_policy.h> +#include <android-base/logging.h> #include <android-base/unique_fd.h> #include <algorithm> @@ -232,6 +233,31 @@ void android_os_Process_setThreadGroupAndCpuset(JNIEnv* env, jobject clazz, int } } +// Look up the user ID of a process in /proc/${pid}/status. The Uid: line is present in +// /proc/${pid}/status since at least kernel v2.5. +static int uid_from_pid(int pid) +{ + int uid = -1; + std::array<char, 64> path; + int res = snprintf(path.data(), path.size(), "/proc/%d/status", pid); + if (res < 0 || res >= static_cast<int>(path.size())) { + DCHECK(false); + return uid; + } + FILE* f = fopen(path.data(), "r"); + if (!f) { + return uid; + } + char line[256]; + while (fgets(line, sizeof(line), f)) { + if (sscanf(line, "Uid: %d", &uid) == 1) { + break; + } + } + fclose(f); + return uid; +} + void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp) { ALOGV("%s pid=%d grp=%" PRId32, __func__, pid, grp); @@ -275,7 +301,12 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin } } - if (!SetProcessProfilesCached(0, pid, {get_cpuset_policy_profile_name((SchedPolicy)grp)})) + const int uid = uid_from_pid(pid); + if (uid < 0) { + signalExceptionForGroupError(env, ESRCH, pid); + return; + } + if (!SetProcessProfilesCached(uid, pid, {get_cpuset_policy_profile_name((SchedPolicy)grp)})) signalExceptionForGroupError(env, errno ? errno : EPERM, pid); } diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp index 3dd9ba9db1d9..f403b4f5353a 100644 --- a/data/fonts/Android.bp +++ b/data/fonts/Android.bp @@ -57,3 +57,9 @@ prebuilt_etc { name: "font_fallback.xml", src: "font_fallback.xml", } + +///////////////////////////////// +// Move `fontchain_lint` to `core/tasks/fontchain_lint.mk`. +// Because `system.img` is a dependency of `fontchain_lint`, it cannot be +// converted to Android.bp for now. +// After system.img can be generated by Soong, then it can be converted to Android.bp. diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk deleted file mode 100644 index a322b829932b..000000000000 --- a/data/fonts/Android.mk +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -# Run sanity tests on fonts on checkbuild -checkbuild: fontchain_lint - -FONTCHAIN_LINTER := $(HOST_OUT_EXECUTABLES)/fontchain_linter -ifeq ($(MINIMAL_FONT_FOOTPRINT),true) -CHECK_EMOJI := false -else -CHECK_EMOJI := true -endif - -fontchain_lint_timestamp := $(call intermediates-dir-for,PACKAGING,fontchain_lint)/stamp - -.PHONY: fontchain_lint -fontchain_lint: $(fontchain_lint_timestamp) - -fontchain_lint_deps := \ - external/unicode/DerivedAge.txt \ - external/unicode/emoji-data.txt \ - external/unicode/emoji-sequences.txt \ - external/unicode/emoji-variation-sequences.txt \ - external/unicode/emoji-zwj-sequences.txt \ - external/unicode/additions/emoji-data.txt \ - external/unicode/additions/emoji-sequences.txt \ - external/unicode/additions/emoji-zwj-sequences.txt \ - -$(fontchain_lint_timestamp): $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img $(fontchain_lint_deps) - @echo Running fontchain lint - $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode - touch $@ diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 1dd22cf43c5c..a733d1772757 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -218,7 +218,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer. */ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) { - if (mDamageGenerationId == info.damageGenerationId) { + if (mDamageGenerationId == info.damageGenerationId && mDamageGenerationId != 0) { // We hit the same node a second time in the same tree. We don't know the minimal // damage rect anymore, so just push the biggest we can onto our parent's transform // We push directly onto parent in case we are clipped to bounds but have moved position. diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 572635a9bd45..4d03bf189a84 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -263,7 +263,7 @@ private: DisplayList mDisplayList; DisplayList mStagingDisplayList; - int64_t mDamageGenerationId; + int64_t mDamageGenerationId = 0; friend class AnimatorManager; AnimatorManager mAnimatorManager; diff --git a/services/core/Android.bp b/services/core/Android.bp index 21cfd242cf45..2dda76e06c56 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -190,6 +190,7 @@ java_library_static { "com.android.sysprop.watchdog", "ImmutabilityAnnotation", "securebox", + "net_flags_lib", ], javac_shard_size: 50, javacflags: [ diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index ad090829a2f6..c81ce26b6403 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -16,6 +16,7 @@ package com.android.server.locksettings; +import static android.security.Flags.reportPrimaryAuthAttempts; import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE; import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS; @@ -90,6 +91,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IProgressListener; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -135,6 +137,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.widget.ICheckCredentialProgressCallback; import com.android.internal.widget.ILockSettings; +import com.android.internal.widget.ILockSettingsStateListener; import com.android.internal.widget.IWeakEscrowTokenActivatedListener; import com.android.internal.widget.IWeakEscrowTokenRemovedListener; import com.android.internal.widget.LockPatternUtils; @@ -327,6 +330,9 @@ public class LockSettingsService extends ILockSettings.Stub { private HashMap<UserHandle, UserManager> mUserManagerCache = new HashMap<>(); + private final RemoteCallbackList<ILockSettingsStateListener> mLockSettingsStateListeners = + new RemoteCallbackList<>(); + // This class manages life cycle events for encrypted users on File Based Encryption (FBE) // devices. The most basic of these is to show/hide notifications about missing features until // the user unlocks the account and credential-encrypted storage is available. @@ -2342,9 +2348,37 @@ public class LockSettingsService extends ILockSettings.Stub { requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId); } } + if (reportPrimaryAuthAttempts()) { + final boolean success = + response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK; + notifyLockSettingsStateListeners(success, userId); + } return response; } + private void notifyLockSettingsStateListeners(boolean success, int userId) { + int i = mLockSettingsStateListeners.beginBroadcast(); + try { + while (i > 0) { + i--; + try { + if (success) { + mLockSettingsStateListeners.getBroadcastItem(i) + .onAuthenticationSucceeded(userId); + } else { + mLockSettingsStateListeners.getBroadcastItem(i) + .onAuthenticationFailed(userId); + } + } catch (RemoteException e) { + Slog.e(TAG, "Exception while notifying LockSettingsStateListener:" + + " success = " + success + ", userId = " + userId, e); + } + } + } finally { + mLockSettingsStateListeners.finishBroadcast(); + } + } + @Override public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential, int userId, @LockPatternUtils.VerifyFlag int flags) { @@ -3662,6 +3696,18 @@ public class LockSettingsService extends ILockSettings.Stub { public void refreshStrongAuthTimeout(int userId) { mStrongAuth.refreshStrongAuthTimeout(userId); } + + @Override + public void registerLockSettingsStateListener( + @NonNull ILockSettingsStateListener listener) { + mLockSettingsStateListeners.register(listener); + } + + @Override + public void unregisterLockSettingsStateListener( + @NonNull ILockSettingsStateListener listener) { + mLockSettingsStateListeners.unregister(listener); + } } private class RebootEscrowCallbacks implements RebootEscrowManager.Callbacks { diff --git a/services/core/java/com/android/server/net/Android.bp b/services/core/java/com/android/server/net/Android.bp new file mode 100644 index 000000000000..71d8e6ba367e --- /dev/null +++ b/services/core/java/com/android/server/net/Android.bp @@ -0,0 +1,10 @@ +aconfig_declarations { + name: "net_flags", + package: "com.android.server.net", + srcs: ["*.aconfig"], +} + +java_aconfig_library { + name: "net_flags_lib", + aconfig_declarations: "net_flags", +} diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java index 681d1a0ee10a..d25f52973085 100644 --- a/services/core/java/com/android/server/net/NetworkManagementService.java +++ b/services/core/java/com/android/server/net/NetworkManagementService.java @@ -17,6 +17,7 @@ package com.android.server.net; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -27,6 +28,7 @@ import static android.net.INetd.FIREWALL_CHAIN_NONE; import static android.net.INetd.FIREWALL_DENYLIST; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; @@ -187,6 +189,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub { */ @GuardedBy("mRulesLock") private final SparseIntArray mUidFirewallLowPowerStandbyRules = new SparseIntArray(); + + /** + * Contains the per-UID firewall rules that are used when Background chain is enabled. + */ + @GuardedBy("mRulesLock") + private final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray(); + /** Set of states for the child firewall chains. True if the chain is active. */ @GuardedBy("mRulesLock") final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); @@ -449,13 +458,15 @@ public class NetworkManagementService extends INetworkManagementService.Stub { syncFirewallChainLocked(FIREWALL_CHAIN_POWERSAVE, "powersave "); syncFirewallChainLocked(FIREWALL_CHAIN_RESTRICTED, "restricted "); syncFirewallChainLocked(FIREWALL_CHAIN_LOW_POWER_STANDBY, "low power standby "); + syncFirewallChainLocked(FIREWALL_CHAIN_BACKGROUND, FIREWALL_CHAIN_NAME_BACKGROUND); final int[] chains = { FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_POWERSAVE, FIREWALL_CHAIN_RESTRICTED, - FIREWALL_CHAIN_LOW_POWER_STANDBY + FIREWALL_CHAIN_LOW_POWER_STANDBY, + FIREWALL_CHAIN_BACKGROUND, }; for (int chain : chains) { @@ -1206,6 +1217,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return FIREWALL_CHAIN_NAME_RESTRICTED; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; + case FIREWALL_CHAIN_BACKGROUND: + return FIREWALL_CHAIN_NAME_BACKGROUND; default: throw new IllegalArgumentException("Bad child chain: " + chain); } @@ -1223,6 +1236,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return FIREWALL_ALLOWLIST; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return FIREWALL_ALLOWLIST; + case FIREWALL_CHAIN_BACKGROUND: + return FIREWALL_ALLOWLIST; default: return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST; } @@ -1343,6 +1358,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return mUidFirewallRestrictedRules; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return mUidFirewallLowPowerStandbyRules; + case FIREWALL_CHAIN_BACKGROUND: + return mUidFirewallBackgroundRules; case FIREWALL_CHAIN_NONE: return mUidFirewallRules; default: @@ -1395,6 +1412,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub { pw.println(getFirewallChainState(FIREWALL_CHAIN_LOW_POWER_STANDBY)); dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY, mUidFirewallLowPowerStandbyRules); + + pw.print("UID firewall background chain enabled: "); + pw.println(getFirewallChainState(FIREWALL_CHAIN_BACKGROUND)); + dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_BACKGROUND, mUidFirewallBackgroundRules); } pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); @@ -1494,6 +1515,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub { if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of low power standby"); return true; } + if (getFirewallChainState(FIREWALL_CHAIN_BACKGROUND) + && mUidFirewallBackgroundRules.get(uid) != FIREWALL_RULE_ALLOW) { + if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because it is in background"); + return true; + } if (mUidRejectOnMetered.get(uid)) { if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of no metered data" + " in the background"); diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index d7188c7f10c6..8e2d7780204a 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -16,6 +16,7 @@ package com.android.server.net; import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -24,6 +25,7 @@ import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; @@ -389,6 +391,8 @@ public class NetworkPolicyLogger { return FIREWALL_CHAIN_NAME_RESTRICTED; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; + case FIREWALL_CHAIN_BACKGROUND: + return FIREWALL_CHAIN_NAME_BACKGROUND; default: return String.valueOf(chain); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 52734a4ad3b0..b47458b88f40 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -47,6 +47,7 @@ import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_ADMIN_DISAB import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND; import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE; @@ -54,6 +55,7 @@ import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; import static android.net.ConnectivityManager.BLOCKED_REASON_RESTRICTED_MODE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -77,6 +79,7 @@ import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_USER_EXEMP import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND; import static android.net.NetworkPolicyManager.ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_NOT_IN_BACKGROUND; import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS; @@ -96,6 +99,7 @@ import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED; import static android.net.NetworkPolicyManager.allowedReasonsToString; import static android.net.NetworkPolicyManager.blockedReasonsToString; +import static android.net.NetworkPolicyManager.isProcStateAllowedNetworkWhileBackground; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileInLowPowerStandby; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground; @@ -201,12 +205,12 @@ import android.os.Message; import android.os.MessageQueue.IdleHandler; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; +import android.os.PowerExemptionManager; import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; -import android.os.PowerWhitelistManager; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -242,6 +246,7 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.SparseSetArray; +import android.util.TimeUtils; import android.util.Xml; import com.android.internal.R; @@ -457,6 +462,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ private static final int MSG_UIDS_BLOCKED_REASONS_CHANGED = 23; + /** + * Message to update background restriction rules for uids that should lose network access + * due to being in the background. + */ + private static final int MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS = 24; + private static final int UID_MSG_STATE_CHANGED = 100; private static final int UID_MSG_GONE = 101; @@ -475,7 +486,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private ConnectivityManager mConnManager; private PowerManagerInternal mPowerManagerInternal; - private PowerWhitelistManager mPowerWhitelistManager; + private PowerExemptionManager mPowerExemptionManager; @NonNull private final Dependencies mDeps; @@ -490,6 +501,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Denotes the status of restrict background read from disk. private boolean mLoadedRestrictBackground; + /** + * Whether or not network for apps in proc-states greater than + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} is always blocked. + */ + private boolean mBackgroundNetworkRestricted; + // See main javadoc for instructions on how to use these locks. final Object mUidRulesFirstLock = new Object(); final Object mNetworkPoliciesSecondLock = new Object(); @@ -514,6 +531,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private volatile boolean mNetworkManagerReady; + /** + * Delay after which a uid going into a process state greater than or equal to + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} will lose network access. + * The delay is meant to prevent churn due to quick process-state changes. + * Note that there is no delay while granting network access. + */ + @VisibleForTesting + long mBackgroundRestrictionDelayMs = TimeUnit.SECONDS.toMillis(5); + /** Defined network policies. */ @GuardedBy("mNetworkPoliciesSecondLock") final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>(); @@ -545,6 +571,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray(); @GuardedBy("mUidRulesFirstLock") + final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray(); + @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallRestrictedModeRules = new SparseIntArray(); @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallLowPowerStandbyModeRules = new SparseIntArray(); @@ -624,6 +652,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private final SparseArray<UidBlockedState> mTmpUidBlockedState = new SparseArray<>(); + /** + * Stores a map of uids to the time their transition to background is considered complete. They + * will lose network access after this time. This is used to prevent churn in rules due to quick + * process-state transitions. + */ + @GuardedBy("mUidRulesFirstLock") + private final SparseLongArray mBackgroundTransitioningUids = new SparseLongArray(); + /** Map from network ID to last observed meteredness state */ @GuardedBy("mNetworkPoliciesSecondLock") private final SparseBooleanArray mNetworkMetered = new SparseBooleanArray(); @@ -823,7 +859,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext = Objects.requireNonNull(context, "missing context"); mActivityManager = Objects.requireNonNull(activityManager, "missing activityManager"); mNetworkManager = Objects.requireNonNull(networkManagement, "missing networkManagement"); - mPowerWhitelistManager = mContext.getSystemService(PowerWhitelistManager.class); + mPowerExemptionManager = mContext.getSystemService(PowerExemptionManager.class); mClock = Objects.requireNonNull(clock, "missing Clock"); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class); @@ -859,15 +895,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private void updatePowerSaveAllowlistUL() { - int[] whitelist = mPowerWhitelistManager.getWhitelistedAppIds(/* includingIdle */ false); + int[] allowlist = mPowerExemptionManager.getAllowListedAppIds(/* includingIdle */ false); mPowerSaveWhitelistExceptIdleAppIds.clear(); - for (int uid : whitelist) { + for (int uid : allowlist) { mPowerSaveWhitelistExceptIdleAppIds.put(uid, true); } - whitelist = mPowerWhitelistManager.getWhitelistedAppIds(/* includingIdle */ true); + allowlist = mPowerExemptionManager.getAllowListedAppIds(/* includingIdle */ true); mPowerSaveWhitelistAppIds.clear(); - for (int uid : whitelist) { + for (int uid : allowlist) { mPowerSaveWhitelistAppIds.put(uid, true); } } @@ -1017,6 +1053,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { writePolicyAL(); } + // The flag is boot-stable. + mBackgroundNetworkRestricted = Flags.networkBlockedForTopSleepingAndAbove(); + if (mBackgroundNetworkRestricted) { + // Firewall rules and UidBlockedState will get updated in + // updateRulesForGlobalChangeAL below. + enableFirewallChainUL(FIREWALL_CHAIN_BACKGROUND, true); + } + setRestrictBackgroundUL(mLoadedRestrictBackground, "init_service"); updateRulesForGlobalChangeAL(false); updateNotificationsNL(); @@ -1027,17 +1071,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int changes = ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_CAPABILITY; + + final int cutpoint = mBackgroundNetworkRestricted ? PROCESS_STATE_UNKNOWN + : NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE; + // TODO (b/319728914): Filter out the unnecessary changes when using no cutpoint. + mActivityManagerInternal.registerNetworkPolicyUidObserver(mUidObserver, changes, - NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android"); + cutpoint, "android"); mNetworkManager.registerObserver(mAlertObserver); } catch (RemoteException e) { // ignored; both services live in system_server } // listen for changes to power save allowlist - final IntentFilter whitelistFilter = new IntentFilter( + final IntentFilter allowlistFilter = new IntentFilter( PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); - mContext.registerReceiver(mPowerSaveWhitelistReceiver, whitelistFilter, null, mHandler); + mContext.registerReceiver(mPowerSaveAllowlistReceiver, allowlistFilter, null, mHandler); // watch for network interfaces to be claimed final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION); @@ -1188,12 +1237,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - final private BroadcastReceiver mPowerSaveWhitelistReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mPowerSaveAllowlistReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // on background handler thread, and POWER_SAVE_WHITELIST_CHANGED is protected synchronized (mUidRulesFirstLock) { updatePowerSaveAllowlistUL(); + if (mBackgroundNetworkRestricted) { + updateRulesForBackgroundChainUL(); + } updateRulesForRestrictPowerUL(); updateRulesForAppIdleUL(); } @@ -3901,6 +3953,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } fout.println(); + fout.println("Flags:"); + fout.println("Network blocked for TOP_SLEEPING and above: " + + mBackgroundNetworkRestricted); + + fout.println(); fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode); fout.println("mRestrictBackgroundBeforeBsm: " + mRestrictBackgroundBeforeBsm); fout.println("mLoadedRestrictBackground: " + mLoadedRestrictBackground); @@ -4042,6 +4099,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.decreaseIndent(); } + size = mBackgroundTransitioningUids.size(); + if (size > 0) { + final long nowUptime = SystemClock.uptimeMillis(); + fout.println("Uids transitioning to background:"); + fout.increaseIndent(); + for (int i = 0; i < size; i++) { + fout.print("UID="); + fout.print(mBackgroundTransitioningUids.keyAt(i)); + fout.print(", "); + TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i), nowUptime, + fout); + fout.println(); + } + fout.decreaseIndent(); + } + final SparseBooleanArray knownUids = new SparseBooleanArray(); collectKeys(mUidState, knownUids); synchronized (mUidBlockedState) { @@ -4163,6 +4236,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return isProcStateAllowedWhileInLowPowerStandby(uidState); } + @GuardedBy("mUidRulesFirstLock") + private boolean isUidExemptFromBackgroundRestrictions(int uid) { + return mBackgroundTransitioningUids.indexOfKey(uid) >= 0 + || isProcStateAllowedNetworkWhileBackground(mUidState.get(uid)); + } + /** * Process state of UID changed; if needed, will trigger * {@link #updateRulesForDataUsageRestrictionsUL(int)} and @@ -4188,6 +4267,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // state changed, push updated rules mUidState.put(uid, newUidState); updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, newUidState); + + boolean updatePowerRestrictionRules = false; boolean allowedWhileIdleOrPowerSaveModeChanged = isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState) != isProcStateAllowedWhileIdleOrPowerSaveMode(newUidState); @@ -4199,19 +4280,44 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mRestrictPower) { updateRuleForRestrictPowerUL(uid); } - updateRulesForPowerRestrictionsUL(uid, procState); + updatePowerRestrictionRules = true; + } + if (mBackgroundNetworkRestricted) { + final boolean wasAllowed = isProcStateAllowedNetworkWhileBackground( + oldUidState); + final boolean isAllowed = isProcStateAllowedNetworkWhileBackground(newUidState); + if (!wasAllowed && isAllowed) { + mBackgroundTransitioningUids.delete(uid); + updateRuleForBackgroundUL(uid); + updatePowerRestrictionRules = true; + } else if (wasAllowed && !isAllowed) { + final long completionTimeMs = SystemClock.uptimeMillis() + + mBackgroundRestrictionDelayMs; + if (mBackgroundTransitioningUids.indexOfKey(uid) < 0) { + // This is just a defensive check in case the upstream code ever makes + // multiple calls for the same process state change. + mBackgroundTransitioningUids.put(uid, completionTimeMs); + } + if (!mHandler.hasMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS)) { + // Many uids may be in this "transitioning" state at the same time, so + // using one message at a time to avoid congestion in the MessageQueue. + mHandler.sendEmptyMessageAtTime( + MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, completionTimeMs); + } + } } if (mLowPowerStandbyActive) { boolean allowedInLpsChanged = isProcStateAllowedWhileInLowPowerStandby(oldUidState) != isProcStateAllowedWhileInLowPowerStandby(newUidState); if (allowedInLpsChanged) { - if (!allowedWhileIdleOrPowerSaveModeChanged) { - updateRulesForPowerRestrictionsUL(uid, procState); - } updateRuleForLowPowerStandbyUL(uid); + updatePowerRestrictionRules = true; } } + if (updatePowerRestrictionRules) { + updateRulesForPowerRestrictionsUL(uid, procState); + } return true; } } finally { @@ -4234,6 +4340,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mRestrictPower) { updateRuleForRestrictPowerUL(uid); } + if (mBackgroundNetworkRestricted) { + // Uid is no longer running, there is no point in any grace period of network + // access during transitions to lower importance proc-states. + mBackgroundTransitioningUids.delete(uid); + updateRuleForBackgroundUL(uid); + } updateRulesForPowerRestrictionsUL(uid); if (mLowPowerStandbyActive) { updateRuleForLowPowerStandbyUL(uid); @@ -4441,11 +4553,41 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + /** + * Updates the rules for apps allowlisted to use network while in the background. + */ + @GuardedBy("mUidRulesFirstLock") + private void updateRulesForBackgroundChainUL() { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForBackgroundChainUL"); + try { + final SparseIntArray uidRules = mUidFirewallBackgroundRules; + uidRules.clear(); + + final List<UserInfo> users = mUserManager.getUsers(); + for (int ui = users.size() - 1; ui >= 0; ui--) { + final UserInfo user = users.get(ui); + updateRulesForAllowlistedAppIds(uidRules, mPowerSaveTempWhitelistAppIds, user.id); + updateRulesForAllowlistedAppIds(uidRules, mPowerSaveWhitelistAppIds, user.id); + updateRulesForAllowlistedAppIds(uidRules, mPowerSaveWhitelistExceptIdleAppIds, + user.id); + } + for (int i = mUidState.size() - 1; i >= 0; i--) { + if (mBackgroundTransitioningUids.indexOfKey(mUidState.keyAt(i)) >= 0 + || isProcStateAllowedNetworkWhileBackground(mUidState.valueAt(i))) { + uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW); + } + } + setUidFirewallRulesUL(FIREWALL_CHAIN_BACKGROUND, uidRules); + } finally { + Trace.traceEnd(TRACE_TAG_NETWORK); + } + } + private void updateRulesForAllowlistedAppIds(final SparseIntArray uidRules, - final SparseBooleanArray whitelistedAppIds, int userId) { - for (int i = whitelistedAppIds.size() - 1; i >= 0; --i) { - if (whitelistedAppIds.valueAt(i)) { - final int appId = whitelistedAppIds.keyAt(i); + final SparseBooleanArray allowlistedAppIds, int userId) { + for (int i = allowlistedAppIds.size() - 1; i >= 0; --i) { + if (allowlistedAppIds.valueAt(i)) { + final int appId = allowlistedAppIds.keyAt(i); final int uid = UserHandle.getUid(userId, appId); uidRules.put(uid, FIREWALL_RULE_ALLOW); } @@ -4504,12 +4646,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private boolean isAllowlistedFromPowerSaveUL(int uid, boolean deviceIdleMode) { final int appId = UserHandle.getAppId(uid); - boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId) + boolean allowlisted = mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId); if (!deviceIdleMode) { - isWhitelisted = isWhitelisted || isAllowlistedFromPowerSaveExceptIdleUL(uid); + allowlisted = allowlisted || isAllowlistedFromPowerSaveExceptIdleUL(uid); } - return isWhitelisted; + return allowlisted; } /** @@ -4598,6 +4740,38 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** + * Update firewall rule for a single uid whenever there are any interesting changes in the uid. + * Currently, it is called when: + * - The uid is added to or removed from power allowlists + * - The uid undergoes a process-state change + * - A package belonging to this uid is added + * - The uid is evicted from memory + */ + @GuardedBy("mUidRulesFirstLock") + void updateRuleForBackgroundUL(int uid) { + if (!isUidValidForAllowlistRulesUL(uid)) { + return; + } + + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRuleForBackgroundUL: " + uid); + try { + // The uid should be absent from mUidState and mBackgroundTransitioningUids if it is + // not running when this method is called. Then, the firewall state will depend on the + // allowlist alone. This is the desired behavior. + if (isAllowlistedFromPowerSaveUL(uid, false) + || isUidExemptFromBackgroundRestrictions(uid)) { + setUidFirewallRuleUL(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_ALLOW); + if (LOGD) Log.d(TAG, "updateRuleForBackgroundUL ALLOW " + uid); + } else { + setUidFirewallRuleUL(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_DEFAULT); + if (LOGD) Log.d(TAG, "updateRuleForBackgroundUL " + uid + " to DEFAULT"); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); + } + } + + /** * Toggle the firewall standby chain and inform listeners if the uid rules have effectively * changed. */ @@ -4644,6 +4818,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { "updateRulesForGlobalChangeAL: " + (restrictedNetworksChanged ? "R" : "-")); } try { + if (mBackgroundNetworkRestricted) { + updateRulesForBackgroundChainUL(); + } updateRulesForAppIdleUL(); updateRulesForRestrictPowerUL(); updateRulesForRestrictBackgroundUL(); @@ -4803,6 +4980,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN); updateRuleForDeviceIdleUL(uid); updateRuleForRestrictPowerUL(uid); + if (mBackgroundNetworkRestricted) { + updateRuleForBackgroundUL(uid); + } // Update internal rules. updateRulesForPowerRestrictionsUL(uid); } @@ -4940,6 +5120,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidFirewallStandbyRules.delete(uid); mUidFirewallDozableRules.delete(uid); mUidFirewallPowerSaveRules.delete(uid); + mUidFirewallBackgroundRules.delete(uid); + mBackgroundTransitioningUids.delete(uid); mPowerSaveWhitelistExceptIdleAppIds.delete(uid); mPowerSaveWhitelistAppIds.delete(uid); mPowerSaveTempWhitelistAppIds.delete(uid); @@ -4973,6 +5155,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForDeviceIdleUL(uid); updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN); updateRuleForRestrictPowerUL(uid); + if (mBackgroundNetworkRestricted) { + updateRuleForBackgroundUL(uid); + } // If the uid has the necessary permissions, then it should be added to the restricted mode // firewall allowlist. @@ -5157,7 +5342,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Similar to above but ignores idle state if app standby is currently disabled by parole. * * @param uid the uid of the app to update rules for - * @param oldUidRules the current rules for the uid, in order to determine if there's a change * @param isUidIdle whether uid is idle or not */ @GuardedBy("mUidRulesFirstLock") @@ -5203,6 +5387,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { newBlockedReasons |= (mLowPowerStandbyActive ? BLOCKED_REASON_LOW_POWER_STANDBY : 0); newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0); newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE); + newBlockedReasons |= mBackgroundNetworkRestricted ? BLOCKED_REASON_APP_BACKGROUND : 0; newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0); newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0); @@ -5215,6 +5400,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS); newAllowedReasons |= (isAllowlistedFromLowPowerStandbyUL(uid)) ? ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST : 0; + newAllowedReasons |= (mBackgroundNetworkRestricted + && isUidExemptFromBackgroundRestrictions(uid)) + ? ALLOWED_REASON_NOT_IN_BACKGROUND : 0; uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons & BLOCKED_METERED_REASON_MASK) | newBlockedReasons; @@ -5236,7 +5424,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons; newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; - uidRules = oldEffectiveBlockedReasons == newEffectiveBlockedReasons + uidRules = (oldEffectiveBlockedReasons == newEffectiveBlockedReasons) ? RULE_NONE : uidBlockedState.deriveUidRules(); } @@ -5429,6 +5617,28 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext.sendBroadcastAsUser(intent, UserHandle.ALL); return true; } + case MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS: { + final long now = SystemClock.uptimeMillis(); + long nextCheckTime = Long.MAX_VALUE; + synchronized (mUidRulesFirstLock) { + for (int i = mBackgroundTransitioningUids.size() - 1; i >= 0; i--) { + final long completionTimeMs = mBackgroundTransitioningUids.valueAt(i); + if (completionTimeMs > now) { + nextCheckTime = Math.min(nextCheckTime, completionTimeMs); + continue; + } + final int uid = mBackgroundTransitioningUids.keyAt(i); + mBackgroundTransitioningUids.removeAt(i); + updateRuleForBackgroundUL(uid); + updateRulesForPowerRestrictionsUL(uid, false); + } + } + if (nextCheckTime < Long.MAX_VALUE) { + mHandler.sendEmptyMessageAtTime(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, + nextCheckTime); + } + return true; + } case MSG_POLICIES_CHANGED: { final int uid = msg.arg1; final int policy = msg.arg2; @@ -5840,6 +6050,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidFirewallRestrictedModeRules.put(uid, rule); } else if (chain == FIREWALL_CHAIN_LOW_POWER_STANDBY) { mUidFirewallLowPowerStandbyModeRules.put(uid, rule); + } else if (chain == FIREWALL_CHAIN_BACKGROUND) { + mUidFirewallBackgroundRules.put(uid, rule); } try { @@ -5896,6 +6108,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { FIREWALL_RULE_DEFAULT); mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_DEFAULT); + mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, uid, + FIREWALL_RULE_DEFAULT); mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false); mLogger.meteredAllowlistChanged(uid, false); mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false); @@ -6420,10 +6634,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER; effectiveBlockedReasons &= ~BLOCKED_REASON_DOZE; effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY; + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_BACKGROUND; } if ((allowedReasons & ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST) != 0) { effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER; effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY; + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_BACKGROUND; } if ((allowedReasons & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS) != 0) { effectiveBlockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE; @@ -6434,19 +6650,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if ((allowedReasons & ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST) != 0) { effectiveBlockedReasons &= ~BLOCKED_REASON_LOW_POWER_STANDBY; } + if ((allowedReasons & ALLOWED_REASON_NOT_IN_BACKGROUND) != 0) { + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_BACKGROUND; + } return effectiveBlockedReasons; } static int getAllowedReasonsForProcState(int procState) { - if (procState > NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE) { - return ALLOWED_REASON_NONE; - } else if (procState <= NetworkPolicyManager.TOP_THRESHOLD_STATE) { + if (procState <= NetworkPolicyManager.TOP_THRESHOLD_STATE) { return ALLOWED_REASON_TOP | ALLOWED_REASON_FOREGROUND - | ALLOWED_METERED_REASON_FOREGROUND; - } else { - return ALLOWED_REASON_FOREGROUND | ALLOWED_METERED_REASON_FOREGROUND; + | ALLOWED_METERED_REASON_FOREGROUND | ALLOWED_REASON_NOT_IN_BACKGROUND; + } else if (procState <= NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE) { + return ALLOWED_REASON_FOREGROUND | ALLOWED_METERED_REASON_FOREGROUND + | ALLOWED_REASON_NOT_IN_BACKGROUND; + } else if (procState < NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE) { + return ALLOWED_REASON_NOT_IN_BACKGROUND; } + return ALLOWED_REASON_NONE; } @Override @@ -6471,6 +6692,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { BLOCKED_REASON_APP_STANDBY, BLOCKED_REASON_RESTRICTED_MODE, BLOCKED_REASON_LOW_POWER_STANDBY, + BLOCKED_REASON_APP_BACKGROUND, BLOCKED_METERED_REASON_DATA_SAVER, BLOCKED_METERED_REASON_USER_RESTRICTED, BLOCKED_METERED_REASON_ADMIN_DISABLED, @@ -6484,6 +6706,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST, ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS, ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST, + ALLOWED_REASON_NOT_IN_BACKGROUND, ALLOWED_METERED_REASON_USER_EXEMPTED, ALLOWED_METERED_REASON_SYSTEM, ALLOWED_METERED_REASON_FOREGROUND, @@ -6503,6 +6726,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return "RESTRICTED_MODE"; case BLOCKED_REASON_LOW_POWER_STANDBY: return "LOW_POWER_STANDBY"; + case BLOCKED_REASON_APP_BACKGROUND: + return "APP_BACKGROUND"; case BLOCKED_METERED_REASON_DATA_SAVER: return "DATA_SAVER"; case BLOCKED_METERED_REASON_USER_RESTRICTED: @@ -6533,6 +6758,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return "RESTRICTED_MODE_PERMISSIONS"; case ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST: return "LOW_POWER_STANDBY_ALLOWLIST"; + case ALLOWED_REASON_NOT_IN_BACKGROUND: + return "NOT_IN_BACKGROUND"; case ALLOWED_METERED_REASON_USER_EXEMPTED: return "METERED_USER_EXEMPTED"; case ALLOWED_METERED_REASON_SYSTEM: @@ -6600,7 +6827,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { int powerBlockedReasons = BLOCKED_REASON_APP_STANDBY | BLOCKED_REASON_DOZE | BLOCKED_REASON_BATTERY_SAVER - | BLOCKED_REASON_LOW_POWER_STANDBY; + | BLOCKED_REASON_LOW_POWER_STANDBY + | BLOCKED_REASON_APP_BACKGROUND; if ((effectiveBlockedReasons & powerBlockedReasons) != 0) { uidRule |= RULE_REJECT_ALL; } else if ((blockedReasons & powerBlockedReasons) != 0) { diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig new file mode 100644 index 000000000000..419665a0a5ab --- /dev/null +++ b/services/core/java/com/android/server/net/flags.aconfig @@ -0,0 +1,8 @@ +package: "com.android.server.net" + +flag { + name: "network_blocked_for_top_sleeping_and_above" + namespace: "backstage_power" + description: "Block network access for apps in a low importance background state" + bug: "304347838" +} diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 27fd8a66fff1..5cce1c2f6234 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -68,6 +68,7 @@ android_test { "coretests-aidl", "securebox", "flag-junit", + "net_flags_lib", ], libs: [ diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java index 5a62d92e8e12..5081198f0058 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java @@ -16,6 +16,8 @@ package com.android.server.locksettings; +import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS; + import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; @@ -30,25 +32,30 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.PropertyInvalidatedCache; +import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.service.gatekeeper.GateKeeperResponse; import android.text.TextUtils; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.widget.ILockSettingsStateListener; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,6 +66,7 @@ import org.junit.runner.RunWith; @Presubmit @RunWith(AndroidJUnit4.class) public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Before public void setUp() { @@ -399,6 +407,60 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } @Test + public void testVerifyCredential_notifyLockSettingsStateListeners_whenGoodPassword() + throws Exception { + mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS); + final LockscreenCredential password = newPassword("password"); + setCredential(PRIMARY_USER_ID, password); + final ILockSettingsStateListener listener = mockLockSettingsStateListener(); + mLocalService.registerLockSettingsStateListener(listener); + + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */) + .getResponseCode()); + + verify(listener).onAuthenticationSucceeded(PRIMARY_USER_ID); + } + + @Test + public void testVerifyCredential_notifyLockSettingsStateListeners_whenBadPassword() + throws Exception { + mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS); + final LockscreenCredential password = newPassword("password"); + setCredential(PRIMARY_USER_ID, password); + final LockscreenCredential badPassword = newPassword("badPassword"); + final ILockSettingsStateListener listener = mockLockSettingsStateListener(); + mLocalService.registerLockSettingsStateListener(listener); + + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, + mService.verifyCredential(badPassword, PRIMARY_USER_ID, 0 /* flags */) + .getResponseCode()); + + verify(listener).onAuthenticationFailed(PRIMARY_USER_ID); + } + + @Test + public void testLockSettingsStateListener_registeredThenUnregistered() throws Exception { + mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS); + final LockscreenCredential password = newPassword("password"); + setCredential(PRIMARY_USER_ID, password); + final LockscreenCredential badPassword = newPassword("badPassword"); + final ILockSettingsStateListener listener = mockLockSettingsStateListener(); + + mLocalService.registerLockSettingsStateListener(listener); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */) + .getResponseCode()); + verify(listener).onAuthenticationSucceeded(PRIMARY_USER_ID); + + mLocalService.unregisterLockSettingsStateListener(listener); + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, + mService.verifyCredential(badPassword, PRIMARY_USER_ID, 0 /* flags */) + .getResponseCode()); + verify(listener, never()).onAuthenticationFailed(PRIMARY_USER_ID); + } + + @Test public void testSetCredentialNotPossibleInSecureFrpModeDuringSuw() { setUserSetupComplete(false); setSecureFrpMode(true); @@ -537,4 +599,12 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertNotEquals(0, mGateKeeperService.getSecureUserId(userId)); } } + + private ILockSettingsStateListener mockLockSettingsStateListener() { + ILockSettingsStateListener listener = mock(ILockSettingsStateListener.Stub.class); + IBinder binder = mock(IBinder.class); + when(binder.isBinderAlive()).thenReturn(true); + when(listener.asBinder()).thenReturn(binder); + return listener; + } } diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java index 13dc12032e7d..d6d2b6d9abd2 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java @@ -16,6 +16,7 @@ package com.android.server.net; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -327,12 +328,20 @@ public class NetworkManagementServiceTest { isRestrictedForLowPowerStandby.put(INetd.FIREWALL_RULE_DENY, true); expected.put(FIREWALL_CHAIN_LOW_POWER_STANDBY, isRestrictedForLowPowerStandby); + // Background chain + final ArrayMap<Integer, Boolean> isRestrictedInBackground = new ArrayMap<>(); + isRestrictedInBackground.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); + isRestrictedInBackground.put(INetd.FIREWALL_RULE_ALLOW, false); + isRestrictedInBackground.put(INetd.FIREWALL_RULE_DENY, true); + expected.put(FIREWALL_CHAIN_BACKGROUND, isRestrictedInBackground); + final int[] chains = { FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_POWERSAVE, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_RESTRICTED, - FIREWALL_CHAIN_LOW_POWER_STANDBY + FIREWALL_CHAIN_LOW_POWER_STANDBY, + FIREWALL_CHAIN_BACKGROUND }; final int[] states = { INetd.FIREWALL_RULE_ALLOW, diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 2a764526a436..4451cae8db42 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -26,12 +26,14 @@ import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND; import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE; import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED; import static android.net.ConnectivityManager.TYPE_MOBILE; @@ -48,8 +50,13 @@ import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_SYSTEM; import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND; import static android.net.NetworkPolicyManager.ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_NOT_IN_BACKGROUND; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_ALLOWLIST; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS; import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM; import static android.net.NetworkPolicyManager.ALLOWED_REASON_TOP; +import static android.net.NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.POLICY_NONE; @@ -64,6 +71,7 @@ import static android.net.NetworkStats.METERED_YES; import static android.net.NetworkTemplate.MATCH_CARRIER; import static android.net.NetworkTemplate.MATCH_MOBILE; import static android.net.NetworkTemplate.MATCH_WIFI; +import static android.os.PowerExemptionManager.REASON_OTHER; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; @@ -146,6 +154,8 @@ import android.os.Build; import android.os.Handler; import android.os.INetworkManagementService; import android.os.PersistableBundle; +import android.os.PowerExemptionManager; +import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteException; @@ -153,6 +163,9 @@ import android.os.SimpleClock; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -169,6 +182,7 @@ import android.util.Pair; import android.util.Range; import android.util.RecurrenceRule; import android.util.SparseArray; +import android.util.SparseIntArray; import androidx.test.InstrumentationRegistry; import androidx.test.filters.FlakyTest; @@ -243,6 +257,9 @@ import java.util.stream.Collectors; public class NetworkPolicyManagerServiceTest { private static final String TAG = "NetworkPolicyManagerServiceTest"; + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private static final long TEST_START = 1194220800000L; private static final String TEST_IFACE = "test0"; private static final String TEST_WIFI_NETWORK_KEY = "TestWifiNetworkKey"; @@ -285,6 +302,7 @@ public class NetworkPolicyManagerServiceTest { private @Mock TelephonyManager mTelephonyManager; private @Mock UserManager mUserManager; private @Mock NetworkStatsManager mStatsManager; + private @Mock PowerExemptionManager mPowerExemptionManager; private TestDependencies mDeps; private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor = @@ -302,6 +320,7 @@ public class NetworkPolicyManagerServiceTest { private NetworkPolicyManagerService mService; private final ArraySet<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>(); + private BroadcastReceiver mPowerAllowlistReceiver; /** * In some of the tests while initializing NetworkPolicyManagerService, @@ -446,6 +465,7 @@ public class NetworkPolicyManagerServiceTest { @Before public void callSystemReady() throws Exception { MockitoAnnotations.initMocks(this); + when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean())).thenReturn(new int[0]); final Context context = InstrumentationRegistry.getContext(); @@ -482,6 +502,8 @@ public class NetworkPolicyManagerServiceTest { return mUserManager; case Context.NETWORK_STATS_SERVICE: return mStatsManager; + case Context.POWER_EXEMPTION_SERVICE: + return mPowerExemptionManager; default: return super.getSystemService(name); } @@ -495,6 +517,9 @@ public class NetworkPolicyManagerServiceTest { @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { + if (filter.hasAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED)) { + mPowerAllowlistReceiver = receiver; + } mRegisteredReceivers.add(receiver); return super.registerReceiver(receiver, filter, broadcastPermission, scheduler); } @@ -2066,6 +2091,12 @@ public class NetworkPolicyManagerServiceTest { expectHasUseRestrictedNetworksPermission(UID_A, true); expectHasUseRestrictedNetworksPermission(UID_B, false); + // Set low enough proc-states to ensure these uids are allowed in the background chain. + // To maintain clean separation between separate firewall chains, the tests could + // check for the specific blockedReasons in the uidBlockedState. + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, 21); + callAndWaitOnUidStateChanged(UID_B, BACKGROUND_THRESHOLD_STATE - 1, 21); + Map<Integer, Integer> firewallUidRules = new ArrayMap<>(); doAnswer(arg -> { int[] uids = arg.getArgument(1); @@ -2113,7 +2144,111 @@ public class NetworkPolicyManagerServiceTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainEnabled() throws Exception { + verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true); + } + + + @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainOnProcStateChange() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + + mService.mBackgroundRestrictionDelayMs = 500; // To avoid waiting too long in tests. + + // The app will be blocked when there is no prior proc-state. + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + int procStateSeq = 23; + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, procStateSeq++); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_ALLOW); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, procStateSeq++); + + // The app should be blocked after a delay. Posting a message just after the delay and + // waiting for it to complete to ensure that the blocking code has executed. + waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionDelayMs + 1); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainOnAllowlistChange() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + + // The apps will be blocked when there is no prior proc-state. + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + assertTrue(mService.isUidNetworkingBlocked(UID_B, false)); + + final int procStateSeq = 29; + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, procStateSeq); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean())) + .thenReturn(new int[]{APP_ID_A, APP_ID_B}); + final SparseIntArray firewallUidRules = new SparseIntArray(); + doAnswer(arg -> { + final int[] uids = arg.getArgument(1); + final int[] rules = arg.getArgument(2); + assertTrue(uids.length == rules.length); + + for (int i = 0; i < uids.length; ++i) { + firewallUidRules.put(uids[i], rules[i]); + } + return null; + }).when(mNetworkManager).setFirewallUidRules(eq(FIREWALL_CHAIN_BACKGROUND), + any(int[].class), any(int[].class)); + + mPowerAllowlistReceiver.onReceive(mServiceContext, null); + + assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_A, -1)); + assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_B, -1)); + + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + assertFalse(mService.isUidNetworkingBlocked(UID_B, false)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainOnTempAllowlistChange() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + + // The app will be blocked as is no prior proc-state. + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + final int procStateSeq = 19; + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, procStateSeq); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + final NetworkPolicyManagerInternal internal = LocalServices.getService( + NetworkPolicyManagerInternal.class); + + internal.onTempPowerSaveWhitelistChange(APP_ID_A, true, REASON_OTHER, "testing"); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_ALLOW); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + internal.onTempPowerSaveWhitelistChange(APP_ID_A, false, REASON_OTHER, "testing"); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } + + @Test public void testLowPowerStandbyAllowlist() throws Exception { + // Chain background is also enabled but these procstates are important enough to be exempt. callAndWaitOnUidStateChanged(UID_A, PROCESS_STATE_TOP, 0); callAndWaitOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0); callAndWaitOnUidStateChanged(UID_C, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0); @@ -2200,7 +2335,21 @@ public class NetworkPolicyManagerServiceTest { ALLOWED_REASON_TOP), BLOCKED_REASON_NONE); effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_LOW_POWER_STANDBY, ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST), BLOCKED_REASON_NONE); - // TODO: test more combinations of blocked reasons. + + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_NOT_IN_BACKGROUND), BLOCKED_REASON_NONE); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND + | BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_NOT_IN_BACKGROUND), + BLOCKED_REASON_BATTERY_SAVER); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND + | BLOCKED_REASON_DOZE, ALLOWED_REASON_NOT_IN_BACKGROUND), + BLOCKED_REASON_DOZE); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS), BLOCKED_REASON_APP_BACKGROUND); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_POWER_SAVE_ALLOWLIST), BLOCKED_REASON_NONE); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST), BLOCKED_REASON_NONE); for (Map.Entry<Pair<Integer, Integer>, Integer> test : effectiveBlockedReasons.entrySet()) { final int expectedEffectiveBlockedReasons = test.getValue(); @@ -2529,7 +2678,6 @@ public class NetworkPolicyManagerServiceTest { private FutureIntent mRestrictBackgroundChanged; private void postMsgAndWaitForCompletion() throws InterruptedException { - final Handler handler = mService.getHandlerForTesting(); final CountDownLatch latch = new CountDownLatch(1); mService.getHandlerForTesting().post(latch::countDown); if (!latch.await(5, TimeUnit.SECONDS)) { @@ -2537,6 +2685,14 @@ public class NetworkPolicyManagerServiceTest { } } + private void waitForDelayedMessageOnHandler(long delayMs) throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + mService.getHandlerForTesting().postDelayed(latch::countDown, delayMs); + if (!latch.await(delayMs + 5_000, TimeUnit.MILLISECONDS)) { + fail("Timed out waiting for delayed msg to be handled"); + } + } + private void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) throws InterruptedException { mService.setSubscriptionPlans(subId, plans, 0, callingPackage); diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index cf4dd79e3d96..24699bf9a7ff 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -56,6 +56,7 @@ #include "java/JavaClassGenerator.h" #include "java/ManifestClassGenerator.h" #include "java/ProguardRules.h" +#include "link/FeatureFlagsFilter.h" #include "link/Linkers.h" #include "link/ManifestFixer.h" #include "link/NoDefaultResourceRemover.h" @@ -1986,6 +1987,19 @@ class Linker { context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()}); context_->SetSplitNameDependencies(app_info_.split_name_dependencies); + FeatureFlagsFilterOptions flags_filter_options; + if (context_->GetMinSdkVersion() > SDK_UPSIDE_DOWN_CAKE) { + // For API version > U, PackageManager will dynamically read the flag values and disable + // manifest elements accordingly when parsing the manifest. + // For API version <= U, we remove disabled elements from the manifest with the filter. + flags_filter_options.remove_disabled_elements = false; + flags_filter_options.flags_must_have_value = false; + } + FeatureFlagsFilter flags_filter(options_.feature_flag_values, flags_filter_options); + if (!flags_filter.Consume(context_, manifest_xml.get())) { + return 1; + } + // Override the package ID when it is "android". if (context_->GetCompilationPackage() == "android") { context_->SetPackageId(kAndroidPackageId); @@ -2530,7 +2544,7 @@ int LinkCommand::Action(const std::vector<std::string>& args) { } for (const std::string& arg : all_feature_flags_args) { - if (ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) { + if (!ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) { return 1; } } diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h index 26713fd92264..dc18b1ccda60 100644 --- a/tools/aapt2/cmd/Link.h +++ b/tools/aapt2/cmd/Link.h @@ -330,7 +330,11 @@ class LinkCommand : public Command { "should only be used together with the --static-lib flag.", &options_.merge_only); AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_); - AddOptionalFlagList("--feature-flags", "Placeholder, to be implemented.", &feature_flags_args_); + 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).", + &feature_flags_args_); } int Action(const std::vector<std::string>& args) override; diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp index 725a1b86f616..7ceb351aaa6f 100644 --- a/tools/aapt2/cmd/Link_test.cpp +++ b/tools/aapt2/cmd/Link_test.cpp @@ -16,11 +16,10 @@ #include "Link.h" -#include <android-base/file.h> - -#include "AppInfo.h" #include "Diagnostics.h" #include "LoadedApk.h" +#include "android-base/file.h" +#include "android-base/stringprintf.h" #include "test/Test.h" using testing::Eq; @@ -993,4 +992,213 @@ TEST_F(LinkTest, LocaleConfigWrongLocaleFormat) { ASSERT_FALSE(Link(link_args, &diag)); } +static void BuildSDKWithFeatureFlagAttr(const std::string& apk_path, const std::string& java_path, + CommandTestFixture* fixture, android::IDiagnostics* diag) { + const std::string android_values = + R"(<resources> + <staging-public-group type="attr" first-id="0x01fe0063"> + <public name="featureFlag" /> + </staging-public-group> + <attr name="featureFlag" format="string" /> + </resources>)"; + + SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values}; + BuildSDK({source_xml}, apk_path, java_path, fixture, diag); +} + +TEST_F(LinkTest, FeatureFlagDisabled_SdkAtMostUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_UPSIDE_DOWN_CAKE); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag=false"); + + const std::string app_apk = GetTestPath("app.apk"); + BuildApk({}, app_apk, std::move(app_link_args), this, &diag); + + // Permission element should be removed if flag is disabled + auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag); + ASSERT_THAT(apk, NotNull()); + auto apk_manifest = apk->GetManifest(); + ASSERT_THAT(apk_manifest, NotNull()); + auto root = apk_manifest->root.get(); + ASSERT_THAT(root, NotNull()); + auto maybe_removed = root->FindChild({}, "permission"); + ASSERT_THAT(maybe_removed, IsNull()); +} + +TEST_F(LinkTest, FeatureFlagEnabled_SdkAtMostUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_UPSIDE_DOWN_CAKE); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag=true"); + + const std::string app_apk = GetTestPath("app.apk"); + BuildApk({}, app_apk, std::move(app_link_args), this, &diag); + + // Permission element should be kept if flag is enabled + auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag); + ASSERT_THAT(apk, NotNull()); + auto apk_manifest = apk->GetManifest(); + ASSERT_THAT(apk_manifest, NotNull()); + auto root = apk_manifest->root.get(); + ASSERT_THAT(root, NotNull()); + auto maybe_removed = root->FindChild({}, "permission"); + ASSERT_THAT(maybe_removed, NotNull()); +} + +TEST_F(LinkTest, FeatureFlagWithNoValue_SdkAtMostUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_UPSIDE_DOWN_CAKE); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag="); + + // Flags must have values if <= UDC + const std::string app_apk = GetTestPath("app.apk"); + ASSERT_FALSE(Link(app_link_args.Build(app_apk), &diag)); +} + +TEST_F(LinkTest, FeatureFlagDisabled_SdkAfterUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_CUR_DEVELOPMENT); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag=false"); + + const std::string app_apk = GetTestPath("app.apk"); + BuildApk({}, app_apk, std::move(app_link_args), this, &diag); + + // Permission element should be kept if > UDC, regardless of flag value + auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag); + ASSERT_THAT(apk, NotNull()); + auto apk_manifest = apk->GetManifest(); + ASSERT_THAT(apk_manifest, NotNull()); + auto root = apk_manifest->root.get(); + ASSERT_THAT(root, NotNull()); + auto maybe_removed = root->FindChild({}, "permission"); + ASSERT_THAT(maybe_removed, NotNull()); +} + +TEST_F(LinkTest, FeatureFlagEnabled_SdkAfterUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_CUR_DEVELOPMENT); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag=true"); + + const std::string app_apk = GetTestPath("app.apk"); + BuildApk({}, app_apk, std::move(app_link_args), this, &diag); + + // Permission element should be kept if > UDC, regardless of flag value + auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag); + ASSERT_THAT(apk, NotNull()); + auto apk_manifest = apk->GetManifest(); + ASSERT_THAT(apk_manifest, NotNull()); + auto root = apk_manifest->root.get(); + ASSERT_THAT(root, NotNull()); + auto maybe_removed = root->FindChild({}, "permission"); + ASSERT_THAT(maybe_removed, NotNull()); +} + +TEST_F(LinkTest, FeatureFlagWithNoValue_SdkAfterUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_CUR_DEVELOPMENT); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag="); + + const std::string app_apk = GetTestPath("app.apk"); + BuildApk({}, app_apk, std::move(app_link_args), this, &diag); + + // Permission element should be kept if > UDC, regardless of flag value + auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag); + ASSERT_THAT(apk, NotNull()); + auto apk_manifest = apk->GetManifest(); + ASSERT_THAT(apk_manifest, NotNull()); + auto root = apk_manifest->root.get(); + ASSERT_THAT(root, NotNull()); + auto maybe_removed = root->FindChild({}, "permission"); + ASSERT_THAT(maybe_removed, NotNull()); +} + } // namespace aapt diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index 93c1b61f9a57..02e4beaed949 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -251,10 +251,13 @@ bool AppendArgsFromFile(StringPiece path, std::vector<std::string>* out_arglist, return false; } - for (StringPiece line : util::Tokenize(contents, ' ')) { + for (StringPiece line : util::Tokenize(contents, '\n')) { line = util::TrimWhitespace(line); - if (!line.empty()) { - out_arglist->emplace_back(line); + for (StringPiece arg : util::Tokenize(line, ' ')) { + arg = util::TrimWhitespace(arg); + if (!arg.empty()) { + out_arglist->emplace_back(arg); + } } } return true; @@ -270,10 +273,13 @@ bool AppendSetArgsFromFile(StringPiece path, std::unordered_set<std::string>* ou return false; } - for (StringPiece line : util::Tokenize(contents, ' ')) { + for (StringPiece line : util::Tokenize(contents, '\n')) { line = util::TrimWhitespace(line); - if (!line.empty()) { - out_argset->emplace(line); + for (StringPiece arg : util::Tokenize(line, ' ')) { + arg = util::TrimWhitespace(arg); + if (!arg.empty()) { + out_argset->emplace(arg); + } } } return true; diff --git a/tools/aapt2/util/Files_test.cpp b/tools/aapt2/util/Files_test.cpp index 6c380808c0df..618a3e0d86ae 100644 --- a/tools/aapt2/util/Files_test.cpp +++ b/tools/aapt2/util/Files_test.cpp @@ -25,6 +25,9 @@ using ::android::base::StringPrintf; +using ::testing::ElementsAre; +using ::testing::UnorderedElementsAre; + namespace aapt { namespace file { @@ -34,9 +37,11 @@ constexpr const char sTestDirSep = '\\'; constexpr const char sTestDirSep = '/'; #endif -class FilesTest : public ::testing::Test { +class FilesTest : public TestDirectoryFixture { public: void SetUp() override { + TestDirectoryFixture::SetUp(); + std::stringstream builder; builder << "hello" << sDirSep << "there"; expected_path_ = builder.str(); @@ -66,6 +71,42 @@ TEST_F(FilesTest, AppendPathWithLeadingOrTrailingSeparators) { EXPECT_EQ(expected_path_, base); } +TEST_F(FilesTest, AppendArgsFromFile) { + const std::string args_file = GetTestPath("args.txt"); + WriteFile(args_file, + " \n" + "arg1 arg2 arg3 \n" + " arg4 arg5"); + std::vector<std::string> args; + std::string error; + ASSERT_TRUE(AppendArgsFromFile(args_file, &args, &error)); + EXPECT_THAT(args, ElementsAre("arg1", "arg2", "arg3", "arg4", "arg5")); +} + +TEST_F(FilesTest, AppendArgsFromFile_InvalidFile) { + std::vector<std::string> args; + std::string error; + ASSERT_FALSE(AppendArgsFromFile(GetTestPath("not_found.txt"), &args, &error)); +} + +TEST_F(FilesTest, AppendSetArgsFromFile) { + const std::string args_file = GetTestPath("args.txt"); + WriteFile(args_file, + " \n" + "arg2 arg4 arg1 \n" + " arg5 arg3"); + std::unordered_set<std::string> args; + std::string error; + ASSERT_TRUE(AppendSetArgsFromFile(args_file, &args, &error)); + EXPECT_THAT(args, UnorderedElementsAre("arg1", "arg2", "arg3", "arg4", "arg5")); +} + +TEST_F(FilesTest, AppendSetArgsFromFile_InvalidFile) { + std::unordered_set<std::string> args; + std::string error; + ASSERT_FALSE(AppendSetArgsFromFile(GetTestPath("not_found.txt"), &args, &error)); +} + #ifdef _WIN32 TEST_F(FilesTest, WindowsMkdirsLongPath) { // Creating directory paths longer than the Windows maximum path length (260 charatcers) should |