diff options
92 files changed, 2183 insertions, 757 deletions
diff --git a/apct-tests/perftests/core/src/android/libcore/OWNERS b/apct-tests/perftests/core/src/android/libcore/OWNERS new file mode 100644 index 000000000000..2d365747473a --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/OWNERS @@ -0,0 +1,2 @@ +# Bug component: 24949 +include platform/libcore:/OWNERS diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 61424ae0e158..1b9cf2648a3f 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -27,6 +27,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -280,6 +281,18 @@ public class AlarmManager { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) public static final long ENABLE_USE_EXACT_ALARM = 218533173L; + /** + * For apps targeting {@link Build.VERSION_CODES#TIRAMISU} or above, the permission + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} will be denied, unless the user explicitly + * allows it from Settings. + * + * TODO (b/226439802): change to EnabledSince(T) after SDK finalization. + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2) + public static final long SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = 226439802L; + @UnsupportedAppUsage private final IAlarmManager mService; private final Context mContext; diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index f67e8d2baa11..881453fe237d 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -66,6 +66,7 @@ import android.app.IAlarmListener; import android.app.IAlarmManager; import android.app.PendingIntent; import android.app.compat.CompatChanges; +import android.app.role.RoleManager; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; @@ -164,6 +165,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Set; import java.util.TimeZone; @@ -224,6 +226,7 @@ public class AlarmManagerService extends SystemService { private ActivityManagerInternal mActivityManagerInternal; private final EconomyManagerInternal mEconomyManagerInternal; private PackageManagerInternal mPackageManagerInternal; + private RoleManager mRoleManager; private volatile PermissionManagerServiceInternal mLocalPermissionManager; final Object mLock = new Object(); @@ -562,6 +565,9 @@ public class AlarmManagerService extends SystemService { @VisibleForTesting static final String KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = "kill_on_schedule_exact_alarm_revoked"; + @VisibleForTesting + static final String KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = + "schedule_exact_alarm_denied_by_default"; private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; @@ -606,6 +612,9 @@ public class AlarmManagerService extends SystemService { private static final boolean DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = true; + // TODO(b/226439802): Flip to true. + private static final boolean DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = false; + // Minimum futurity of a new alarm public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; @@ -693,6 +702,14 @@ public class AlarmManagerService extends SystemService { public boolean KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED = DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED; + /** + * When this is {@code true}, apps with the change + * {@link AlarmManager#SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT} enabled will not get + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} unless the user grants it to them. + */ + public volatile boolean SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = + DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT; + public boolean USE_TARE_POLICY = Settings.Global.DEFAULT_ENABLE_TARE == 1; private long mLastAllowWhileIdleWhitelistDuration = -1; @@ -876,6 +893,15 @@ public class AlarmManagerService extends SystemService { KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED, DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED); break; + case KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT: + final boolean oldValue = SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT; + + SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = properties.getBoolean( + KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, + DEFAULT_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT); + handleScheduleExactAlarmDeniedByDefaultChange(oldValue, + SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT); + break; default: if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) { // The quotas need to be updated in order, so we can't just rely @@ -946,6 +972,15 @@ public class AlarmManagerService extends SystemService { } } + private void handleScheduleExactAlarmDeniedByDefaultChange(boolean oldValue, + boolean newValue) { + if (oldValue == newValue) { + return; + } + mHandler.obtainMessage(AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE, + newValue).sendToTarget(); + } + private void migrateAlarmsToNewStoreLocked() { final AlarmStore newStore = LAZY_BATCHING ? new LazyAlarmStore() : new BatchingAlarmStore(); @@ -1122,6 +1157,9 @@ public class AlarmManagerService extends SystemService { pw.print(KEY_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED, KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED); pw.println(); + pw.print(KEY_SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, + SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT); + pw.println(); pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY); pw.println(); @@ -1892,11 +1930,10 @@ public class AlarmManagerService extends SystemService { if (hasUseExactAlarmInternal(packageName, uid)) { return; } - - final boolean requested = mExactAlarmCandidates.contains( - UserHandle.getAppId(uid)); - final boolean denyListed = - mConstants.EXACT_ALARM_DENY_LIST.contains(packageName); + if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) { + // Permission not requested, app op doesn't matter. + return; + } final int newMode = mAppOps.checkOpNoThrow( AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, packageName); @@ -1913,11 +1950,24 @@ public class AlarmManagerService extends SystemService { mLastOpScheduleExactAlarm.setValueAt(index, newMode); } } - - final boolean hadPermission = getScheduleExactAlarmState(requested, - denyListed, oldMode); - final boolean hasPermission = getScheduleExactAlarmState(requested, - denyListed, newMode); + if (oldMode == newMode) { + return; + } + final boolean allowedByDefault = + isScheduleExactAlarmAllowedByDefault(packageName, uid); + + final boolean hadPermission; + if (oldMode != AppOpsManager.MODE_DEFAULT) { + hadPermission = (oldMode == AppOpsManager.MODE_ALLOWED); + } else { + hadPermission = allowedByDefault; + } + final boolean hasPermission; + if (newMode != AppOpsManager.MODE_DEFAULT) { + hasPermission = (newMode == AppOpsManager.MODE_ALLOWED); + } else { + hasPermission = allowedByDefault; + } if (hadPermission && !hasPermission) { mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS, @@ -1939,6 +1989,8 @@ public class AlarmManagerService extends SystemService { LocalServices.getService(AppStandbyInternal.class); appStandbyInternal.addListener(new AppStandbyTracker()); + mRoleManager = getContext().getSystemService(RoleManager.class); + mMetricsHelper.registerPuller(() -> mAlarmStore); } } @@ -2525,19 +2577,6 @@ public class AlarmManagerService extends SystemService { } } - private static boolean getScheduleExactAlarmState(boolean requested, boolean denyListed, - int appOpMode) { - // This does not account for the state of the USE_EXACT_ALARM permission. - // The caller should do that separately. - if (!requested) { - return false; - } - if (appOpMode == AppOpsManager.MODE_DEFAULT) { - return !denyListed; - } - return appOpMode == AppOpsManager.MODE_ALLOWED; - } - boolean hasUseExactAlarmInternal(String packageName, int uid) { return isUseExactAlarmEnabled(packageName, UserHandle.getUserId(uid)) && (PermissionChecker.checkPermissionForPreflight(getContext(), @@ -2545,6 +2584,32 @@ public class AlarmManagerService extends SystemService { packageName) == PermissionChecker.PERMISSION_GRANTED); } + /** + * Returns whether SCHEDULE_EXACT_ALARM is allowed by default. + */ + boolean isScheduleExactAlarmAllowedByDefault(String packageName, int uid) { + if (isScheduleExactAlarmDeniedByDefault(packageName, UserHandle.getUserId(uid))) { + + // This is essentially like changing the protection level of the permission to + // (privileged|signature|role|appop), but have to implement this logic to maintain + // compatibility for older apps. + if (mPackageManagerInternal.isPlatformSigned(packageName) + || mPackageManagerInternal.isUidPrivileged(uid)) { + return true; + } + final long token = Binder.clearCallingIdentity(); + try { + final List<String> wellbeingHolders = (mRoleManager != null) + ? mRoleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING) + : Collections.emptyList(); + return wellbeingHolders.contains(packageName); + } finally { + Binder.restoreCallingIdentity(token); + } + } + return !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName); + } + boolean hasScheduleExactAlarmInternal(String packageName, int uid) { final long start = mStatLogger.getTime(); @@ -2560,7 +2625,7 @@ public class AlarmManagerService extends SystemService { final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid, packageName); if (mode == AppOpsManager.MODE_DEFAULT) { - hasPermission = !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName); + hasPermission = isScheduleExactAlarmAllowedByDefault(packageName, uid); } else { hasPermission = (mode == AppOpsManager.MODE_ALLOWED); } @@ -2860,6 +2925,13 @@ public class AlarmManagerService extends SystemService { packageName, UserHandle.of(userId)); } + private boolean isScheduleExactAlarmDeniedByDefault(String packageName, int userId) { + return mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT + && CompatChanges.isChangeEnabled( + AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, packageName, + UserHandle.of(userId)); + } + @NeverCompile // Avoid size overhead of debugging code. void dumpImpl(IndentingPrintWriter pw) { synchronized (mLock) { @@ -3769,26 +3841,27 @@ public class AlarmManagerService extends SystemService { if (!isExactAlarmChangeEnabled(changedPackage, userId)) { continue; } + if (isScheduleExactAlarmDeniedByDefault(changedPackage, userId)) { + continue; + } if (hasUseExactAlarmInternal(changedPackage, uid)) { continue; } + if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) { + // Permission isn't requested, deny list doesn't matter. + continue; + } final int appOpMode; synchronized (mLock) { appOpMode = mLastOpScheduleExactAlarm.get(uid, AppOpsManager.opToDefaultMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM)); } - final boolean requested = mExactAlarmCandidates.contains(UserHandle.getAppId(uid)); - - // added: true => package was added to the deny list - // added: false => package was removed from the deny list - final boolean hadPermission = getScheduleExactAlarmState(requested, !added, - appOpMode); - final boolean hasPermission = getScheduleExactAlarmState(requested, added, - appOpMode); - - if (hadPermission == hasPermission) { + if (appOpMode != AppOpsManager.MODE_DEFAULT) { + // Deny list doesn't matter. continue; } + // added: true => package was added to the deny list + // added: false => package was removed from the deny list if (added) { synchronized (mLock) { removeExactAlarmsOnPermissionRevokedLocked(uid, @@ -4634,6 +4707,7 @@ public class AlarmManagerService extends SystemService { public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11; public static final int TARE_AFFORDABILITY_CHANGED = 12; public static final int CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE = 13; + public static final int CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE = 14; AlarmHandler() { super(Looper.myLooper()); @@ -4759,6 +4833,35 @@ public class AlarmManagerService extends SystemService { } } break; + case CHECK_EXACT_ALARM_PERMISSION_ON_FEATURE_TOGGLE: + final boolean defaultDenied = (Boolean) msg.obj; + + final int[] startedUserIds = mActivityManagerInternal.getStartedUserIds(); + for (int appId : mExactAlarmCandidates) { + for (int userId : startedUserIds) { + uid = UserHandle.getUid(userId, appId); + + final AndroidPackage packageForUid = + mPackageManagerInternal.getPackage(uid); + if (packageForUid == null) { + continue; + } + final String pkg = packageForUid.getPackageName(); + if (defaultDenied) { + if (!hasScheduleExactAlarmInternal(pkg, uid) + && !hasUseExactAlarmInternal(pkg, uid)) { + synchronized (mLock) { + removeExactAlarmsOnPermissionRevokedLocked(uid, pkg, + true); + } + } + } else if (hasScheduleExactAlarmInternal(pkg, uid)) { + sendScheduleExactAlarmPermissionStateChangedBroadcast(pkg, + UserHandle.getUserId(uid)); + } + } + } + break; default: // nope, just ignore it break; diff --git a/core/java/android/hardware/input/InputDeviceSensorManager.java b/core/java/android/hardware/input/InputDeviceSensorManager.java index 89db857b860b..8a40d00327f1 100644 --- a/core/java/android/hardware/input/InputDeviceSensorManager.java +++ b/core/java/android/hardware/input/InputDeviceSensorManager.java @@ -98,7 +98,7 @@ public class InputDeviceSensorManager implements InputManager.InputDeviceListene */ private void updateInputDeviceSensorInfoLocked(int deviceId) { final InputDevice inputDevice = InputDevice.getDevice(deviceId); - if (inputDevice.hasSensor()) { + if (inputDevice != null && inputDevice.hasSensor()) { final InputSensorInfo[] sensorInfos = mInputManager.getSensorList(deviceId); populateSensorsForInputDeviceLocked(deviceId, sensorInfos); diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index 03d11515c0a8..bd6c4e1a0c09 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -151,6 +151,8 @@ final class NavigationBarController { private boolean mDrawLegacyNavigationBarBackground; + private final Rect mTempRect = new Rect(); + Impl(@NonNull InputMethodService inputMethodService) { mService = inputMethodService; } @@ -281,13 +283,12 @@ final class NavigationBarController { touchableRegion.set(originalInsets.touchableRegion); break; } - final Rect navBarRect = new Rect(decor.getLeft(), - decor.getBottom() - systemInsets.bottom, + mTempRect.set(decor.getLeft(), decor.getBottom() - systemInsets.bottom, decor.getRight(), decor.getBottom()); if (touchableRegion == null) { - touchableRegion = new Region(navBarRect); + touchableRegion = new Region(mTempRect); } else { - touchableRegion.union(navBarRect); + touchableRegion.union(mTempRect); } dest.touchableRegion.set(touchableRegion); diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java index 3abe83bd3373..1b503b11816f 100644 --- a/core/java/android/net/Ikev2VpnProfile.java +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -25,12 +25,6 @@ import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384; import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512; import static android.net.IpSecAlgorithm.CRYPT_AES_CBC; import static android.net.IpSecAlgorithm.CRYPT_AES_CTR; -import static android.net.eap.EapSessionConfig.EapMsChapV2Config; -import static android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig; -import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignLocalConfig; -import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignRemoteConfig; -import static android.net.ipsec.ike.IkeSessionParams.IkeAuthEapConfig; -import static android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.internal.util.Preconditions.checkStringNotEmpty; @@ -40,6 +34,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.content.pm.PackageManager; +import android.net.ipsec.ike.IkeDerAsn1DnIdentification; import android.net.ipsec.ike.IkeFqdnIdentification; import android.net.ipsec.ike.IkeIdentification; import android.net.ipsec.ike.IkeIpv4AddrIdentification; @@ -119,8 +114,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { DEFAULT_ALGORITHMS = Collections.unmodifiableList(algorithms); } - @NonNull private final String mServerAddr; - @NonNull private final String mUserIdentity; + @Nullable private final String mServerAddr; + @Nullable private final String mUserIdentity; // PSK authentication @Nullable private final byte[] mPresharedKey; @@ -146,8 +141,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { private Ikev2VpnProfile( int type, - @NonNull String serverAddr, - @NonNull String userIdentity, + @Nullable String serverAddr, + @Nullable String userIdentity, @Nullable byte[] presharedKey, @Nullable X509Certificate serverRootCaCert, @Nullable String username, @@ -165,8 +160,6 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @Nullable IkeTunnelConnectionParams ikeTunConnParams) { super(type, excludeLocalRoutes, requiresInternetValidation); - checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address"); - checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity"); checkNotNull(allowedAlgorithms, MISSING_PARAM_MSG_TMPL, "Allowed Algorithms"); mServerAddr = serverAddr; @@ -191,18 +184,12 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { mIsMetered = isMetered; mMaxMtu = maxMtu; mIsRestrictedToTestNetworks = restrictToTestNetworks; - mIkeTunConnParams = ikeTunConnParams; validate(); } private void validate() { - // Server Address not validated except to check an address was provided. This allows for - // dual-stack servers and hostname based addresses. - checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address"); - checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity"); - // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6 // networks, the VPN must provide a link fulfilling the stricter of the two conditions // (at least that of the IPv6 MTU). @@ -210,6 +197,15 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { throw new IllegalArgumentException("Max MTU must be at least" + IPV6_MIN_MTU); } + // Skip validating the other fields if mIkeTunConnParams is set because the required + // information should all come from the mIkeTunConnParams. + if (mIkeTunConnParams != null) return; + + // Server Address not validated except to check an address was provided. This allows for + // dual-stack servers and hostname based addresses. + checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address"); + checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity"); + switch (mType) { case TYPE_IKEV2_IPSEC_USER_PASS: checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username"); @@ -286,22 +282,31 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { /** Retrieves the server address string. */ @NonNull public String getServerAddr() { - return mServerAddr; + if (mIkeTunConnParams == null) return mServerAddr; + + final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams(); + return ikeSessionParams.getServerHostname(); } /** Retrieves the user identity. */ @NonNull public String getUserIdentity() { - return mUserIdentity; + if (mIkeTunConnParams == null) return mUserIdentity; + + final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams(); + return getUserIdentityFromIkeSession(ikeSessionParams); } /** * Retrieves the pre-shared key. * - * <p>May be null if the profile is not using Pre-shared key authentication. + * <p>May be null if the profile is not using Pre-shared key authentication, or the profile is + * built from an {@link IkeTunnelConnectionParams}. */ @Nullable public byte[] getPresharedKey() { + if (mIkeTunConnParams != null) return null; + return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length); } @@ -309,46 +314,62 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { * Retrieves the certificate for the server's root CA. * * <p>May be null if the profile is not using RSA Digital Signature Authentication or - * Username/Password authentication + * Username/Password authentication, or the profile is built from an + * {@link IkeTunnelConnectionParams}. */ @Nullable public X509Certificate getServerRootCaCert() { + if (mIkeTunConnParams != null) return null; + return mServerRootCaCert; } - /** * Retrieves the username. * - * <p>May be null if the profile is not using Username/Password authentication + * <p>May be null if the profile is not using Username/Password authentication, or the profile + * is built from an {@link IkeTunnelConnectionParams}. */ @Nullable public String getUsername() { + if (mIkeTunConnParams != null) return null; + return mUsername; } /** * Retrieves the password. * - * <p>May be null if the profile is not using Username/Password authentication + * <p>May be null if the profile is not using Username/Password authentication, or the profile + * is built from an {@link IkeTunnelConnectionParams}. */ @Nullable public String getPassword() { + if (mIkeTunConnParams != null) return null; + return mPassword; } /** * Retrieves the RSA private key. * - * <p>May be null if the profile is not using RSA Digital Signature authentication + * <p>May be null if the profile is not using RSA Digital Signature authentication, or the + * profile is built from an {@link IkeTunnelConnectionParams}. */ @Nullable public PrivateKey getRsaPrivateKey() { + if (mIkeTunConnParams != null) return null; + return mRsaPrivateKey; } - /** Retrieves the user certificate, if any was set. */ + /** Retrieves the user certificate, if any was set. + * + * <p>May be null if the profile is built from an {@link IkeTunnelConnectionParams}. + */ @Nullable public X509Certificate getUserCert() { + if (mIkeTunConnParams != null) return null; + return mUserCert; } @@ -358,9 +379,14 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { return mProxyInfo; } - /** Returns all the algorithms allowed by this VPN profile. */ + /** Returns all the algorithms allowed by this VPN profile. + * + * <p>May be an empty list if the profile is built from an {@link IkeTunnelConnectionParams}. + */ @NonNull public List<String> getAllowedAlgorithms() { + if (mIkeTunConnParams != null) return new ArrayList<>(); + return mAllowedAlgorithms; } @@ -455,18 +481,25 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @NonNull public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException { final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */, - mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation); - profile.type = mType; - profile.server = mServerAddr; - profile.ipsecIdentifier = mUserIdentity; + mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation, + mIkeTunConnParams); + + profile.server = getServerAddr(); + profile.ipsecIdentifier = getUserIdentity(); profile.proxy = mProxyInfo; - profile.setAllowedAlgorithms(mAllowedAlgorithms); profile.isBypassable = mIsBypassable; profile.isMetered = mIsMetered; profile.maxMtu = mMaxMtu; profile.areAuthParamsInline = true; profile.saveLogin = true; + // The other fields should come from mIkeTunConnParams if it's available. + if (mIkeTunConnParams != null) { + profile.type = VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS; + return profile; + } + profile.type = mType; + profile.setAllowedAlgorithms(mAllowedAlgorithms); switch (mType) { case TYPE_IKEV2_IPSEC_USER_PASS: profile.username = mUsername; @@ -516,10 +549,47 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @NonNull public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile) throws GeneralSecurityException { - // TODO: Build the VpnProfile from mIkeTunConnParams if it exists. - final Builder builder = new Builder(profile.server, profile.ipsecIdentifier); + final Builder builder; + if (profile.ikeTunConnParams == null) { + builder = new Builder(profile.server, profile.ipsecIdentifier); + builder.setAllowedAlgorithms(profile.getAllowedAlgorithms()); + + switch (profile.type) { + case TYPE_IKEV2_IPSEC_USER_PASS: + builder.setAuthUsernamePassword( + profile.username, + profile.password, + certificateFromPemString(profile.ipsecCaCert)); + break; + case TYPE_IKEV2_IPSEC_PSK: + builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret)); + break; + case TYPE_IKEV2_IPSEC_RSA: + final PrivateKey key; + if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) { + final String alias = + profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length()); + key = getPrivateKeyFromAndroidKeystore(alias); + } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) { + key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length())); + } else { + throw new IllegalArgumentException("Invalid RSA private key prefix"); + } + + final X509Certificate userCert = + certificateFromPemString(profile.ipsecUserCert); + final X509Certificate serverRootCa = + certificateFromPemString(profile.ipsecCaCert); + builder.setAuthDigitalSignature(userCert, key, serverRootCa); + break; + default: + throw new IllegalArgumentException("Invalid auth method set"); + } + } else { + builder = new Builder(profile.ikeTunConnParams); + } + builder.setProxy(profile.proxy); - builder.setAllowedAlgorithms(profile.getAllowedAlgorithms()); builder.setBypassable(profile.isBypassable); builder.setMetered(profile.isMetered); builder.setMaxMtu(profile.maxMtu); @@ -527,36 +597,6 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { builder.restrictToTestNetworks(); } - switch (profile.type) { - case TYPE_IKEV2_IPSEC_USER_PASS: - builder.setAuthUsernamePassword( - profile.username, - profile.password, - certificateFromPemString(profile.ipsecCaCert)); - break; - case TYPE_IKEV2_IPSEC_PSK: - builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret)); - break; - case TYPE_IKEV2_IPSEC_RSA: - final PrivateKey key; - if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) { - final String alias = - profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length()); - key = getPrivateKeyFromAndroidKeystore(alias); - } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) { - key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length())); - } else { - throw new IllegalArgumentException("Invalid RSA private key prefix"); - } - - final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert); - final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert); - builder.setAuthDigitalSignature(userCert, key, serverRootCa); - break; - default: - throw new IllegalArgumentException("Invalid auth method set"); - } - if (profile.excludeLocalRoutes && !profile.isBypassable) { Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN"); } @@ -678,82 +718,13 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { } private static void checkBuilderSetter(boolean constructedFromIkeTunConParams, - @NonNull String message) { + @NonNull String field) { if (constructedFromIkeTunConParams) { - throw new IllegalArgumentException("Constructed using IkeTunnelConnectionParams " - + "should not set " + message); - } - } - - private static int getTypeFromIkeSession(@NonNull IkeSessionParams params) { - final IkeAuthConfig config = params.getLocalAuthConfig(); - if (config instanceof IkeAuthDigitalSignLocalConfig) { - return TYPE_IKEV2_IPSEC_RSA; - } else if (config instanceof IkeAuthEapConfig) { - return TYPE_IKEV2_IPSEC_USER_PASS; - } else if (config instanceof IkeAuthPskConfig) { - return TYPE_IKEV2_IPSEC_PSK; - } else { - throw new IllegalStateException("Invalid local IkeAuthConfig"); + throw new IllegalArgumentException( + field + " can't be set with IkeTunnelConnectionParams builder"); } } - @Nullable - private static String getPasswordFromIkeSession(@NonNull IkeSessionParams params) { - if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null; - - final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig(); - final EapMsChapV2Config eapMsChapV2Config = - ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config(); - return (eapMsChapV2Config != null) ? eapMsChapV2Config.getPassword() : null; - } - - @Nullable - private static String getUsernameFromIkeSession(@NonNull IkeSessionParams params) { - if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null; - - final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig(); - final EapMsChapV2Config eapMsChapV2Config = - ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config(); - return (eapMsChapV2Config != null) ? eapMsChapV2Config.getUsername() : null; - } - - @Nullable - private static X509Certificate getUserCertFromIkeSession(@NonNull IkeSessionParams params) { - if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null; - - final IkeAuthDigitalSignLocalConfig config = - (IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig(); - return config.getClientEndCertificate(); - } - - @Nullable - private static X509Certificate getServerRootCaCertFromIkeSession( - @NonNull IkeSessionParams params) { - if (!(params.getRemoteAuthConfig() instanceof IkeAuthDigitalSignRemoteConfig)) return null; - - final IkeAuthDigitalSignRemoteConfig config = - (IkeAuthDigitalSignRemoteConfig) params.getRemoteAuthConfig(); - return config.getRemoteCaCert(); - } - - @Nullable - private static PrivateKey getRsaPrivateKeyFromIkeSession(@NonNull IkeSessionParams params) { - if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null; - - final IkeAuthDigitalSignLocalConfig config = - (IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig(); - return config.getPrivateKey(); - } - - @Nullable - private static byte[] getPresharedKeyFromIkeSession(@NonNull IkeSessionParams params) { - if (!(params.getLocalAuthConfig() instanceof IkeAuthPskConfig)) return null; - - final IkeAuthPskConfig config = (IkeAuthPskConfig) params.getLocalAuthConfig(); - return config.getPsk(); - } - @NonNull private static String getUserIdentityFromIkeSession(@NonNull IkeSessionParams params) { final IkeIdentification ident = params.getLocalIdentification(); @@ -768,6 +739,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { return ((IkeIpv4AddrIdentification) ident).ipv4Address.getHostAddress(); } else if (ident instanceof IkeIpv6AddrIdentification) { return ((IkeIpv6AddrIdentification) ident).ipv6Address.getHostAddress(); + } else if (ident instanceof IkeDerAsn1DnIdentification) { + throw new IllegalArgumentException("Unspported ASN.1 encoded identities"); } else { throw new IllegalArgumentException("Unknown IkeIdentification to get user identity"); } @@ -776,8 +749,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { /** A incremental builder for IKEv2 VPN profiles */ public static final class Builder { private int mType = -1; - @NonNull private final String mServerAddr; - @NonNull private final String mUserIdentity; + @Nullable private final String mServerAddr; + @Nullable private final String mUserIdentity; // PSK authentication @Nullable private byte[] mPresharedKey; @@ -831,19 +804,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { checkNotNull(ikeTunConnParams, MISSING_PARAM_MSG_TMPL, "ikeTunConnParams"); mIkeTunConnParams = ikeTunConnParams; - - final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams(); - mServerAddr = ikeSessionParams.getServerHostname(); - - mType = getTypeFromIkeSession(ikeSessionParams); - mUserCert = getUserCertFromIkeSession(ikeSessionParams); - mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams); - mRsaPrivateKey = getRsaPrivateKeyFromIkeSession(ikeSessionParams); - mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams); - mUsername = getUsernameFromIkeSession(ikeSessionParams); - mPassword = getPasswordFromIkeSession(ikeSessionParams); - mPresharedKey = getPresharedKeyFromIkeSession(ikeSessionParams); - mUserIdentity = getUserIdentityFromIkeSession(ikeSessionParams); + mServerAddr = null; + mUserIdentity = null; } private void resetAuthParams() { @@ -862,6 +824,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { * authentication method may be set. This method will overwrite any previously set * authentication method. * + * <p>It's not allowed to set this if this {@link Builder} is constructed from an + * {@link IkeTunnelConnectionParams}. This information should be retrieved from + * {@link IkeTunnelConnectionParams} + * * @param user the username to be used for EAP-MSCHAPv2 authentication * @param pass the password to be used for EAP-MSCHAPv2 authentication * @param serverRootCa the root certificate to be used for verifying the identity of the @@ -898,6 +864,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { * Only one authentication method may be set. This method will overwrite any previously set * authentication method. * + * <p>It's not allowed to set this if this {@link Builder} is constructed from an + * {@link IkeTunnelConnectionParams}. This information should be retrieved from + * {@link IkeTunnelConnectionParams} + * * @param userCert the username to be used for RSA Digital signiture authentication * @param key the PrivateKey instance associated with the user ceritificate, used for * constructing the signature @@ -936,6 +906,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { * authentication method may be set. This method will overwrite any previously set * authentication method. * + * <p>It's not allowed to set this if this {@link Builder} is constructed from an + * {@link IkeTunnelConnectionParams}. This information should be retrieved from + * {@link IkeTunnelConnectionParams} + * * @param psk the key to be used for Pre-Shared Key authentication * @return this {@link Builder} object to facilitate chaining of method calls */ @@ -1068,6 +1042,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { * Authentication, and one that provides Encryption. Authenticated Encryption with * Associated Data (AEAD) algorithms provide both Authentication and Encryption. * + * <p>It's not allowed to set this if this {@link Builder} is constructed from an + * {@link IkeTunnelConnectionParams}. This information should be retrieved from + * {@link IkeTunnelConnectionParams} + * * <p>By default, this profile will use any algorithm defined in {@link IpSecAlgorithm}, * with the exception of those considered insecure (as described above). * @@ -1079,6 +1057,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS) public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) { checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames"); + checkBuilderSetter(mIkeTunConnParams != null, "algorithmNames"); validateAllowedAlgorithms(algorithmNames); mAllowedAlgorithms = algorithmNames; diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java index 2dac81c66d2a..e98d046e8c6c 100644 --- a/core/java/android/view/RemoteAnimationTarget.java +++ b/core/java/android/view/RemoteAnimationTarget.java @@ -33,6 +33,7 @@ import static android.view.RemoteAnimationTargetProto.TASK_ID; import static android.view.RemoteAnimationTargetProto.WINDOW_CONFIGURATION; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; +import android.annotation.ColorInt; import android.annotation.IntDef; import android.app.ActivityManager; import android.app.TaskInfo; @@ -221,6 +222,12 @@ public class RemoteAnimationTarget implements Parcelable { */ public boolean hasAnimatingParent; + /** + * The background color of animation in case the task info is not available if the transition + * is activity level. + */ + public @ColorInt int backgroundColor; + public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent, Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position, Rect localBounds, Rect screenSpaceBounds, @@ -279,6 +286,7 @@ public class RemoteAnimationTarget implements Parcelable { allowEnterPip = in.readBoolean(); windowType = in.readInt(); hasAnimatingParent = in.readBoolean(); + backgroundColor = in.readInt(); } @Override @@ -307,6 +315,7 @@ public class RemoteAnimationTarget implements Parcelable { dest.writeBoolean(allowEnterPip); dest.writeInt(windowType); dest.writeBoolean(hasAnimatingParent); + dest.writeInt(backgroundColor); } public void dump(PrintWriter pw, String prefix) { @@ -327,6 +336,7 @@ public class RemoteAnimationTarget implements Parcelable { pw.print(prefix); pw.print("allowEnterPip="); pw.println(allowEnterPip); pw.print(prefix); pw.print("windowType="); pw.print(windowType); pw.print(prefix); pw.print("hasAnimatingParent="); pw.print(hasAnimatingParent); + pw.print(prefix); pw.print("backgroundColor="); pw.print(backgroundColor); } public void dumpDebug(ProtoOutputStream proto, long fieldId) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1328e66c1815..c45a4c7f6e8d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2767,17 +2767,16 @@ public final class ViewRootImpl implements ViewParent, dispatchApplyInsets(host); } + if (mFirst) { + // make sure touch mode code executes by setting cached value + // to opposite of the added touch mode. + mAttachInfo.mInTouchMode = !mAddedTouchMode; + ensureTouchModeLocally(mAddedTouchMode); + } + boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw); if (layoutRequested) { - - final Resources res = mView.getContext().getResources(); - - if (mFirst) { - // make sure touch mode code executes by setting cached value - // to opposite of the added touch mode. - mAttachInfo.mInTouchMode = !mAddedTouchMode; - ensureTouchModeLocally(mAddedTouchMode); - } else { + if (!mFirst) { if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { windowSizeMayChange = true; @@ -2797,7 +2796,7 @@ public final class ViewRootImpl implements ViewParent, } // Ask host how big it wants to be - windowSizeMayChange |= measureHierarchy(host, lp, res, + windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(), desiredWindowWidth, desiredWindowHeight); } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 29a9926aeb9f..aae930edb729 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -216,10 +216,14 @@ public final class WindowManagerGlobal { public String[] getViewRootNames() { synchronized (mLock) { final int numRoots = mRoots.size(); - String[] mViewRoots = new String[numRoots]; + final int windowlessRoots = mWindowlessRoots.size(); + String[] mViewRoots = new String[numRoots + windowlessRoots]; for (int i = 0; i < numRoots; ++i) { mViewRoots[i] = getWindowName(mRoots.get(i)); } + for (int i = 0; i < windowlessRoots; ++i) { + mViewRoots[i + numRoots] = getWindowName(mWindowlessRoots.get(i)); + } return mViewRoots; } } @@ -288,6 +292,10 @@ public final class WindowManagerGlobal { final ViewRootImpl root = mRoots.get(i); if (name.equals(getWindowName(root))) return root.getView(); } + for (int i = mWindowlessRoots.size() - 1; i >= 0; --i) { + final ViewRootImpl root = mWindowlessRoots.get(i); + if (name.equals(getWindowName(root))) return root.getView(); + } } return null; diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java index 5007df574ec1..4b9a957f541d 100644 --- a/core/java/android/window/WindowContextController.java +++ b/core/java/android/window/WindowContextController.java @@ -41,14 +41,13 @@ import java.lang.annotation.Retention; * @hide */ public class WindowContextController { - // TODO(220049234): Disable attach debug logging before shipping. - private static final boolean DEBUG_ATTACH = true; + private static final boolean DEBUG_ATTACH = false; private static final String TAG = "WindowContextController"; /** - * {@link AttachStatus.STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a + * {@link AttachStatus#STATUS_ATTACHED} to indicate that the {@code mToken} is associated with a * {@link com.android.server.wm.DisplayArea}. Note that {@code mToken} is able to attach a - * WindowToken after this flag sets to {@link AttachStatus.STATUS_ATTACHED}. + * WindowToken after this flag sets to {@link AttachStatus#STATUS_ATTACHED}. */ @VisibleForTesting public int mAttachedToDisplayArea = AttachStatus.STATUS_INITIALIZED; diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index bea2c7885d57..cad8b9b64d0b 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -51,9 +51,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private IWindowSession mWindowSession; private IWindow mWindow; private static final String TAG = "WindowOnBackDispatcher"; - private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability"; - private static final boolean IS_BACK_PREDICTABILITY_ENABLED = SystemProperties - .getInt(BACK_PREDICTABILITY_PROP, 1) > 0; + private static final boolean ENABLE_PREDICTIVE_BACK = SystemProperties + .getInt("persist.wm.debug.predictive_back", 1) != 0; + private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties + .getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0; /** Convenience hashmap to quickly decide if a callback has been added. */ private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>(); @@ -254,18 +255,18 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { public static boolean isOnBackInvokedCallbackEnabled(@Nullable Context context) { // new back is enabled if the feature flag is enabled AND the app does not explicitly // request legacy back. - boolean featureFlagEnabled = IS_BACK_PREDICTABILITY_ENABLED; + boolean featureFlagEnabled = ENABLE_PREDICTIVE_BACK; // If the context is null, we assume true and fallback on the two other conditions. boolean appRequestsPredictiveBack = context != null && context.getApplicationInfo().isOnBackInvokedCallbackEnabled(); if (DEBUG) { Log.d(TAG, TextUtils.formatSimple("App: %s featureFlagEnabled=%s " - + "appRequestsPredictiveBack=%s", + + "appRequestsPredictiveBack=%s alwaysEnforce=%s", context != null ? context.getApplicationInfo().packageName : "null context", - featureFlagEnabled, appRequestsPredictiveBack)); + featureFlagEnabled, appRequestsPredictiveBack, ALWAYS_ENFORCE_PREDICTIVE_BACK)); } - return featureFlagEnabled && appRequestsPredictiveBack; + return featureFlagEnabled && (appRequestsPredictiveBack || ALWAYS_ENFORCE_PREDICTIVE_BACK); } } diff --git a/core/java/com/android/internal/app/AppLocaleStore.java b/core/java/com/android/internal/app/AppLocaleStore.java index 76e58988eedf..f95838516927 100644 --- a/core/java/com/android/internal/app/AppLocaleStore.java +++ b/core/java/com/android/internal/app/AppLocaleStore.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus; + import android.app.LocaleConfig; import android.content.Context; import android.content.pm.PackageManager; @@ -25,41 +27,43 @@ import android.util.Log; import java.util.ArrayList; import java.util.Locale; -public class AppLocaleStore { +class AppLocaleStore { private static final String TAG = AppLocaleStore.class.getSimpleName(); - public static ArrayList<Locale> getAppSupportedLocales(Context context, String packageName) { + public static AppLocaleResult getAppSupportedLocales( + Context context, String packageName) { + LocaleConfig localeConfig = null; + AppLocaleResult.LocaleStatus localeStatus = LocaleStatus.UNKNOWN_FAILURE; ArrayList<Locale> appSupportedLocales = new ArrayList<>(); - LocaleList packageLocaleList = getPackageLocales(context, packageName); - if (packageLocaleList != null && packageLocaleList.size() > 0) { - for (int i = 0; i < packageLocaleList.size(); i++) { - appSupportedLocales.add(packageLocaleList.get(i)); - } - Log.d(TAG, "getAppSupportedLocales from LocaleConfig. Size: " - + appSupportedLocales.size()); - } else { - String[] languages = getAssetLocales(context, packageName); - for (String language : languages) { - appSupportedLocales.add(Locale.forLanguageTag(language)); - } - Log.d(TAG, "getAppSupportedLocales from asset. Size: " - + appSupportedLocales.size()); + try { + localeConfig = new LocaleConfig(context.createPackageContext(packageName, 0)); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Can not found the package name : " + packageName + " / " + e); } - return appSupportedLocales; - } - private static LocaleList getPackageLocales(Context context, String packageName) { - try { - LocaleConfig localeConfig = - new LocaleConfig(context.createPackageContext(packageName, 0)); + if (localeConfig != null) { if (localeConfig.getStatus() == LocaleConfig.STATUS_SUCCESS) { - return localeConfig.getSupportedLocales(); + LocaleList packageLocaleList = localeConfig.getSupportedLocales(); + if (packageLocaleList.size() > 0) { + localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG; + for (int i = 0; i < packageLocaleList.size(); i++) { + appSupportedLocales.add(packageLocaleList.get(i)); + } + } else { + localeStatus = LocaleStatus.NO_SUPPORTED_LANGUAGE; + } + } else if (localeConfig.getStatus() == LocaleConfig.STATUS_NOT_SPECIFIED) { + localeStatus = LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET; + String[] languages = getAssetLocales(context, packageName); + for (String language : languages) { + appSupportedLocales.add(Locale.forLanguageTag(language)); + } } - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Can not found the package name : " + packageName + " / " + e); } - return null; + Log.d(TAG, "getAppSupportedLocales(). status: " + localeStatus + + ", appSupportedLocales:" + appSupportedLocales.size()); + return new AppLocaleResult(localeStatus, appSupportedLocales); } private static String[] getAssetLocales(Context context, String packageName) { @@ -82,4 +86,20 @@ public class AppLocaleStore { return new String[0]; } + static class AppLocaleResult { + enum LocaleStatus { + UNKNOWN_FAILURE, + NO_SUPPORTED_LANGUAGE, + GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG, + GET_SUPPORTED_LANGUAGE_FROM_ASSET, + } + + LocaleStatus mLocaleStatus; + ArrayList<Locale> mAppSupportedLocales; + + public AppLocaleResult(LocaleStatus localeStatus, ArrayList<Locale> appSupportedLocales) { + this.mLocaleStatus = localeStatus; + this.mAppSupportedLocales = appSupportedLocales; + } + } } diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java index 52c74cf81508..213af26a436f 100644 --- a/core/java/com/android/internal/app/LocalePickerWithRegion.java +++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus; + import android.app.FragmentManager; import android.app.FragmentTransaction; import android.app.ListFragment; @@ -158,30 +160,39 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O if (appCurrentLocale != null && !isForCountryMode) { mLocaleList.add(appCurrentLocale); } - filterTheLanguagesNotSupportedInApp(context, appPackageName); - if (!isForCountryMode) { - mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo()); + AppLocaleStore.AppLocaleResult result = + AppLocaleStore.getAppSupportedLocales(context, appPackageName); + boolean shouldShowList = + result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG + || result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET; + + mLocaleList = filterTheLanguagesNotSupportedInApp( + shouldShowList, result.mAppSupportedLocales); + + // Add "system language" + if (!isForCountryMode && shouldShowList) { + mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo(appCurrentLocale == null)); } } return true; } - private void filterTheLanguagesNotSupportedInApp(Context context, String appPackageName) { - ArrayList<Locale> supportedLocales = - AppLocaleStore.getAppSupportedLocales(context, appPackageName); - + private Set<LocaleStore.LocaleInfo> filterTheLanguagesNotSupportedInApp( + boolean shouldShowList, ArrayList<Locale> supportedLocales) { Set<LocaleStore.LocaleInfo> filteredList = new HashSet<>(); - for(LocaleStore.LocaleInfo li: mLocaleList) { - for(Locale l: supportedLocales) { - if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) { - filteredList.add(li); + if (shouldShowList) { + for(LocaleStore.LocaleInfo li: mLocaleList) { + for(Locale l: supportedLocales) { + if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) { + filteredList.add(li); + } } } + Log.d(TAG, "mLocaleList after app-supported filter: " + filteredList.size()); } - Log.d(TAG, "mLocaleList after app-supported filter: " + filteredList.size()); - mLocaleList = filteredList; + return filteredList; } private void returnToParentFrame() { diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java index cea8eaa3ee7f..eb11b9b8b138 100644 --- a/core/java/com/android/internal/app/LocaleStore.java +++ b/core/java/com/android/internal/app/LocaleStore.java @@ -281,9 +281,12 @@ public class LocaleStore { * The "system default" is special case for per-app picker. Intentionally keep the locale * empty to let activity know "system default" been selected. */ - public static LocaleInfo getSystemDefaultLocaleInfo() { + public static LocaleInfo getSystemDefaultLocaleInfo(boolean hasAppLanguage) { LocaleInfo systemDefaultInfo = new LocaleInfo(""); systemDefaultInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SYSTEM_LANGUAGE; + if (hasAppLanguage) { + systemDefaultInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_CURRENT; + } systemDefaultInfo.mIsTranslated = true; return systemDefaultInfo; } diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java index 2eb104ed215a..68b8968fe399 100644 --- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java +++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java @@ -27,6 +27,7 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; +import android.widget.FrameLayout; import android.widget.TextView; import com.android.internal.R; @@ -54,7 +55,11 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { private static final int TYPE_HEADER_ALL_OTHERS = 1; private static final int TYPE_LOCALE = 2; private static final int TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER = 3; + private static final int TYPE_CURRENT_LOCALE = 4; private static final int MIN_REGIONS_FOR_SUGGESTIONS = 6; + private static final int APP_LANGUAGE_PICKER_TYPE_COUNT = 5; + private static final int SYSTEM_LANGUAGE_TYPE_COUNT = 3; + private static final int SYSTEM_LANGUAGE_WITHOUT_HEADER_TYPE_COUNT = 1; private ArrayList<LocaleStore.LocaleInfo> mLocaleOptions; private ArrayList<LocaleStore.LocaleInfo> mOriginalLocaleOptions; @@ -93,7 +98,8 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { @Override public boolean isEnabled(int position) { return getItemViewType(position) == TYPE_LOCALE - || getItemViewType(position) == TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER; + || getItemViewType(position) == TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER + || getItemViewType(position) == TYPE_CURRENT_LOCALE; } @Override @@ -112,6 +118,9 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { if (item.isSystemLocale()) { return TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER; } + if (item.isAppCurrentLocale()) { + return TYPE_CURRENT_LOCALE; + } return TYPE_LOCALE; } } @@ -119,11 +128,13 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { @Override public int getViewTypeCount() { if (!TextUtils.isEmpty(mAppPackageName) && showHeaders()) { - return 4; // Two headers and 1 for "System language" + // Two headers, 1 "System language", 1 current locale + return APP_LANGUAGE_PICKER_TYPE_COUNT; } else if (showHeaders()) { - return 3; // Two headers in addition to the locales + // Two headers in addition to the locales + return SYSTEM_LANGUAGE_TYPE_COUNT; } else { - return 1; // Locales items only + return SYSTEM_LANGUAGE_WITHOUT_HEADER_TYPE_COUNT; // Locales items only } } @@ -204,11 +215,15 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { textView.setTextLocale( mDisplayLocale != null ? mDisplayLocale : Locale.getDefault()); break; - case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER: if (!(convertView instanceof ViewGroup)) { - convertView = mInflater.inflate( - R.layout.app_language_picker_system_default, parent, false); + if (((LocaleStore.LocaleInfo)getItem(position)).isAppCurrentLocale()) { + convertView = mInflater.inflate( + R.layout.app_language_picker_system_current, parent, false); + } else { + convertView = mInflater.inflate( + R.layout.app_language_picker_system_default, parent, false); + } } Locale defaultLocale = Locale.getDefault(); @@ -219,25 +234,20 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { subtitle.setText(defaultLocale.getDisplayName()); subtitle.setTextLocale(defaultLocale); break; + case TYPE_CURRENT_LOCALE: + if (!(convertView instanceof ViewGroup)) { + convertView = mInflater.inflate( + R.layout.app_language_picker_current_locale_item, parent, false); + } + updateTextView( + convertView, convertView.findViewById(R.id.language_picker_item), position); + break; default: // Covers both null, and "reusing" a wrong kind of view if (!(convertView instanceof ViewGroup)) { convertView = mInflater.inflate(R.layout.language_picker_item, parent, false); } - - TextView text = (TextView) convertView.findViewById(R.id.locale); - LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position); - text.setText(item.getLabel(mCountryMode)); - text.setTextLocale(item.getLocale()); - text.setContentDescription(item.getContentDescription(mCountryMode)); - if (mCountryMode) { - int layoutDir = TextUtils.getLayoutDirectionFromLocale(item.getParent()); - //noinspection ResourceType - convertView.setLayoutDirection(layoutDir); - text.setTextDirection(layoutDir == View.LAYOUT_DIRECTION_RTL - ? View.TEXT_DIRECTION_RTL - : View.TEXT_DIRECTION_LTR); - } + updateTextView(convertView, convertView.findViewById(R.id.locale), position); } return convertView; } @@ -348,4 +358,19 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { public Filter getFilter() { return new FilterByNativeAndUiNames(); } + + private void updateTextView(View convertView, TextView text, int position) { + LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position); + text.setText(item.getLabel(mCountryMode)); + text.setTextLocale(item.getLocale()); + text.setContentDescription(item.getContentDescription(mCountryMode)); + if (mCountryMode) { + int layoutDir = TextUtils.getLayoutDirectionFromLocale(item.getParent()); + //noinspection ResourceType + convertView.setLayoutDirection(layoutDir); + text.setTextDirection(layoutDir == View.LAYOUT_DIRECTION_RTL + ? View.TEXT_DIRECTION_RTL + : View.TEXT_DIRECTION_LTR); + } + } } diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java index 576860d6a50f..b334e9172729 100644 --- a/core/java/com/android/internal/net/VpnProfile.java +++ b/core/java/com/android/internal/net/VpnProfile.java @@ -22,12 +22,17 @@ import android.net.Ikev2VpnProfile; import android.net.PlatformVpnProfile; import android.net.ProxyInfo; import android.net.Uri; +import android.net.ipsec.ike.IkeTunnelConnectionParams; +import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtils; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.os.PersistableBundle; import android.text.TextUtils; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.HexDump; import com.android.net.module.util.ProxyUtils; import java.io.UnsupportedEncodingException; @@ -69,7 +74,8 @@ public final class VpnProfile implements Cloneable, Parcelable { public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; public static final int TYPE_IKEV2_IPSEC_PSK = 7; public static final int TYPE_IKEV2_IPSEC_RSA = 8; - public static final int TYPE_MAX = 8; + public static final int TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS = 9; + public static final int TYPE_MAX = 9; // Match these constants with R.array.vpn_proxy_settings. public static final int PROXY_NONE = 0; @@ -145,25 +151,27 @@ public final class VpnProfile implements Cloneable, Parcelable { public final boolean excludeLocalRoutes; // 25 public final boolean requiresInternetValidation; // 26 + public final IkeTunnelConnectionParams ikeTunConnParams; // 27 // Helper fields. @UnsupportedAppUsage public transient boolean saveLogin = false; public VpnProfile(String key) { - this(key, false, false, false); + this(key, false, false, false, null); } public VpnProfile(String key, boolean isRestrictedToTestNetworks) { - this(key, isRestrictedToTestNetworks, false, false); + this(key, isRestrictedToTestNetworks, false, false, null); } public VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes, - boolean requiresInternetValidation) { + boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams) { this.key = key; this.isRestrictedToTestNetworks = isRestrictedToTestNetworks; this.excludeLocalRoutes = excludeLocalRoutes; this.requiresInternetValidation = requiresInternetValidation; + this.ikeTunConnParams = ikeTunConnParams; } @UnsupportedAppUsage @@ -195,6 +203,10 @@ public final class VpnProfile implements Cloneable, Parcelable { isRestrictedToTestNetworks = in.readBoolean(); excludeLocalRoutes = in.readBoolean(); requiresInternetValidation = in.readBoolean(); + final PersistableBundle bundle = + in.readParcelable(PersistableBundle.class.getClassLoader()); + ikeTunConnParams = (bundle == null) ? null + : TunnelConnectionParamsUtils.fromPersistableBundle(bundle); } /** @@ -244,6 +256,8 @@ public final class VpnProfile implements Cloneable, Parcelable { out.writeBoolean(isRestrictedToTestNetworks); out.writeBoolean(excludeLocalRoutes); out.writeBoolean(requiresInternetValidation); + out.writeParcelable(ikeTunConnParams == null ? null + : TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams), flags); } /** @@ -259,15 +273,17 @@ public final class VpnProfile implements Cloneable, Parcelable { } String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1); + // Acceptable numbers of values are: // 14-19: Standard profile, with option for serverCert, proxy // 24: Standard profile with serverCert, proxy and platform-VPN parameters // 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks // 26: ...and excludeLocalRoutes - // (26 can only be found on dogfood devices) // 27: ...and requiresInternetValidation + // (26,27 can only be found on dogfood devices) + // 28: ...and ikeTunConnParams if ((values.length < 14 || (values.length > 19 && values.length < 24) - || values.length > 27)) { + || values.length > 28)) { return null; } @@ -292,8 +308,22 @@ public final class VpnProfile implements Cloneable, Parcelable { requiresInternetValidation = false; } + final IkeTunnelConnectionParams tempIkeTunConnParams; + // Assign null directly if the ikeTunConParams field is empty. + if (values.length >= 28 && values[27].length() != 0) { + final Parcel parcel = Parcel.obtain(); + final byte[] bytes = HexDump.hexStringToByteArray(values[27]); + parcel.unmarshall(bytes, 0, bytes.length); + parcel.setDataPosition(0); + final PersistableBundle bundle = (PersistableBundle) parcel.readValue( + PersistableBundle.class.getClassLoader()); + tempIkeTunConnParams = TunnelConnectionParamsUtils.fromPersistableBundle(bundle); + } else { + tempIkeTunConnParams = null; + } + VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks, - excludeLocalRoutes, requiresInternetValidation); + excludeLocalRoutes, requiresInternetValidation, tempIkeTunConnParams); profile.name = values[0]; profile.type = Integer.parseInt(values[1]); if (profile.type < 0 || profile.type > TYPE_MAX) { @@ -345,6 +375,7 @@ public final class VpnProfile implements Cloneable, Parcelable { profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty(); return profile; } catch (Exception e) { + Log.d(TAG, "Got exception in decode.", e); // ignore } return null; @@ -406,6 +437,17 @@ public final class VpnProfile implements Cloneable, Parcelable { builder.append(VALUE_DELIMITER).append(excludeLocalRoutes); builder.append(VALUE_DELIMITER).append(requiresInternetValidation); + if (ikeTunConnParams != null) { + final PersistableBundle bundle = + TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams); + final Parcel parcel = Parcel.obtain(); + parcel.writeValue(bundle); + final byte[] bytes = parcel.marshall(); + builder.append(VALUE_DELIMITER).append(HexDump.toHexString(bytes)); + } else { + builder.append(VALUE_DELIMITER).append(""); + } + return builder.toString().getBytes(StandardCharsets.UTF_8); } @@ -486,7 +528,8 @@ public final class VpnProfile implements Cloneable, Parcelable { key, type, server, username, password, dnsServers, searchDomains, routes, mppe, l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert, proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline, - isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation); + isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation, + ikeTunConnParams); } /** Checks VPN profiles for interior equality. */ @@ -521,7 +564,8 @@ public final class VpnProfile implements Cloneable, Parcelable { && areAuthParamsInline == other.areAuthParamsInline && isRestrictedToTestNetworks == other.isRestrictedToTestNetworks && excludeLocalRoutes == other.excludeLocalRoutes - && requiresInternetValidation == other.requiresInternetValidation; + && requiresInternetValidation == other.requiresInternetValidation + && Objects.equals(ikeTunConnParams, other.ikeTunConnParams); } @NonNull diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index c825f770c0c3..06d12b5195ab 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -59,7 +59,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -1487,7 +1486,7 @@ public class SystemConfig { addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0); } - if (isFilesystemSupported("erofs")) { + if (isErofsSupported()) { if (isKernelVersionAtLeast(5, 10)) { addFeature(PackageManager.FEATURE_EROFS, 0); } else if (isKernelVersionAtLeast(4, 19)) { @@ -1865,11 +1864,10 @@ public class SystemConfig { return Process.myUid() == Process.SYSTEM_UID; } - private static boolean isFilesystemSupported(String fs) { + private static boolean isErofsSupported() { try { - final byte[] fsTableData = Files.readAllBytes(Paths.get("/proc/filesystems")); - final String fsTable = new String(fsTableData, StandardCharsets.UTF_8); - return fsTable.contains("\t" + fs + "\n"); + final Path path = Paths.get("/sys/fs/erofs"); + return Files.exists(path); } catch (Exception e) { return false; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 96fe7e1aa541..4075c5f4d8ae 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2086,7 +2086,7 @@ <!-- @SystemApi @hide Allows applications to register network factory or agent --> <permission android:name="android.permission.NETWORK_FACTORY" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- @SystemApi @hide Allows applications to access network stats provider --> <permission android:name="android.permission.NETWORK_STATS_PROVIDER" @@ -2275,13 +2275,13 @@ @hide --> <permission android:name="android.permission.BLUETOOTH_MAP" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows bluetooth stack to access files @hide This should only be used by Bluetooth apk. --> <permission android:name="android.permission.BLUETOOTH_STACK" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows uhid write access for creating virtual input devices @hide @@ -2552,7 +2552,7 @@ <!-- Allows access to configure network interfaces, configure/use IPSec, etc. @hide --> <permission android:name="android.permission.NET_ADMIN" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows registration for remote audio playback. @hide --> <permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK" @@ -2676,7 +2676,7 @@ <!-- Allows listen permission to always reported system signal strength. @hide Used internally. --> <permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- @SystemApi Protects the ability to register any PhoneAccount with PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount @@ -3911,7 +3911,7 @@ Not for use by third party apps. @hide --> <permission android:name="android.permission.MANAGE_APP_OPS_MODES" - android:protectionLevel="signature|installer|verifier" /> + android:protectionLevel="signature|installer|verifier|role" /> <!-- @SystemApi Allows an application to open windows that are for use by parts of the system user interface. @@ -4792,7 +4792,7 @@ <!-- Allows an application to manage the companion devices. @hide --> <permission android:name="android.permission.MANAGE_COMPANION_DEVICES" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows an application to subscribe to notifications about the presence status change of their associated companion device @@ -5041,7 +5041,7 @@ <!-- @TestApi Allows an application to query audio related state. @hide --> <permission android:name="android.permission.QUERY_AUDIO_STATE" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows an application to modify what effects are applied to all audio (matching certain criteria) from any application. @@ -5114,7 +5114,7 @@ @hide --> <permission android:name="android.permission.DEVICE_POWER" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Allows toggling battery saver on the system. Superseded by DEVICE_POWER permission. @hide @SystemApi @@ -5140,7 +5140,7 @@ <!-- @hide Allows low-level access to tun tap driver --> <permission android:name="android.permission.NET_TUNNELING" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- Run as a manufacturer test application, running as the root user. Only available when the device is running in manufacturer test mode. diff --git a/core/res/res/drawable/ic_check_24dp.xml b/core/res/res/drawable/ic_check_24dp.xml new file mode 100644 index 000000000000..a0e21ff2826e --- /dev/null +++ b/core/res/res/drawable/ic_check_24dp.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2022 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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" + android:fillColor="@android:color/white"/> +</vector> diff --git a/core/res/res/layout/app_language_picker_current_locale_item.xml b/core/res/res/layout/app_language_picker_current_locale_item.xml new file mode 100644 index 000000000000..bf6d9639791a --- /dev/null +++ b/core/res/res/layout/app_language_picker_current_locale_item.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight=".8"> + <include + android:id="@+id/language_picker_item" + layout="@layout/language_picker_item" /> + </FrameLayout> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight=".2" + android:gravity="center" + android:minHeight="?android:attr/listPreferredItemHeight"> + <ImageView + android:id="@+id/imageView" + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/ic_check_24dp" + app:tint="#0F9D58"/> + </LinearLayout> +</LinearLayout> diff --git a/core/res/res/layout/app_language_picker_system_current.xml b/core/res/res/layout/app_language_picker_system_current.xml new file mode 100644 index 000000000000..341ee2528671 --- /dev/null +++ b/core/res/res/layout/app_language_picker_system_current.xml @@ -0,0 +1,45 @@ +<!-- + ~ Copyright (C) 2022 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight=".8"> + <include + android:id="@+id/system_language_view" + layout="@layout/app_language_picker_system_default" /> + </FrameLayout> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight=".2" + android:gravity="center" + android:minHeight="?android:attr/listPreferredItemHeight"> + <ImageView + android:id="@+id/imageView" + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/ic_check_24dp" + app:tint="#0F9D58"/> + </LinearLayout> +</LinearLayout> diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 783fabe20a6d..f5c82d5019ad 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -35,4 +35,9 @@ <color name="personal_apps_suspension_notification_color">#8AB4F8</color> <color name="overview_background">@color/overview_background_dark</color> + + <color name="user_icon_4">#fff439a0</color><!-- pink --> + <color name="user_icon_6">#ff4ecde6</color><!-- cyan --> + <color name="user_icon_7">#fffbbc04</color><!-- yellow --> + <color name="user_icon_8">#fffa903e</color><!-- orange --> </resources> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 54325e590347..71c98d002984 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -174,14 +174,14 @@ <color name="system_notification_accent_color">#00000000</color> <!-- Default user icon colors --> - <color name="user_icon_1">#ff00bcd4</color><!-- cyan 500 --> - <color name="user_icon_2">#ff3f51b5</color><!-- indigo 500 --> - <color name="user_icon_3">#ff4285f4</color><!-- blue 500 --> - <color name="user_icon_4">#ffe91e63</color><!-- pink 500 --> - <color name="user_icon_5">#ff0f9d58</color><!-- green 500 --> - <color name="user_icon_6">#ff8bc34a</color><!-- light green 500 --> - <color name="user_icon_7">#ffff9800</color><!-- orange 500 --> - <color name="user_icon_8">#ffff5722</color><!-- deep orange 500 --> + <color name="user_icon_1">#ffe46962</color><!-- red --> + <color name="user_icon_2">#ffaf5cf7</color><!-- purple --> + <color name="user_icon_3">#ff4c8df6</color><!-- blue --> + <color name="user_icon_4">#fff439a0</color><!-- pink --> + <color name="user_icon_5">#ff1ea446</color><!-- green --> + <color name="user_icon_6">#ff129eaf</color><!-- cyan --> + <color name="user_icon_7">#ffb26c00</color><!-- yellow --> + <color name="user_icon_8">#ffe8710a</color><!-- orange --> <color name="user_icon_default_gray">#ff9e9e9e</color><!-- gray 500 --> <color name="user_icon_default_white">#ffffffff</color><!-- white --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 60d875c2ca39..42f789bb59fc 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2918,6 +2918,11 @@ com.android.settings.intelligence </string> + <!-- System bluetooth stack package name --> + <string name="config_systemBluetoothStack" translatable="false"> + com.android.bluetooth.services + </string> + <!-- Flag indicating that the media framework should not allow changes or mute on any stream or global volumes. --> <bool name="config_useFixedVolume">false</bool> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index 870f54989674..86bad7f12258 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -184,6 +184,8 @@ <public name="safety_protection_display_text" /> <!-- @hide @SystemApi --> <public name="config_systemSettingsIntelligence" /> + <!-- @hide --> + <public name="config_systemBluetoothStack" /> </staging-public-group> <staging-public-group type="dimen" first-id="0x01db0000"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index dd69fa0bebb2..776d3dafe8c0 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4754,7 +4754,11 @@ <java-symbol type="drawable" name="ic_swap_horiz" /> <java-symbol type="bool" name="config_notificationForceUserSetOnUpgrade" /> + <!-- For app language picker --> <java-symbol type="string" name="system_locale_title" /> <java-symbol type="layout" name="app_language_picker_system_default" /> + <java-symbol type="layout" name="app_language_picker_system_current" /> + <java-symbol type="layout" name="app_language_picker_current_locale_item" /> <java-symbol type="id" name="system_locale_subtitle" /> + <java-symbol type="id" name="language_picker_item" /> </resources> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 58a2073981bf..04ead1bf1e9c 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -33,6 +33,30 @@ applications that come with the platform <permission name="android.permission.WRITE_SECURE_SETTINGS"/> </privapp-permissions> + <privapp-permissions package="com.android.bluetooth.services"> + <permission name="android.permission.DUMP"/> + <permission name="android.permission.MODIFY_AUDIO_ROUTING"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permission name="android.permission.TETHER_PRIVILEGED"/> + <permission name="android.permission.CALL_PRIVILEGED"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/> + <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/> + <permission name="android.permission.UPDATE_DEVICE_STATS"/> + <permission name="android.permission.PACKAGE_USAGE_STATS"/> + <permission name="android.permission.NFC_HANDOVER_STATUS"/> + <permission name="android.permission.CONNECTIVITY_INTERNAL"/> + <permission name="android.permission.BLUETOOTH_PRIVILEGED"/> + <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> + <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + <permission name="android.permission.REAL_GET_TASKS"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/> + <permission name="android.permission.WRITE_APN_SETTINGS"/> + <permission name="android.permission.UPDATE_APP_OPS_STATS"/> + </privapp-permissions> + <privapp-permissions package="com.android.backupconfirm"> <permission name="android.permission.BACKUP"/> <permission name="android.permission.CRYPT_KEEPER"/> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index e528df8c89b4..3f0b01bef0ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -131,7 +131,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou mDisplayController.getDisplayContext(mRootTaskInfo.displayId), mRootTaskInfo.configuration, this /* layoutChangeListener */, mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(), - true /* applyDismissingParallax */); + SplitLayout.PARALLAX_DISMISSING); mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout); final WindowContainerToken token1 = task1.token; @@ -327,13 +327,15 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou @Override public void onLayoutPositionChanging(SplitLayout layout) { mSyncQueue.runInSync(t -> - layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2)); + layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2, + true /* applyResizingOffset */)); } @Override public void onLayoutSizeChanging(SplitLayout layout) { mSyncQueue.runInSync(t -> - layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2)); + layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2, + true /* applyResizingOffset */)); } @Override @@ -342,7 +344,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> - layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2)); + layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2, + false /* applyResizingOffset */)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 577ced53f992..42ac19509693 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -48,18 +48,16 @@ import com.android.wm.shell.common.annotations.ShellMainThread; * Controls the window animation run when a user initiates a back gesture. */ public class BackAnimationController implements RemoteCallable<BackAnimationController> { - - private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP = - "persist.debug.back_predictability_progress_threshold"; - // By default, enable new back dispatching without any animations. - private static final int BACK_PREDICTABILITY_PROP = - SystemProperties.getInt("persist.debug.back_predictability", 1); - public static final boolean IS_ENABLED = BACK_PREDICTABILITY_PROP > 0; - private static final int PROGRESS_THRESHOLD = SystemProperties - .getInt(BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP, -1); private static final String TAG = "BackAnimationController"; + private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP = + "persist.wm.debug.predictive_back_progress_threshold"; + public static final boolean IS_ENABLED = + SystemProperties.getInt("persist.wm.debug.predictive_back", 1) != 0; + private static final int PROGRESS_THRESHOLD = SystemProperties + .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1); @VisibleForTesting - boolean mEnableAnimations = (BACK_PREDICTABILITY_PROP & (1 << 1)) != 0; + boolean mEnableAnimations = SystemProperties.getInt( + "persist.wm.debug.predictive_back_anim", 0) != 0; /** * Location of the initial touch event of the back gesture. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 116d3524e711..ec81d230fae7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -72,6 +72,10 @@ import java.io.PrintWriter; */ public final class SplitLayout implements DisplayInsetsController.OnInsetsChangedListener { + public static final int PARALLAX_NONE = 0; + public static final int PARALLAX_DISMISSING = 1; + public static final int PARALLAX_ALIGN_CENTER = 2; + private final int mDividerWindowWidth; private final int mDividerInsets; private final int mDividerSize; @@ -87,7 +91,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private final SplitWindowManager mSplitWindowManager; private final DisplayImeController mDisplayImeController; private final ImePositionProcessor mImePositionProcessor; - private final DismissingEffectPolicy mDismissingEffectPolicy; + private final ResizingEffectPolicy mSurfaceEffectPolicy; private final ShellTaskOrganizer mTaskOrganizer; private final InsetsState mInsetsState = new InsetsState(); @@ -105,7 +109,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange SplitLayoutHandler splitLayoutHandler, SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks, DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer, - boolean applyDismissingParallax) { + int parallaxType) { mContext = context.createConfigurationContext(configuration); mOrientation = configuration.orientation; mRotation = configuration.windowConfiguration.getRotation(); @@ -115,7 +119,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange parentContainerCallbacks); mTaskOrganizer = taskOrganizer; mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId()); - mDismissingEffectPolicy = new DismissingEffectPolicy(applyDismissingParallax); + mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType); final Resources resources = context.getResources(); mDividerSize = resources.getDimensionPixelSize(R.dimen.split_divider_bar_width); @@ -281,7 +285,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */); DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */); - mDismissingEffectPolicy.applyDividerPosition(position, isLandscape); + mSurfaceEffectPolicy.applyDividerPosition(position, isLandscape); } /** Inflates {@link DividerView} on the root surface. */ @@ -486,7 +490,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */ public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1, - SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) { + SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2, + boolean applyResizingOffset) { final SurfaceControl dividerLeash = getDividerLeash(); if (dividerLeash != null) { mTempRect.set(getRefDividerBounds()); @@ -506,7 +511,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return; } - mDismissingEffectPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2); + mSurfaceEffectPolicy.adjustDimSurface(t, dimLayer1, dimLayer2); + if (applyResizingOffset) { + mSurfaceEffectPolicy.adjustRootSurface(t, leash1, leash2); + } } /** Apply recorded task layout to the {@link WindowContainerTransaction}. */ @@ -590,7 +598,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * Calls when resizing the split bounds. * * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl, - * SurfaceControl, SurfaceControl) + * SurfaceControl, SurfaceControl, boolean) */ void onLayoutSizeChanging(SplitLayout layout); @@ -600,7 +608,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * @see #applyTaskChanges(WindowContainerTransaction, ActivityManager.RunningTaskInfo, * ActivityManager.RunningTaskInfo) * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl, - * SurfaceControl, SurfaceControl) + * SurfaceControl, SurfaceControl, boolean) */ void onLayoutSizeChanged(SplitLayout layout); @@ -609,7 +617,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * panel. * * @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl, - * SurfaceControl, SurfaceControl) + * SurfaceControl, SurfaceControl, boolean) */ void onLayoutPositionChanging(SplitLayout layout); @@ -637,21 +645,25 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * Calculates and applies proper dismissing parallax offset and dimming value to hint users * dismissing gesture. */ - private class DismissingEffectPolicy { + private class ResizingEffectPolicy { /** Indicates whether to offset splitting bounds to hint dismissing progress or not. */ - private final boolean mApplyParallax; + private final int mParallaxType; + + int mShrinkSide = DOCKED_INVALID; // The current dismissing side. int mDismissingSide = DOCKED_INVALID; // The parallax offset to hint the dismissing side and progress. - final Point mDismissingParallaxOffset = new Point(); + final Point mParallaxOffset = new Point(); // The dimming value to hint the dismissing side and progress. float mDismissingDimValue = 0.0f; + final Rect mContentBounds = new Rect(); + final Rect mSurfaceBounds = new Rect(); - DismissingEffectPolicy(boolean applyDismissingParallax) { - mApplyParallax = applyDismissingParallax; + ResizingEffectPolicy(int parallaxType) { + mParallaxType = parallaxType; } /** @@ -662,7 +674,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange */ void applyDividerPosition(int position, boolean isLandscape) { mDismissingSide = DOCKED_INVALID; - mDismissingParallaxOffset.set(0, 0); + mParallaxOffset.set(0, 0); mDismissingDimValue = 0; int totalDismissingDistance = 0; @@ -676,15 +688,39 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange - mDividerSnapAlgorithm.getDismissEndTarget().position; } + final boolean topLeftShrink = isLandscape + ? position < mWinBounds1.right : position < mWinBounds1.bottom; + if (topLeftShrink) { + mShrinkSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP; + mContentBounds.set(mWinBounds1); + mSurfaceBounds.set(mBounds1); + } else { + mShrinkSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM; + mContentBounds.set(mWinBounds2); + mSurfaceBounds.set(mBounds2); + } + if (mDismissingSide != DOCKED_INVALID) { float fraction = Math.max(0, Math.min(mDividerSnapAlgorithm.calculateDismissingFraction(position), 1f)); mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction); - fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide); + if (mParallaxType == PARALLAX_DISMISSING) { + fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide); + if (isLandscape) { + mParallaxOffset.x = (int) (fraction * totalDismissingDistance); + } else { + mParallaxOffset.y = (int) (fraction * totalDismissingDistance); + } + } + } + + if (mParallaxType == PARALLAX_ALIGN_CENTER) { if (isLandscape) { - mDismissingParallaxOffset.x = (int) (fraction * totalDismissingDistance); + mParallaxOffset.x = + (mSurfaceBounds.width() - mContentBounds.width()) / 2; } else { - mDismissingParallaxOffset.y = (int) (fraction * totalDismissingDistance); + mParallaxOffset.y = + (mSurfaceBounds.height() - mContentBounds.height()) / 2; } } } @@ -704,41 +740,66 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } /** Applies parallax offset and dimming value to the root surface at the dismissing side. */ - boolean adjustDismissingSurface(SurfaceControl.Transaction t, - SurfaceControl leash1, SurfaceControl leash2, + void adjustRootSurface(SurfaceControl.Transaction t, + SurfaceControl leash1, SurfaceControl leash2) { + SurfaceControl targetLeash = null; + + if (mParallaxType == PARALLAX_DISMISSING) { + switch (mDismissingSide) { + case DOCKED_TOP: + case DOCKED_LEFT: + targetLeash = leash1; + mTempRect.set(mBounds1); + break; + case DOCKED_BOTTOM: + case DOCKED_RIGHT: + targetLeash = leash2; + mTempRect.set(mBounds2); + break; + } + } else if (mParallaxType == PARALLAX_ALIGN_CENTER) { + switch (mShrinkSide) { + case DOCKED_TOP: + case DOCKED_LEFT: + targetLeash = leash1; + mTempRect.set(mBounds1); + break; + case DOCKED_BOTTOM: + case DOCKED_RIGHT: + targetLeash = leash2; + mTempRect.set(mBounds2); + break; + } + } + if (mParallaxType != PARALLAX_NONE && targetLeash != null) { + t.setPosition(targetLeash, + mTempRect.left + mParallaxOffset.x, mTempRect.top + mParallaxOffset.y); + // Transform the screen-based split bounds to surface-based crop bounds. + mTempRect.offsetTo(-mParallaxOffset.x, -mParallaxOffset.y); + t.setWindowCrop(targetLeash, mTempRect); + } + } + + void adjustDimSurface(SurfaceControl.Transaction t, SurfaceControl dimLayer1, SurfaceControl dimLayer2) { - SurfaceControl targetLeash, targetDimLayer; + SurfaceControl targetDimLayer; switch (mDismissingSide) { case DOCKED_TOP: case DOCKED_LEFT: - targetLeash = leash1; targetDimLayer = dimLayer1; - mTempRect.set(mBounds1); break; case DOCKED_BOTTOM: case DOCKED_RIGHT: - targetLeash = leash2; targetDimLayer = dimLayer2; - mTempRect.set(mBounds2); break; case DOCKED_INVALID: default: t.setAlpha(dimLayer1, 0).hide(dimLayer1); t.setAlpha(dimLayer2, 0).hide(dimLayer2); - return false; - } - - if (mApplyParallax) { - t.setPosition(targetLeash, - mTempRect.left + mDismissingParallaxOffset.x, - mTempRect.top + mDismissingParallaxOffset.y); - // Transform the screen-based split bounds to surface-based crop bounds. - mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y); - t.setWindowCrop(targetLeash, mTempRect); + return; } t.setAlpha(targetDimLayer, mDismissingDimValue) .setVisibility(targetDimLayer, mDismissingDimValue > 0.001f); - return true; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index f20870ff0b2d..aec51baa4af7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -377,7 +377,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return; } - mStageCoordinator.updateSurfaceBounds(null /* layout */, t); + mStageCoordinator.updateSurfaceBounds(null /* layout */, t, + false /* applyResizingOffset */); for (int i = 0; i < apps.length; ++i) { if (apps[i].mode == MODE_OPENING) { t.show(apps[i].leash); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index e150cf9cd112..45931de5e35e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -30,6 +30,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; +import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -495,7 +496,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Using legacy transitions, so we can't use blast sync since it conflicts. mTaskOrganizer.applyTransaction(wct); - mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t)); + mSyncQueue.runInSync(t -> + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */)); } /** @@ -704,9 +706,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainStage.deactivate(wct, !fromEnteringPip && mMainStage == childrenToTop); wct.reorder(mRootTaskInfo.token, false /* onTop */); mTaskOrganizer.applyTransaction(wct); - mSyncQueue.runInSync(t -> t - .setWindowCrop(mMainStage.mRootLeash, null) - .setWindowCrop(mSideStage.mRootLeash, null)); + mSyncQueue.runInSync(t -> { + setResizingSplits(false /* resizing */); + t.setWindowCrop(mMainStage.mRootLeash, null) + .setWindowCrop(mSideStage.mRootLeash, null); + }); // Hide divider and reset its position. mSplitLayout.resetDividerPosition(); @@ -780,7 +784,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, void finishEnterSplitScreen(SurfaceControl.Transaction t) { mSplitLayout.init(); setDividerVisibility(true, t); - updateSurfaceBounds(mSplitLayout, t); + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); setSplitsVisible(true); mShouldUpdateRecents = true; updateRecentTasksSplitPair(); @@ -925,7 +929,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitLayout == null) { mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mRootTaskInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */); + mDisplayImeController, mTaskOrganizer, + PARALLAX_ALIGN_CENTER /* parallaxType */); mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); } @@ -1075,7 +1080,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareEnterSplitScreen(wct); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { - updateSurfaceBounds(mSplitLayout, t); + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); setDividerVisibility(true, t); }); } @@ -1094,8 +1099,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onSnappedToDismiss(boolean bottomOrRight) { - setResizingSplits(false /* resizing */); - final boolean mainStageToTop = bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT; @@ -1104,6 +1107,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } + setResizingSplits(false /* resizing */); final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(dismissTop, wct); @@ -1121,14 +1125,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onLayoutPositionChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */)); } @Override public void onLayoutSizeChanging(SplitLayout layout) { mSyncQueue.runInSync(t -> { setResizingSplits(true /* resizing */); - updateSurfaceBounds(layout, t); + updateSurfaceBounds(layout, t, true /* applyResizingOffset */); mMainStage.onResizing(getMainStageBounds(), t); mSideStage.onResizing(getSideStageBounds(), t); }); @@ -1142,7 +1146,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { setResizingSplits(false /* resizing */); - updateSurfaceBounds(layout, t); + updateSurfaceBounds(layout, t, false /* applyResizingOffset */); mMainStage.onResized(t); mSideStage.onResized(t); }); @@ -1174,13 +1178,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); } - void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) { + void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t, + boolean applyResizingOffset) { final StageTaskListener topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash, - bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer); + bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer, + applyResizingOffset); } void setResizingSplits(boolean resizing) { @@ -1220,7 +1226,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTaskOrganizer.applyTransaction(wct); } - @Override public void onDisplayAdded(int displayId) { if (displayId != DEFAULT_DISPLAY) { return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java index f1520edf53b1..07174051a344 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java @@ -253,7 +253,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t) { - mStageCoordinator.updateSurfaceBounds(null /* layout */, t); + mStageCoordinator.updateSurfaceBounds(null /* layout */, t, + false /* applyResizingOffset */); if (apps != null) { for (int i = 0; i < apps.length; ++i) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java index 6ef20e37d5bc..ac25c7510931 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java @@ -265,7 +265,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainStage.activate(getMainStageBounds(), wct); mSideStage.addTask(task, getSideStageBounds(), wct); mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> updateSurfaceBounds(null /* layout */, t)); + mSyncQueue.runInSync( + t -> updateSurfaceBounds(null /* layout */, t, false /* applyResizingOffset */)); return true; } @@ -801,12 +802,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onLayoutPositionChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */)); } @Override public void onLayoutSizeChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */)); mSideStage.setOutlineVisibility(false); } @@ -816,7 +817,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, updateWindowBounds(layout, wct); updateUnfoldBounds(); mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */)); mSideStage.setOutlineVisibility(true); mLogger.logResize(mSplitLayout.getDividerPositionAsFraction()); } @@ -840,13 +841,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); } - void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) { + void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t, + boolean applyResizingOffset) { final StageTaskListener topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash, - bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer); + bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer, + applyResizingOffset); } @Override @@ -882,7 +885,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitLayout == null) { mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mDisplayAreaInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer, true /* applyDismissingParallax */); + mDisplayImeController, mTaskOrganizer, SplitLayout.PARALLAX_DISMISSING); mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); if (mMainUnfoldController != null && mSideUnfoldController != null) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index 8b4e1f8bfdb7..f1e602fcf778 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -73,7 +73,7 @@ public class SplitLayoutTests extends ShellTestCase { mCallbacks, mDisplayImeController, mTaskOrganizer, - false /* applyDismissingParallax */)); + SplitLayout.PARALLAX_NONE)); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index 061136c65daf..c571d44d3da9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -309,7 +309,7 @@ public class StageCoordinatorTests extends ShellTestCase { public void testFinishEnterSplitScreen_applySurfaceLayout() { mStageCoordinator.finishEnterSplitScreen(new SurfaceControl.Transaction()); - verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any()); + verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any(), eq(false)); } private class UnfoldControllerProvider implements diff --git a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml index fc3ec4344712..fd45a16f24ba 100644 --- a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml @@ -17,7 +17,8 @@ <resources> <style name="SettingsSpinnerTitleBar"> - <item name="android:textAppearance">?android:attr/textAppearanceButton</item> + <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item> + <item name="android:textSize">16sp</item> <item name="android:textColor">@color/settingslib_spinner_title_color</item> <item name="android:maxLines">1</item> <item name="android:ellipsize">marquee</item> @@ -29,7 +30,8 @@ </style> <style name="SettingsSpinnerDropdown"> - <item name="android:textAppearance">?android:attr/textAppearanceButton</item> + <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item> + <item name="android:textSize">16sp</item> <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> <item name="android:maxLines">1</item> <item name="android:ellipsize">marquee</item> diff --git a/packages/SettingsLib/res/layout/user_preference.xml b/packages/SettingsLib/res/layout/user_preference.xml deleted file mode 100644 index f13447a4737c..000000000000 --- a/packages/SettingsLib/res/layout/user_preference.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 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. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@android:id/widget_frame" - android:layout_width="match_parent" - android:layout_height="@dimen/user_spinner_item_height" - android:paddingStart="@dimen/user_spinner_padding_sides" - android:paddingEnd="@dimen/user_spinner_padding_sides" - android:orientation="horizontal" > - - <ImageView - android:id="@android:id/icon" - android:layout_width="@dimen/user_icon_view_height" - android:layout_height="@dimen/user_icon_view_height" - android:layout_gravity="center" - android:scaleType="fitCenter" /> - - <TextView - android:id="@android:id/title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:layout_gravity="center" - android:labelFor="@android:id/icon" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - android:paddingStart="@dimen/user_spinner_padding" - android:paddingEnd="@dimen/user_spinner_padding" - android:textAppearance="?android:attr/textAppearanceMedium" /> - -</LinearLayout> diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 74b759fcdcf9..f934b1f3ab99 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -432,7 +432,8 @@ class ActivityLaunchAnimator( right = windowBounds.right ) val callback = this@ActivityLaunchAnimator.callback!! - val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo) + val windowBackgroundColor = window.taskInfo?.let { callback.getBackgroundColor(it) } + ?: window.backgroundColor // Make sure we use the modified timings when animating a dialog into an app. val launchAnimator = if (controller.isDialogLaunch) { diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt index 2e9a16fffe9a..47c11010c072 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt @@ -138,7 +138,7 @@ class RemoteTransitionAdapter { info: TransitionInfo, t: SurfaceControl.Transaction ): RemoteAnimationTarget { - return RemoteAnimationTarget( + val target = RemoteAnimationTarget( /* taskId */ if (change.taskInfo != null) change.taskInfo!!.taskId else -1, /* mode */ newModeToLegacyMode(change.mode), /* leash */ createLeash(info, change, order, t), @@ -160,6 +160,8 @@ class RemoteTransitionAdapter { /* taskInfo */ change.taskInfo, /* allowEnterPip */ change.allowEnterPip, /* windowType */ WindowManager.LayoutParams.INVALID_WINDOW_TYPE) + target.backgroundColor = change.backgroundColor + return target } /** diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index 120b09a2ad31..de35514be2cb 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -41,6 +41,7 @@ import android.os.Parcelable; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; +import android.util.SparseArray; import android.view.IRecentsAnimationController; import android.view.SurfaceControl; import android.window.IRemoteTransition; @@ -244,22 +245,28 @@ public class RemoteTransitionCompat implements Parcelable { @SuppressLint("NewApi") boolean merge(TransitionInfo info, SurfaceControl.Transaction t, RecentsAnimationListener recents) { - ArrayList<TransitionInfo.Change> openingTasks = null; + SparseArray<TransitionInfo.Change> openingTasks = null; boolean cancelRecents = false; boolean homeGoingAway = false; boolean hasChangingApp = false; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) { - if (change.getTaskInfo() != null) { - if (change.getTaskInfo().topActivityType == ACTIVITY_TYPE_HOME) { + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); + if (taskInfo != null) { + if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { // canceling recents animation cancelRecents = true; } if (openingTasks == null) { - openingTasks = new ArrayList<>(); + openingTasks = new SparseArray<>(); + } + if (taskInfo.hasParentTask()) { + // Collects opening leaf tasks only since Launcher monitors leaf task + // ids to perform recents animation. + openingTasks.remove(taskInfo.parentTaskId); } - openingTasks.add(change); + openingTasks.put(taskInfo.taskId, change); } } else if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) { @@ -287,7 +294,7 @@ public class RemoteTransitionCompat implements Parcelable { int pauseMatches = 0; if (!cancelRecents) { for (int i = 0; i < openingTasks.size(); ++i) { - if (mPausingTasks.contains(openingTasks.get(i).getContainer())) { + if (mPausingTasks.contains(openingTasks.valueAt(i).getContainer())) { ++pauseMatches; } } @@ -308,10 +315,11 @@ public class RemoteTransitionCompat implements Parcelable { final RemoteAnimationTargetCompat[] targets = new RemoteAnimationTargetCompat[openingTasks.size()]; for (int i = 0; i < openingTasks.size(); ++i) { - mOpeningLeashes.add(openingTasks.get(i).getLeash()); + final TransitionInfo.Change change = openingTasks.valueAt(i); + mOpeningLeashes.add(change.getLeash()); // We are receiving new opening tasks, so convert to onTasksAppeared. final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat( - openingTasks.get(i), layer, info, t); + change, layer, info, t); mLeashMap.put(mOpeningLeashes.get(i), target.leash); t.reparent(target.leash, mInfo.getRootLeash()); t.setLayer(target.leash, layer); diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt index ad8c126aa2fa..19d39d515325 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt @@ -22,6 +22,7 @@ import android.annotation.IntRange import android.annotation.SuppressLint import android.content.Context import android.graphics.Canvas +import android.text.TextUtils import android.text.format.DateFormat import android.util.AttributeSet import android.util.Log @@ -125,13 +126,30 @@ class AnimatableClockView @JvmOverloads constructor( fun refreshTime() { time.timeInMillis = System.currentTimeMillis() - text = DateFormat.format(format, time) contentDescription = DateFormat.format(descFormat, time) - Log.d(tag, "refreshTime this=$this" + - " currTimeContextDesc=$contentDescription" + - " measuredHeight=$measuredHeight" + - " lastMeasureCall=$lastMeasureCall" + - " isSingleLineInternal=$isSingleLineInternal") + val formattedText = DateFormat.format(format, time) + // Setting text actually triggers a layout pass (because the text view is set to + // wrap_content width and TextView always relayouts for this). Avoid needless + // relayout if the text didn't actually change. + if (!TextUtils.equals(text, formattedText)) { + text = formattedText + Log.d( + tag, "refreshTime this=$this" + + " currTimeContextDesc=$contentDescription" + + " measuredHeight=$measuredHeight" + + " lastMeasureCall=$lastMeasureCall" + + " isSingleLineInternal=$isSingleLineInternal" + ) + } else { + Log.d( + tag, "refreshTime (skipped due to unchanged text)" + + " this=$this" + + " currTimeContextDesc=$contentDescription" + + " measuredHeight=$measuredHeight" + + " lastMeasureCall=$lastMeasureCall" + + " isSingleLineInternal=$isSingleLineInternal" + ) + } } fun onTimeZoneChanged(timeZone: TimeZone?) { diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index 2b0c083e2f31..f444b373947f 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -32,6 +32,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.provider.Settings; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; @@ -267,8 +268,15 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { if (mBatteryPercentView == null) { return; } - mBatteryPercentView.setText( - NumberFormat.getPercentInstance().format(mLevel / 100f)); + + String percentText = NumberFormat.getPercentInstance().format(mLevel / 100f); + // Setting text actually triggers a layout pass (because the text view is set to + // wrap_content width and TextView always relayouts for this). Avoid needless + // relayout if the text didn't actually change. + if (!TextUtils.equals(mBatteryPercentView.getText(), percentText)) { + mBatteryPercentView.setText(percentText); + } + setContentDescription( getContext().getString(mCharging ? R.string.accessibility_battery_level_charging : R.string.accessibility_battery_level, mLevel)); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 63b2b201c498..2ac240885faa 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -376,7 +376,6 @@ public class UdfpsController implements DozeReceiver { boolean withinSensorArea = isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView); if (withinSensorArea) { - mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE); Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0); Log.v(TAG, "onTouch | action down"); // The pointer that causes ACTION_DOWN is always at index 0. @@ -792,6 +791,7 @@ public class UdfpsController implements DozeReceiver { + " current: " + mOverlay.getRequestId()); return; } + mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE); if (!mOnFingerDown) { playStartHaptic(); @@ -806,11 +806,9 @@ public class UdfpsController implements DozeReceiver { final UdfpsView view = mOverlay.getOverlayView(); if (view != null) { - Trace.beginAsyncSection("UdfpsController.e2e.startIllumination", 0); view.startIllumination(() -> { mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId); mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE); - Trace.endAsyncSection("UdfpsController.e2e.startIllumination", 0); }); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index 6d727b4cf966..b172e92871ce 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -65,6 +65,7 @@ import android.safetycenter.SafetyCenterManager; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.telecom.TelecomManager; +import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.view.CrossWindowBlurListeners; @@ -452,6 +453,12 @@ public class FrameworkServicesModule { @Provides @Singleton + static CarrierConfigManager provideCarrierConfigManager(Context context) { + return context.getSystemService(CarrierConfigManager.class); + } + + @Provides + @Singleton static WindowManager provideWindowManager(Context context) { return context.getSystemService(WindowManager.class); } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index c9a61a8a09df..44580aa4230a 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -162,6 +162,17 @@ public class Flags { public static final SysPropBooleanFlag WM_ENABLE_SHELL_TRANSITIONS = new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false); + // 1200 - predictive back + @Keep + public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag( + 1200, "persist.wm.debug.predictive_back", true); + @Keep + public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK_ANIM = new SysPropBooleanFlag( + 1201, "persist.wm.debug.predictive_back_anim", false); + @Keep + public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK = + new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false); + // Pay no attention to the reflection behind the curtain. // ========================== Curtain ========================== // | | diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt index 3c6805b4e881..cd86fff1c6f8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt @@ -19,6 +19,7 @@ package com.android.systemui.media.taptotransfer.sender import android.app.StatusBarManager import android.content.Context import android.media.MediaRoute2Info +import android.util.Log import android.view.View import androidx.annotation.StringRes import com.android.internal.logging.UiEventLogger @@ -221,7 +222,12 @@ enum class ChipStateSender( */ fun getSenderStateFromId( @StatusBarManager.MediaTransferSenderState displayState: Int, - ): ChipStateSender = values().first { it.stateInt == displayState } + ): ChipStateSender? = try { + values().first { it.stateInt == displayState } + } catch (e: NoSuchElementException) { + Log.e(TAG, "Could not find requested state $displayState", e) + null + } /** * Returns the state int from [StatusBarManager] associated with the given sender state @@ -238,3 +244,5 @@ enum class ChipStateSender( // process and we should keep the user informed about it as long as possible (but don't allow it to // continue indefinitely). private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L + +private const val TAG = "ChipStateSender" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index d785059e3de7..27586b4f5caa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.SecureSettings; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -84,6 +85,7 @@ public class NotificationLockscreenUserManagerImpl implements private final DeviceProvisionedController mDeviceProvisionedController; private final KeyguardStateController mKeyguardStateController; + private final SecureSettings mSecureSettings; private final Object mLock = new Object(); // Lazy @@ -187,6 +189,7 @@ public class NotificationLockscreenUserManagerImpl implements protected NotificationPresenter mPresenter; protected ContentObserver mLockscreenSettingsObserver; protected ContentObserver mSettingsObserver; + private boolean mHideSilentNotificationsOnLockscreen; private NotificationEntryManager getEntryManager() { if (mEntryManager == null) { @@ -208,6 +211,7 @@ public class NotificationLockscreenUserManagerImpl implements @Main Handler mainHandler, DeviceProvisionedController deviceProvisionedController, KeyguardStateController keyguardStateController, + SecureSettings secureSettings, DumpManager dumpManager) { mContext = context; mMainHandler = mainHandler; @@ -222,6 +226,7 @@ public class NotificationLockscreenUserManagerImpl implements mKeyguardManager = keyguardManager; mBroadcastDispatcher = broadcastDispatcher; mDeviceProvisionedController = deviceProvisionedController; + mSecureSettings = secureSettings; mKeyguardStateController = keyguardStateController; dumpManager.registerDumpable(this); @@ -256,12 +261,18 @@ public class NotificationLockscreenUserManagerImpl implements }; mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false, + mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false, mLockscreenSettingsObserver, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), + mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), + true, + mLockscreenSettingsObserver, + UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS), true, mLockscreenSettingsObserver, UserHandle.USER_ALL); @@ -272,7 +283,7 @@ public class NotificationLockscreenUserManagerImpl implements if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), + mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), false, mSettingsObserver, UserHandle.USER_ALL); @@ -366,7 +377,7 @@ public class NotificationLockscreenUserManagerImpl implements } } boolean exceedsPriorityThreshold; - if (hideSilentNotificationsOnLockscreen()) { + if (mHideSilentNotificationsOnLockscreen) { exceedsPriorityThreshold = entry.getBucket() == BUCKET_MEDIA_CONTROLS || (entry.getBucket() != BUCKET_SILENT @@ -377,11 +388,6 @@ public class NotificationLockscreenUserManagerImpl implements return mShowLockscreenNotifications && exceedsPriorityThreshold; } - private boolean hideSilentNotificationsOnLockscreen() { - return whitelistIpcs(() -> Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1) == 0); - } - private void setShowLockscreenNotifications(boolean show) { mShowLockscreenNotifications = show; } @@ -391,7 +397,7 @@ public class NotificationLockscreenUserManagerImpl implements } protected void updateLockscreenNotificationSetting() { - final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(), + final boolean show = mSecureSettings.getIntForUser( Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUserId) != 0; @@ -400,10 +406,13 @@ public class NotificationLockscreenUserManagerImpl implements final boolean allowedByDpm = (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0; + mHideSilentNotificationsOnLockscreen = mSecureSettings.getIntForUser( + Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1, mCurrentUserId) == 0; + setShowLockscreenNotifications(show && allowedByDpm); if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { - final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(), + final boolean remoteInput = mSecureSettings.getIntForUser( Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, 0, mCurrentUserId) != 0; @@ -426,8 +435,7 @@ public class NotificationLockscreenUserManagerImpl implements } if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) { - final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( - mContext.getContentResolver(), + final boolean allowedByUser = 0 != mSecureSettings.getIntForUser( Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle); final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); @@ -492,8 +500,7 @@ public class NotificationLockscreenUserManagerImpl implements } if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) { - final boolean allowedByUser = 0 != Settings.Secure.getIntForUser( - mContext.getContentResolver(), + final boolean allowedByUser = 0 != mSecureSettings.getIntForUser( Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle); final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle, DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java index 4a6d7e184ec2..8d7fc98164c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java @@ -21,8 +21,6 @@ import android.widget.TextView; import com.android.settingslib.WirelessUtils; -import java.util.List; - /** Shows the operator name */ public class OperatorNameView extends TextView { private boolean mDemoMode; @@ -43,8 +41,10 @@ public class OperatorNameView extends TextView { mDemoMode = demoMode; } - void update(boolean showOperatorName, boolean hasMobile, - List<OperatorNameViewController.SubInfo> subs) { + void update(boolean showOperatorName, + boolean hasMobile, + OperatorNameViewController.SubInfo sub + ) { setVisibility(showOperatorName ? VISIBLE : GONE); boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext); @@ -55,24 +55,21 @@ public class OperatorNameView extends TextView { } if (!mDemoMode) { - updateText(subs); + updateText(sub); } } - void updateText(List<OperatorNameViewController.SubInfo> subs) { + void updateText(OperatorNameViewController.SubInfo subInfo) { + CharSequence carrierName = null; CharSequence displayText = null; - final int N = subs.size(); - for (int i = 0; i < N; i++) { - OperatorNameViewController.SubInfo subInfo = subs.get(i); - CharSequence carrierName = subs.get(i).getCarrierName(); - if (!TextUtils.isEmpty(carrierName) && subInfo.simReady()) { - if (subInfo.stateInService()) { - displayText = subInfo.getCarrierName(); - break; - } + if (subInfo != null) { + carrierName = subInfo.getCarrierName(); + } + if (!TextUtils.isEmpty(carrierName) && subInfo.simReady()) { + if (subInfo.stateInService()) { + displayText = carrierName; } } - setText(displayText); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java index 8a4c4b5ac5c6..8afc72f08656 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.os.Bundle; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.view.View; @@ -30,12 +31,11 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; +import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.ViewController; -import java.util.ArrayList; -import java.util.List; - import javax.inject.Inject; /** Controller for {@link OperatorNameView}. */ @@ -47,19 +47,22 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> private final TunerService mTunerService; private final TelephonyManager mTelephonyManager; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final CarrierConfigTracker mCarrierConfigTracker; private OperatorNameViewController(OperatorNameView view, DarkIconDispatcher darkIconDispatcher, NetworkController networkController, TunerService tunerService, TelephonyManager telephonyManager, - KeyguardUpdateMonitor keyguardUpdateMonitor) { + KeyguardUpdateMonitor keyguardUpdateMonitor, + CarrierConfigTracker carrierConfigTracker) { super(view); mDarkIconDispatcher = darkIconDispatcher; mNetworkController = networkController; mTunerService = tunerService; mTelephonyManager = telephonyManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mCarrierConfigTracker = carrierConfigTracker; } @Override @@ -79,24 +82,22 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> } private void update() { - mView.update(mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0, - mTelephonyManager.isDataCapable(), getSubInfos()); + SubInfo defaultSubInfo = getDefaultSubInfo(); + boolean showOperatorName = + mCarrierConfigTracker + .getShowOperatorNameInStatusBarConfig(defaultSubInfo.getSubId()) + && (mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0); + mView.update(showOperatorName, mTelephonyManager.isDataCapable(), getDefaultSubInfo()); } - private List<SubInfo> getSubInfos() { - List<SubInfo> result = new ArrayList<>(); - List<SubscriptionInfo> subscritionInfos = - mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false); - - for (SubscriptionInfo subscriptionInfo : subscritionInfos) { - int subId = subscriptionInfo.getSubscriptionId(); - result.add(new SubInfo( - subscriptionInfo.getCarrierName(), - mKeyguardUpdateMonitor.getSimState(subId), - mKeyguardUpdateMonitor.getServiceState(subId))); - } - - return result; + private SubInfo getDefaultSubInfo() { + int defaultSubId = SubscriptionManager.getDefaultDataSubscriptionId(); + SubscriptionInfo sI = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(defaultSubId); + return new SubInfo( + sI.getSubscriptionId(), + sI.getCarrierName(), + mKeyguardUpdateMonitor.getSimState(defaultSubId), + mKeyguardUpdateMonitor.getServiceState(defaultSubId)); } /** Factory for constructing an {@link OperatorNameViewController}. */ @@ -106,22 +107,32 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> private final TunerService mTunerService; private final TelephonyManager mTelephonyManager; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final CarrierConfigTracker mCarrierConfigTracker; @Inject - public Factory(DarkIconDispatcher darkIconDispatcher, NetworkController networkController, - TunerService tunerService, TelephonyManager telephonyManager, - KeyguardUpdateMonitor keyguardUpdateMonitor) { + public Factory(DarkIconDispatcher darkIconDispatcher, + NetworkController networkController, + TunerService tunerService, + TelephonyManager telephonyManager, + KeyguardUpdateMonitor keyguardUpdateMonitor, + CarrierConfigTracker carrierConfigTracker) { mDarkIconDispatcher = darkIconDispatcher; mNetworkController = networkController; mTunerService = tunerService; mTelephonyManager = telephonyManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mCarrierConfigTracker = carrierConfigTracker; } /** Create an {@link OperatorNameViewController}. */ public OperatorNameViewController create(OperatorNameView view) { - return new OperatorNameViewController(view, mDarkIconDispatcher, mNetworkController, - mTunerService, mTelephonyManager, mKeyguardUpdateMonitor); + return new OperatorNameViewController(view, + mDarkIconDispatcher, + mNetworkController, + mTunerService, + mTelephonyManager, + mKeyguardUpdateMonitor, + mCarrierConfigTracker); } } @@ -152,7 +163,7 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> new KeyguardUpdateMonitorCallback() { @Override public void onRefreshCarrierInfo() { - mView.updateText(getSubInfos()); + mView.updateText(getDefaultSubInfo()); } }; @@ -176,17 +187,26 @@ public class OperatorNameViewController extends ViewController<OperatorNameView> }; static class SubInfo { + private final int mSubId; private final CharSequence mCarrierName; private final int mSimState; private final ServiceState mServiceState; - private SubInfo(CharSequence carrierName, - int simState, ServiceState serviceState) { + private SubInfo( + int subId, + CharSequence carrierName, + int simState, + ServiceState serviceState) { + mSubId = subId; mCarrierName = carrierName; mSimState = simState; mServiceState = serviceState; } + int getSubId() { + return mSubId; + } + boolean simReady() { return mSimState == TelephonyManager.SIM_STATE_READY; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java index c640ab6c3a90..7d0f00abcc98 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java @@ -270,8 +270,8 @@ public class NotificationBackgroundView extends View { /** Set the current expand animation size. */ public void setExpandAnimationSize(int width, int height) { - mExpandAnimationHeight = width; - mExpandAnimationWidth = height; + mExpandAnimationHeight = height; + mExpandAnimationWidth = width; invalidate(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index 6c6ec192646d..06532c4f9d17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -61,6 +61,7 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.settings.SecureSettings; import java.util.concurrent.Executor; @@ -263,6 +264,7 @@ public abstract class StatusBarViewModule { NetworkController networkController, StatusBarStateController statusBarStateController, CommandQueue commandQueue, + CarrierConfigTracker carrierConfigTracker, CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, OperatorNameViewController.Factory operatorNameViewControllerFactory, SecureSettings secureSettings, @@ -282,6 +284,7 @@ public abstract class StatusBarViewModule { networkController, statusBarStateController, commandQueue, + carrierConfigTracker, collapsedStatusBarFragmentLogger, operatorNameViewControllerFactory, secureSettings, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 8194957c52fc..9e48b763f4f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -32,6 +32,7 @@ import android.database.ContentObserver; import android.os.Bundle; import android.os.Parcelable; import android.provider.Settings; +import android.telephony.SubscriptionManager; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; @@ -69,6 +70,9 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.policy.EncryptionHelper; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.CarrierConfigTracker; +import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListener; +import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener; import com.android.systemui.util.settings.SecureSettings; import java.util.ArrayList; @@ -115,6 +119,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final NotificationIconAreaController mNotificationIconAreaController; private final PanelExpansionStateManager mPanelExpansionStateManager; private final StatusBarIconController mStatusBarIconController; + private final CarrierConfigTracker mCarrierConfigTracker; private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; private final SecureSettings mSecureSettings; private final Executor mMainExecutor; @@ -137,6 +142,28 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private OperatorNameViewController mOperatorNameViewController; private StatusBarSystemEventAnimator mSystemEventAnimator; + private final CarrierConfigChangedListener mCarrierConfigCallback = + new CarrierConfigChangedListener() { + @Override + public void onCarrierConfigChanged() { + if (mOperatorNameViewController == null) { + initOperatorName(); + } else { + // Already initialized, KeyguardUpdateMonitorCallback will handle the update + } + } + }; + + private final DefaultDataSubscriptionChangedListener mDefaultDataListener = + new DefaultDataSubscriptionChangedListener() { + @Override + public void onDefaultSubscriptionChanged(int subId) { + if (mOperatorNameViewController == null) { + initOperatorName(); + } + } + }; + @SuppressLint("ValidFragment") public CollapsedStatusBarFragment( StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory, @@ -153,6 +180,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue NetworkController networkController, StatusBarStateController statusBarStateController, CommandQueue commandQueue, + CarrierConfigTracker carrierConfigTracker, CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, OperatorNameViewController.Factory operatorNameViewControllerFactory, SecureSettings secureSettings, @@ -172,6 +200,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mNetworkController = networkController; mStatusBarStateController = statusBarStateController; mCommandQueue = commandQueue; + mCarrierConfigTracker = carrierConfigTracker; mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger; mOperatorNameViewControllerFactory = operatorNameViewControllerFactory; mSecureSettings = secureSettings; @@ -212,6 +241,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue initNotificationIconArea(); mSystemEventAnimator = new StatusBarSystemEventAnimator(mSystemIconArea, getResources()); + mCarrierConfigTracker.addCallback(mCarrierConfigCallback); + mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener); } @VisibleForTesting @@ -283,6 +314,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue if (mNetworkController.hasEmergencyCryptKeeperText()) { mNetworkController.removeCallback(mSignalCallback); } + mCarrierConfigTracker.removeCallback(mCarrierConfigCallback); + mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener); } /** Initializes views related to the notification icon area. */ @@ -569,11 +602,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void initOperatorName() { - if (getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar)) { + int subId = SubscriptionManager.getDefaultDataSubscriptionId(); + if (mCarrierConfigTracker.getShowOperatorNameInStatusBarConfig(subId)) { ViewStub stub = mStatusBar.findViewById(R.id.operator_name); mOperatorNameViewController = mOperatorNameViewControllerFactory.create((OperatorNameView) stub.inflate()); mOperatorNameViewController.init(); + // This view should not be visible on lock-screen + if (mKeyguardStateController.isShowing()) { + hideOperatorName(false); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 9e1e87b9856f..4c43734836c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -31,6 +31,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.text.Spannable; import android.text.SpannableStringBuilder; +import android.text.TextUtils; import android.text.format.DateFormat; import android.text.style.CharacterStyle; import android.text.style.RelativeSizeSpan; @@ -291,7 +292,13 @@ public class Clock extends TextView implements final void updateClock() { if (mDemoMode) return; mCalendar.setTimeInMillis(System.currentTimeMillis()); - setText(getSmallTime()); + CharSequence smallTime = getSmallTime(); + // Setting text actually triggers a layout pass (because the text view is set to + // wrap_content width and TextView always relayouts for this). Avoid needless + // relayout if the text didn't actually change. + if (!TextUtils.equals(smallTime, getText())) { + setText(smallTime); + } setContentDescription(mContentDescriptionFormat.format(mCalendar.getTime())); } diff --git a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java index 14190fa752c3..5f7d74542fff 100644 --- a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java +++ b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java @@ -23,43 +23,73 @@ import android.content.IntentFilter; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; +import android.util.ArraySet; import android.util.SparseBooleanArray; +import androidx.annotation.NonNull; + +import com.android.internal.telephony.TelephonyIntents; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.policy.CallbackController; + +import java.util.Set; import javax.inject.Inject; /** - * Tracks the Carrier Config values. + * Tracks CarrierConfigs for each subId, as well as the default configuration. CarrierConfigurations + * do not trigger a device configuration event, so any UI that relies on carrier configurations must + * register with the tracker to get proper updates. + * + * The tracker also listens for `TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED` + * + * @see CarrierConfigChangedListener to listen for updates */ @SysUISingleton -public class CarrierConfigTracker extends BroadcastReceiver { +public class CarrierConfigTracker + extends BroadcastReceiver + implements CallbackController<CarrierConfigTracker.CarrierConfigChangedListener> { private final SparseBooleanArray mCallStrengthConfigs = new SparseBooleanArray(); private final SparseBooleanArray mNoCallingConfigs = new SparseBooleanArray(); private final SparseBooleanArray mCarrierProvisionsWifiMergedNetworks = new SparseBooleanArray(); + private final SparseBooleanArray mShowOperatorNameConfigs = new SparseBooleanArray(); private final CarrierConfigManager mCarrierConfigManager; + private final Set<CarrierConfigChangedListener> mListeners = new ArraySet<>(); + private final Set<DefaultDataSubscriptionChangedListener> mDataListeners = + new ArraySet<>(); private boolean mDefaultCallStrengthConfigLoaded; private boolean mDefaultCallStrengthConfig; private boolean mDefaultNoCallingConfigLoaded; private boolean mDefaultNoCallingConfig; private boolean mDefaultCarrierProvisionsWifiMergedNetworksLoaded; private boolean mDefaultCarrierProvisionsWifiMergedNetworks; + private boolean mDefaultShowOperatorNameConfigLoaded; + private boolean mDefaultShowOperatorNameConfig; @Inject - public CarrierConfigTracker(Context context, BroadcastDispatcher broadcastDispatcher) { - mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class); - broadcastDispatcher.registerReceiver( - this, new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); + public CarrierConfigTracker( + CarrierConfigManager carrierConfigManager, + BroadcastDispatcher broadcastDispatcher) { + mCarrierConfigManager = carrierConfigManager; + IntentFilter filter = new IntentFilter(); + filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); + filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); + broadcastDispatcher.registerReceiver(this, filter); } @Override public void onReceive(Context context, Intent intent) { - if (!CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) { - return; + String action = intent.getAction(); + if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) { + updateFromNewCarrierConfig(intent); + } else if (TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) { + updateDefaultDataSubscription(intent); } + } + private void updateFromNewCarrierConfig(Intent intent) { final int subId = intent.getIntExtra( CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, SubscriptionManager.INVALID_SUBSCRIPTION_ID); @@ -84,6 +114,29 @@ public class CarrierConfigTracker extends BroadcastReceiver { mCarrierProvisionsWifiMergedNetworks.put(subId, config.getBoolean( CarrierConfigManager.KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL)); } + synchronized (mShowOperatorNameConfigs) { + mShowOperatorNameConfigs.put(subId, config.getBoolean( + CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL)); + } + + notifyCarrierConfigChanged(); + } + + private void updateDefaultDataSubscription(Intent intent) { + int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, -1); + notifyDefaultDataSubscriptionChanged(subId); + } + + private void notifyCarrierConfigChanged() { + for (CarrierConfigChangedListener l : mListeners) { + l.onCarrierConfigChanged(); + } + } + + private void notifyDefaultDataSubscriptionChanged(int subId) { + for (DefaultDataSubscriptionChangedListener l : mDataListeners) { + l.onDefaultSubscriptionChanged(subId); + } } /** @@ -139,4 +192,73 @@ public class CarrierConfigTracker extends BroadcastReceiver { } return mDefaultCarrierProvisionsWifiMergedNetworks; } + + /** + * Returns the KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL value for the default config + */ + public boolean getShowOperatorNameInStatusBarConfigDefault() { + if (!mDefaultShowOperatorNameConfigLoaded) { + mDefaultShowOperatorNameConfig = CarrierConfigManager.getDefaultConfig().getBoolean( + CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL); + mDefaultShowOperatorNameConfigLoaded = true; + } + + return mDefaultShowOperatorNameConfig; + } + + /** + * Returns the KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL value for the given subId, or the + * default value if no override exists + * + * @param subId the subscription id for which to query the config + */ + public boolean getShowOperatorNameInStatusBarConfig(int subId) { + if (mShowOperatorNameConfigs.indexOfKey(subId) >= 0) { + return mShowOperatorNameConfigs.get(subId); + } else { + return getShowOperatorNameInStatusBarConfigDefault(); + } + } + + @Override + public void addCallback(@NonNull CarrierConfigChangedListener listener) { + mListeners.add(listener); + } + + @Override + public void removeCallback(@NonNull CarrierConfigChangedListener listener) { + mListeners.remove(listener); + } + + /** */ + public void addDefaultDataSubscriptionChangedListener( + @NonNull DefaultDataSubscriptionChangedListener listener) { + mDataListeners.add(listener); + } + + /** */ + public void removeDataSubscriptionChangedListener( + DefaultDataSubscriptionChangedListener listener) { + mDataListeners.remove(listener); + } + + /** + * Called when carrier config changes + */ + public interface CarrierConfigChangedListener { + /** */ + void onCarrierConfigChanged(); + } + + /** + * Called when the default data subscription changes. Listeners may want to query + * subId-dependent configuration values when this event happens + */ + public interface DefaultDataSubscriptionChangedListener { + /** + * @param subId the new default data subscription id per + * {@link SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX} + */ + void onDefaultSubscriptionChanged(int subId); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt index ef5315428a60..9a01464fc869 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt @@ -277,6 +277,17 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { } @Test + fun commandQueueCallback_invalidStateParam_noChipShown() { + commandQueueCallback.updateMediaTapToTransferSenderDisplay( + 100, + routeInfo, + null + ) + + verify(windowManager, never()).addView(any(), any()) + } + + @Test fun receivesNewStateFromCommandQueue_isLogged() { commandQueueCallback.updateMediaTapToTransferSenderDisplay( StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index 48f820626fac..7687d1204541 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -66,6 +66,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.FakeSettings; import com.google.android.collect.Lists; @@ -109,6 +110,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { private UserInfo mCurrentUser; private UserInfo mSecondaryUser; private UserInfo mWorkUser; + private FakeSettings mSettings; private TestNotificationLockscreenUserManager mLockscreenUserManager; private NotificationEntry mCurrentUserNotif; private NotificationEntry mSecondaryUserNotif; @@ -120,6 +122,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); int currentUserId = ActivityManager.getCurrentUser(); + mSettings = new FakeSettings(); + mSettings.setUserId(ActivityManager.getCurrentUser()); mCurrentUser = new UserInfo(currentUserId, "", 0); mSecondaryUser = new UserInfo(currentUserId + 1, "", 0); mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0, @@ -157,48 +161,45 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testLockScreenShowNotificationsFalse() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications()); } @Test public void testLockScreenShowNotificationsTrue() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications()); } @Test public void testLockScreenAllowPrivateNotificationsTrue() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id)); } @Test public void testLockScreenAllowPrivateNotificationsFalse() { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id)); } @Test public void testLockScreenAllowsWorkPrivateNotificationsFalse() { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mWorkUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id)); } @Test public void testLockScreenAllowsWorkPrivateNotificationsTrue() { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mWorkUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id)); } @@ -206,8 +207,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testCurrentUserPrivateNotificationsNotRedacted() { // GIVEN current user doesn't allow private notifications to show - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN current user's notification is redacted @@ -217,8 +218,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testCurrentUserPrivateNotificationsRedacted() { // GIVEN current user allows private notifications to show - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mCurrentUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN current user's notification isn't redacted @@ -228,8 +229,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testWorkPrivateNotificationsRedacted() { // GIVEN work profile doesn't private notifications to show - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mWorkUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN work profile notification is redacted @@ -239,8 +240,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testWorkPrivateNotificationsNotRedacted() { // GIVEN work profile allows private notifications to show - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mWorkUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN work profile notification isn't redacted @@ -250,12 +251,11 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() { // GIVEN work profile allows private notifications to show but the other users don't - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id); - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mSecondaryUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); @@ -270,12 +270,11 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testWorkProfileRedacted_otherUsersNotRedacted() { // GIVEN work profile doesn't allow private notifications to show but the other users do - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id); - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id); - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mWorkUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mSecondaryUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); @@ -291,10 +290,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { public void testSecondaryUserNotRedacted_currentUserRedacted() { // GIVEN secondary profile allows private notifications to show but the current user // doesn't allow private notifications to show - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id); - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, + mCurrentUser.id); + mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mSecondaryUser.id); mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); @@ -328,10 +326,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testShowSilentNotifications_settingSaysShow() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); NotificationEntry entry = new NotificationEntryBuilder() .setImportance(IMPORTANCE_LOW) @@ -343,10 +340,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testShowSilentNotifications_settingSaysHide() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); final Notification notification = mock(Notification.class); when(notification.isForegroundService()).thenReturn(true); @@ -360,10 +356,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testShowSilentNotificationsPeopleBucket_settingSaysHide() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); final Notification notification = mock(Notification.class); when(notification.isForegroundService()).thenReturn(true); @@ -377,10 +372,9 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testShowSilentNotificationsMediaBucket_settingSaysHide() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); final Notification notification = mock(Notification.class); when(notification.isForegroundService()).thenReturn(true); @@ -396,8 +390,8 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Test public void testKeyguardNotificationSuppressors() { // GIVEN a notification that should be shown on the lockscreen - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); final NotificationEntry entry = new NotificationEntryBuilder() .setImportance(IMPORTANCE_HIGH) .build(); @@ -433,6 +427,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { Handler.createAsync(Looper.myLooper()), mDeviceProvisionedController, mKeyguardStateController, + mSettings, mock(DumpManager.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 509509401d13..497f7fba2dbf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -59,6 +59,7 @@ import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentCom import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; @@ -90,6 +91,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private OperatorNameViewController mOperatorNameViewController; private SecureSettings mSecureSettings; private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + private final CarrierConfigTracker mCarrierConfigTracker = mock(CarrierConfigTracker.class); @Mock private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory; @@ -373,6 +375,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mNetworkController, mStatusBarStateController, mCommandQueue, + mCarrierConfigTracker, new CollapsedStatusBarFragmentLogger( new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)), new DisableFlagsLogger() diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java index e66491e4cbd1..e660e1f2d845 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java @@ -16,6 +16,7 @@ package com.android.systemui.util.settings; +import android.annotation.UserIdInt; import android.content.ContentResolver; import android.database.ContentObserver; import android.net.Uri; @@ -34,6 +35,8 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>(); public static final Uri CONTENT_URI = Uri.parse("content://settings/fake"); + @UserIdInt + private int mUserId = UserHandle.USER_CURRENT; public FakeSettings() { } @@ -85,9 +88,13 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti return Uri.withAppendedPath(CONTENT_URI, name); } + public void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + @Override public int getUserId() { - return UserHandle.USER_CURRENT; + return mUserId; } @Override diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index 76df8b9f84e8..e78c8d1ddcac 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -24,8 +24,10 @@ import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT; +import android.annotation.NonNull; import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; +import android.app.backup.BackupAgent; import android.app.backup.BackupManager; import android.app.backup.FullBackup; import android.app.backup.IBackupManagerMonitor; @@ -38,10 +40,12 @@ import android.content.pm.Signature; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.provider.Settings; +import android.system.OsConstants; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupRestoreTask; @@ -57,6 +61,7 @@ import com.android.server.backup.utils.FullBackupRestoreObserverUtils; import com.android.server.backup.utils.RestoreUtils; import com.android.server.backup.utils.TarBackupReader; +import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -135,6 +140,8 @@ public class FullRestoreEngine extends RestoreEngine { private boolean mPipesClosed; private final BackupEligibilityRules mBackupEligibilityRules; + private FileMetadata mReadOnlyParent = null; + public FullRestoreEngine( UserBackupManagerService backupManagerService, OperationStorage operationStorage, BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer, @@ -158,6 +165,22 @@ public class FullRestoreEngine extends RestoreEngine { mBackupEligibilityRules = backupEligibilityRules; } + @VisibleForTesting + FullRestoreEngine() { + mIsAdbRestore = false; + mAllowApks = false; + mEphemeralOpToken = 0; + mUserId = 0; + mBackupEligibilityRules = null; + mAgentTimeoutParameters = null; + mBuffer = null; + mBackupManagerService = null; + mOperationStorage = null; + mMonitor = null; + mMonitorTask = null; + mOnlyPackage = null; + } + public IBackupAgent getAgent() { return mAgent; } @@ -397,6 +420,11 @@ public class FullRestoreEngine extends RestoreEngine { okay = false; } + if (shouldSkipReadOnlyDir(info)) { + // b/194894879: We don't support restore of read-only dirs. + okay = false; + } + // At this point we have an agent ready to handle the full // restore data as well as a pipe for sending data to // that agent. Tell the agent to start reading from the @@ -573,6 +601,45 @@ public class FullRestoreEngine extends RestoreEngine { return (info != null); } + boolean shouldSkipReadOnlyDir(FileMetadata info) { + if (isValidParent(mReadOnlyParent, info)) { + // This file has a read-only parent directory, we shouldn't + // restore it. + return true; + } else { + // We're now in a different branch of the file tree, update the parent + // value. + if (isReadOnlyDir(info)) { + // Current directory is read-only. Remember it so that we can skip all + // of its contents. + mReadOnlyParent = info; + Slog.w(TAG, "Skipping restore of " + info.path + " and its contents as " + + "read-only dirs are currently not supported."); + return true; + } else { + mReadOnlyParent = null; + } + } + + return false; + } + + private static boolean isValidParent(FileMetadata parentDir, @NonNull FileMetadata childDir) { + return parentDir != null + && childDir.packageName.equals(parentDir.packageName) + && childDir.domain.equals(parentDir.domain) + && childDir.path.startsWith(getPathWithTrailingSeparator(parentDir.path)); + } + + private static String getPathWithTrailingSeparator(String path) { + return path.endsWith(File.separator) ? path : path + File.separator; + } + + private static boolean isReadOnlyDir(FileMetadata file) { + // Check if owner has 'write' bit in the file's mode value (see 'man -7 inode' for details). + return file.type == BackupAgent.TYPE_DIRECTORY && (file.mode & OsConstants.S_IWUSR) == 0; + } + private void setUpPipes() throws IOException { synchronized (mPipesLock) { mPipes = ParcelFileDescriptor.createPipe(); diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index f34c5062144e..06f698efde2b 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -1208,6 +1208,11 @@ public abstract class PackageManagerInternal { public abstract SharedUserApi getSharedUserApi(int sharedUserAppId); /** + * Returns if the given uid is privileged or not. + */ + public abstract boolean isUidPrivileged(int uid); + + /** * Initiates a package state mutation request, returning the current state as known by * PackageManager. This allows the later commit request to compare the initial values and * determine if any state was changed or any packages were updated since the whole request diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index e6b7a4c287cd..b059cc7e2aa2 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -2977,19 +2977,21 @@ public class AccountManagerService * outside of those expected to be injected by the AccountManager, e.g. * ANDORID_PACKAGE_NAME. */ - String token = readCachedTokenInternal( + TokenCache.Value cachedToken = readCachedTokenInternal( accounts, account, authTokenType, callerPkg, callerPkgSigDigest); - if (token != null) { + if (cachedToken != null) { logGetAuthTokenMetrics(callerPkg, account.type); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator."); } Bundle result = new Bundle(); - result.putString(AccountManager.KEY_AUTHTOKEN, token); + result.putString(AccountManager.KEY_AUTHTOKEN, cachedToken.token); + result.putLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, + cachedToken.expiryEpochMillis); result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); onResult(response, result); @@ -6121,7 +6123,7 @@ public class AccountManagerService } } - protected String readCachedTokenInternal( + protected TokenCache.Value readCachedTokenInternal( UserAccounts accounts, Account account, String tokenType, diff --git a/services/core/java/com/android/server/accounts/TokenCache.java b/services/core/java/com/android/server/accounts/TokenCache.java index 66e550fe3c4c..9427ee41cfdc 100644 --- a/services/core/java/com/android/server/accounts/TokenCache.java +++ b/services/core/java/com/android/server/accounts/TokenCache.java @@ -20,8 +20,6 @@ import android.accounts.Account; import android.util.LruCache; import android.util.Pair; -import com.android.internal.util.Preconditions; - import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -35,7 +33,8 @@ import java.util.Objects; private static final int MAX_CACHE_CHARS = 64000; - private static class Value { + /** Package private*/ + static class Value { public final String token; public final long expiryEpochMillis; @@ -217,12 +216,12 @@ import java.util.Objects; /** * Gets a token from the cache if possible. */ - public String get(Account account, String tokenType, String packageName, byte[] sigDigest) { + public Value get(Account account, String tokenType, String packageName, byte[] sigDigest) { Key k = new Key(account, tokenType, packageName, sigDigest); Value v = mCachedTokens.get(k); long currentTime = System.currentTimeMillis(); if (v != null && currentTime < v.expiryEpochMillis) { - return v.token; + return v; } else if (v != null) { remove(account.type, v.token); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index a73c8e0c914a..0e4bbbb7a412 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -18,12 +18,14 @@ package com.android.server.locksettings; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import android.app.ActivityManager; import android.app.admin.PasswordMetrics; import android.content.Context; import android.os.ShellCommand; import android.os.SystemProperties; +import android.os.UserHandle; import android.text.TextUtils; import android.util.Slog; @@ -48,6 +50,8 @@ class LockSettingsShellCommand extends ShellCommand { private static final String COMMAND_REMOVE_CACHE = "remove-cache"; private static final String COMMAND_SET_ROR_PROVIDER_PACKAGE = "set-resume-on-reboot-provider-package"; + private static final String COMMAND_REQUIRE_STRONG_AUTH = + "require-strong-auth"; private static final String COMMAND_HELP = "help"; private int mCurrentUserId; @@ -97,6 +101,9 @@ class LockSettingsShellCommand extends ShellCommand { case COMMAND_SET_ROR_PROVIDER_PACKAGE: runSetResumeOnRebootProviderPackage(); return 0; + case COMMAND_REQUIRE_STRONG_AUTH: + runRequireStrongAuth(); + return 0; case COMMAND_HELP: onHelp(); return 0; @@ -192,6 +199,10 @@ class LockSettingsShellCommand extends ShellCommand { pw.println(" Sets the package name for server based resume on reboot service " + "provider."); pw.println(""); + pw.println(" require-strong-auth [--user USER_ID] <reason>"); + pw.println(" Requires the strong authentication. The current supported reasons: " + + "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN."); + pw.println(""); } } @@ -288,6 +299,24 @@ class LockSettingsShellCommand extends ShellCommand { return true; } + private boolean runRequireStrongAuth() { + final String reason = mNew; + int strongAuthReason; + switch (reason) { + case "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN": + strongAuthReason = STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; + mCurrentUserId = UserHandle.USER_ALL; + break; + default: + getErrPrintWriter().println("Unsupported reason: " + reason); + return false; + } + mLockPatternUtils.requireStrongAuth(strongAuthReason, mCurrentUserId); + getOutPrintWriter().println("Require strong auth for USER_ID " + + mCurrentUserId + " because of " + mNew); + return true; + } + private boolean runClear() { LockscreenCredential none = LockscreenCredential.createNone(); if (!isNewCredentialSufficient(none)) { diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java index 8be90e0cc622..b45bfb1c2d92 100644 --- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java +++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java @@ -30,6 +30,7 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.os.logcat.ILogcatManagerService; import android.util.Slog; +import android.view.InflateException; import android.view.View; import android.widget.Button; import android.widget.TextView; @@ -56,33 +57,46 @@ public class LogAccessDialogActivity extends Activity implements private String mAlertTitle; private AlertDialog.Builder mAlertDialog; private AlertDialog mAlert; + private View mAlertView; private static final int DIALOG_TIME_OUT = Build.IS_DEBUGGABLE ? 60000 : 300000; private static final int MSG_DISMISS_DIALOG = 0; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mContext = this; - Intent intent = getIntent(); - mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); - mUid = intent.getIntExtra("com.android.server.logcat.uid", 0); - mGid = intent.getIntExtra("com.android.server.logcat.gid", 0); - mPid = intent.getIntExtra("com.android.server.logcat.pid", 0); - mFd = intent.getIntExtra("com.android.server.logcat.fd", 0); - mAlertTitle = getTitleString(mContext, mPackageName, mUid); + try { + mContext = this; + + // retrieve Intent extra information + Intent intent = getIntent(); + getIntentInfo(intent); - if (mAlertTitle != null) { + // retrieve the title string from passed intent extra + mAlertTitle = getTitleString(mContext, mPackageName, mUid); + // creaet View + mAlertView = createView(); + + // create AlertDialog mAlertDialog = new AlertDialog.Builder(this); - mAlertDialog.setView(createView()); + mAlertDialog.setView(mAlertView); + // show Alert mAlert = mAlertDialog.create(); mAlert.show(); + + // set Alert Timeout mHandler.sendEmptyMessageDelayed(MSG_DISMISS_DIALOG, DIALOG_TIME_OUT); + } catch (Exception e) { + try { + Slog.e(TAG, "onCreate failed, declining the logd access", e); + mLogcatManagerService.decline(mUid, mGid, mPid, mFd); + } catch (RemoteException ex) { + Slog.e(TAG, "Fails to call remote functions", ex); + } } } @@ -95,6 +109,19 @@ public class LogAccessDialogActivity extends Activity implements mAlert = null; } + private void getIntentInfo(Intent intent) throws Exception { + + if (intent == null) { + throw new NullPointerException("Intent is null"); + } + + mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); + mUid = intent.getIntExtra("com.android.server.logcat.uid", 0); + mGid = intent.getIntExtra("com.android.server.logcat.gid", 0); + mPid = intent.getIntExtra("com.android.server.logcat.pid", 0); + mFd = intent.getIntExtra("com.android.server.logcat.fd", 0); + } + private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { @@ -116,26 +143,41 @@ public class LogAccessDialogActivity extends Activity implements } }; - private String getTitleString(Context context, String callingPackage, int uid) { + private String getTitleString(Context context, String callingPackage, int uid) + throws Exception { + PackageManager pm = context.getPackageManager(); - try { - return context.getString( - com.android.internal.R.string.log_access_confirmation_title, - pm.getApplicationInfoAsUser(callingPackage, - PackageManager.MATCH_DIRECT_BOOT_AUTO, - UserHandle.getUserId(uid)).loadLabel(pm)); - } catch (NameNotFoundException e) { - Slog.e(TAG, "App name is unknown.", e); - return null; + if (pm == null) { + throw new NullPointerException("PackageManager is null"); } + + CharSequence appLabel = pm.getApplicationInfoAsUser(callingPackage, + PackageManager.MATCH_DIRECT_BOOT_AUTO, + UserHandle.getUserId(uid)).loadLabel(pm); + if (appLabel == null) { + throw new NameNotFoundException("Application Label is null"); + } + + return context.getString(com.android.internal.R.string.log_access_confirmation_title, + appLabel); } - private View createView() { + /** + * Returns the dialog view. + * If we cannot retrieve the package name, it returns null and we decline the full device log + * access + */ + private View createView() throws Exception { + final View view = getLayoutInflater().inflate( R.layout.log_access_user_consent_dialog_permission, null /*root*/); + if (view == null) { + throw new InflateException(); + } + ((TextView) view.findViewById(R.id.log_access_dialog_title)) - .setText(mAlertTitle); + .setText(mAlertTitle); Button button_allow = (Button) view.findViewById(R.id.log_access_dialog_allow_button); button_allow.setOnClickListener(this); @@ -144,6 +186,7 @@ public class LogAccessDialogActivity extends Activity implements button_deny.setOnClickListener(this); return view; + } @Override diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java index 015bf3c5e390..4c265ad5ff88 100644 --- a/services/core/java/com/android/server/logcat/LogcatManagerService.java +++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java @@ -102,16 +102,27 @@ public final class LogcatManagerService extends SystemService { } } - private void showDialog(int uid, int gid, int pid, int fd) { + /** + * Returns the package name. + * If we cannot retrieve the package name, it returns null and we decline the full device log + * access + */ + private String getPackageName(int uid, int gid, int pid, int fd) { + final ActivityManagerInternal activityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); + if (activityManagerInternal != null) { + String packageName = activityManagerInternal.getPackageNameByPid(pid); + if (packageName != null) { + return packageName; + } + } PackageManager pm = mContext.getPackageManager(); - String packageName = activityManagerInternal.getPackageNameByPid(pid); - if (packageName != null) { - Intent mIntent = createIntent(packageName, uid, gid, pid, fd); - mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM); - return; + if (pm == null) { + // Decline the logd access if PackageManager is null + Slog.e(TAG, "PackageManager is null, declining the logd access"); + return null; } String[] packageNames = pm.getPackagesForUid(uid); @@ -119,21 +130,19 @@ public final class LogcatManagerService extends SystemService { if (ArrayUtils.isEmpty(packageNames)) { // Decline the logd access if the app name is unknown Slog.e(TAG, "Unknown calling package name, declining the logd access"); - declineLogdAccess(uid, gid, pid, fd); - return; + return null; } String firstPackageName = packageNames[0]; - if (firstPackageName.isEmpty() || firstPackageName == null) { + if (firstPackageName == null || firstPackageName.isEmpty()) { // Decline the logd access if the package name from uid is unknown Slog.e(TAG, "Unknown calling package name, declining the logd access"); - declineLogdAccess(uid, gid, pid, fd); - return; + return null; } - final Intent mIntent = createIntent(firstPackageName, uid, gid, pid, fd); - mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM); + return firstPackageName; + } private void declineLogdAccess(int uid, int gid, int pid, int fd) { @@ -198,16 +207,23 @@ public final class LogcatManagerService extends SystemService { final int procState = LocalServices.getService(ActivityManagerInternal.class) .getUidProcessState(mUid); - // If the process is foreground, show a dialog for user consent + // If the process is foreground and we can retrieve the package name, show a dialog + // for user consent if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { - showDialog(mUid, mGid, mPid, mFd); - } else { - /** - * If the process is background, decline the logd access. - **/ - declineLogdAccess(mUid, mGid, mPid, mFd); - return; + String packageName = getPackageName(mUid, mGid, mPid, mFd); + if (packageName != null) { + final Intent mIntent = createIntent(packageName, mUid, mGid, mPid, mFd); + mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM); + return; + } } + + /** + * If the process is background or cannot retrieve the package name, + * decline the logd access. + **/ + declineLogdAccess(mUid, mGid, mPid, mFd); + return; } } } diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING index 02095eb70c0e..4ccf09e5e020 100644 --- a/services/core/java/com/android/server/net/TEST_MAPPING +++ b/services/core/java/com/android/server/net/TEST_MAPPING @@ -2,12 +2,8 @@ "presubmit-large": [ { "name": "CtsHostsideNetworkTests", - "file_patterns": ["(/|^)NetworkPolicy[^/]*\\.java"], "options": [ { - "include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests" - }, - { "exclude-annotation": "androidx.test.filters.FlakyTest" }, { diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index 2fe7913342a2..ec6443db9f0f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -722,6 +722,11 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { return snapshot().getSharedUser(sharedUserAppId); } + @Override + public boolean isUidPrivileged(int uid) { + return snapshot().isUidPrivileged(uid); + } + @NonNull @Override @Deprecated diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 003268b33cdc..f6396556ae22 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -195,7 +195,6 @@ class ActivityStarter { private TaskFragment mInTaskFragment; @VisibleForTesting boolean mAddingToTask; - private Task mReuseTask; private ActivityInfo mNewTaskInfo; private Intent mNewTaskIntent; @@ -204,6 +203,7 @@ class ActivityStarter { // The task that the last activity was started into. We currently reset the actual start // activity's task and as a result may not have a reference to the task in all cases private Task mTargetTask; + private boolean mIsTaskCleared; private boolean mMovedToFront; private boolean mNoAnimation; private boolean mAvoidMoveToFront; @@ -597,7 +597,6 @@ class ActivityStarter { mInTask = starter.mInTask; mInTaskFragment = starter.mInTaskFragment; mAddingToTask = starter.mAddingToTask; - mReuseTask = starter.mReuseTask; mNewTaskInfo = starter.mNewTaskInfo; mNewTaskIntent = starter.mNewTaskIntent; @@ -605,6 +604,7 @@ class ActivityStarter { mTargetTask = starter.mTargetTask; mTargetRootTask = starter.mTargetRootTask; + mIsTaskCleared = starter.mIsTaskCleared; mMovedToFront = starter.mMovedToFront; mNoAnimation = starter.mNoAnimation; mAvoidMoveToFront = starter.mAvoidMoveToFront; @@ -1568,10 +1568,7 @@ class ActivityStarter { return; } - final int clearTaskFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK; - boolean clearedTask = (mLaunchFlags & clearTaskFlags) == clearTaskFlags - && mReuseTask != null; - if (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP || clearedTask) { + if (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP) { // The activity was already running so it wasn't started, but either brought to the // front or the new intent was delivered to it since it was already in front. Notify // anyone interested in this piece of information. @@ -1581,7 +1578,7 @@ class ActivityStarter { final ActivityRecord top = targetTask.getTopNonFinishingActivity(); final boolean visible = top != null && top.isVisible(); mService.getTaskChangeNotificationController().notifyActivityRestartAttempt( - targetTask.getTaskInfo(), homeTaskVisible, clearedTask, visible); + targetTask.getTaskInfo(), homeTaskVisible, mIsTaskCleared, visible); } if (ActivityManager.isStartResultSuccessful(result)) { @@ -1927,6 +1924,7 @@ class ActivityStarter { return START_SUCCESS; } + /** Returns the leaf task where the target activity may be placed. */ private Task computeTargetTask() { if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { @@ -1935,6 +1933,12 @@ class ActivityStarter { } else if (mSourceRecord != null) { return mSourceRecord.getTask(); } else if (mInTask != null) { + // The task is specified from AppTaskImpl, so it may not be attached yet. + if (!mInTask.isAttached()) { + // Attach the task to display area. Ignore the returned root task (though usually + // they are the same) because "target task" should be leaf task. + getOrCreateRootTask(mStartActivity, mLaunchFlags, mInTask, mOptions); + } return mInTask; } else { final Task rootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, null /* task */, @@ -2225,13 +2229,12 @@ class ActivityStarter { // activity. Well that should not be too hard... // Note: we must persist the {@link Task} first as intentActivity could be // removed from calling performClearTaskLocked (For example, if it is being brought out - // of history or if it is finished immediately), thus disassociating the task. Also note - // that mReuseTask is reset as a result of {@link Task#performClearTaskLocked} - // launching another activity. Keep the task-overlay activity because the targetTask - // will be reused to launch new activity. + // of history or if it is finished immediately), thus disassociating the task. Keep the + // task-overlay activity because the targetTask will be reused to launch new activity. targetTask.performClearTaskForReuse(true /* excludingTaskOverlay*/); targetTask.setIntent(mStartActivity); mAddingToTask = true; + mIsTaskCleared = true; } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || isDocumentLaunchesIntoExisting(mLaunchFlags) || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK, @@ -2344,7 +2347,6 @@ class ActivityStarter { mInTask = null; mInTaskFragment = null; mAddingToTask = false; - mReuseTask = null; mNewTaskInfo = null; mNewTaskIntent = null; @@ -2352,6 +2354,7 @@ class ActivityStarter { mTargetRootTask = null; mTargetTask = null; + mIsTaskCleared = false; mMovedToFront = false; mNoAnimation = false; mAvoidMoveToFront = false; @@ -2569,8 +2572,6 @@ class ActivityStarter { } else { mAddingToTask = true; } - - mReuseTask = mInTask; } else { mInTask = null; // Launch ResolverActivity in the source task, so that it stays in the task bounds @@ -2843,7 +2844,7 @@ class ActivityStarter { mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions); task.mTransitionController.collectExistenceChange(task); - addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask"); + addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask"); ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s", mStartActivity, mStartActivity.getTask()); @@ -2953,11 +2954,6 @@ class ActivityStarter { private Task getOrCreateRootTask(ActivityRecord r, int launchFlags, Task task, ActivityOptions aOptions) { - // We are reusing a task, keep the root task! - if (mReuseTask != null) { - return mReuseTask.getRootTask(); - } - final boolean onTop = (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind; final Task sourceTask = mSourceRecord != null ? mSourceRecord.getTask() : null; diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 22a2c41ca5f3..6e46fa6b67d0 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -26,6 +26,8 @@ import android.content.Intent; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; import android.os.UserHandle; /** @@ -54,6 +56,16 @@ class AppTaskImpl extends IAppTask.Stub { } @Override + public boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + try { + return super.onTransact(code, data, reply, flags); + } catch (RuntimeException e) { + throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact(TAG, e); + } + } + + @Override public void finishAndRemoveTask() { checkCaller(); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index ef0b7374f79e..66c625e13f78 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -47,12 +47,6 @@ import com.android.server.LocalServices; class BackNavigationController { private static final String TAG = "BackNavigationController"; - // By default, enable new back dispatching without any animations. - private static final int BACK_PREDICTABILITY_PROP = - SystemProperties.getInt("persist.debug.back_predictability", 1); - private static final int ANIMATIONS_MASK = 1 << 1; - private static final int SCREENSHOT_MASK = 1 << 2; - @Nullable private TaskSnapshotController mTaskSnapshotController; @@ -60,15 +54,15 @@ class BackNavigationController { * Returns true if the back predictability feature is enabled */ static boolean isEnabled() { - return BACK_PREDICTABILITY_PROP > 0; + return SystemProperties.getInt("persist.wm.debug.predictive_back", 1) != 0; } static boolean isScreenshotEnabled() { - return (BACK_PREDICTABILITY_PROP & SCREENSHOT_MASK) != 0; + return SystemProperties.getInt("persist.wm.debug.predictive_back_screenshot", 0) != 0; } private static boolean isAnimationEnabled() { - return (BACK_PREDICTABILITY_PROP & ANIMATIONS_MASK) != 0; + return SystemProperties.getInt("persist.wm.debug.predictive_back_anim", 0) != 0; } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 81560d4f8676..0893207a1cbe 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1992,7 +1992,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp scheduleAnimation(); forAllWindows(w -> { - if (w.mHasSurface && !rotateSeamlessly) { + if (!w.mHasSurface) return; + if (!rotateSeamlessly) { ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w); w.setOrientationChanging(true); } diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index 0038c716fee7..c162e8ecadfc 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -175,7 +175,7 @@ final class LetterboxUiController { final Rect spaceToFill = transformedBounds != null ? transformedBounds : mActivityRecord.inMultiWindowMode() - ? mActivityRecord.getRootTask().getBounds() + ? mActivityRecord.getTask().getBounds() : mActivityRecord.getRootTask().getParent().getBounds(); mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint); } else if (mLetterbox != null) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 51d68bc0177a..237982220a18 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2606,11 +2606,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } + // Remove immediately if there is display transition because the animation is + // usually unnoticeable (e.g. covered by rotation animation) and the animation + // bounds could be inconsistent, such as depending on when the window applies + // its draw transaction with new rotation. + final boolean allowExitAnimation = !getDisplayContent().inTransition(); + if (wasVisible) { final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE; // Try starting an animation. - if (mWinAnimator.applyAnimationLocked(transit, false)) { + if (allowExitAnimation && mWinAnimator.applyAnimationLocked(transit, false)) { ProtoLog.v(WM_DEBUG_ANIM, "Set animatingExit: reason=remove/applyAnimation win=%s", this); mAnimatingExit = true; @@ -2624,7 +2630,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWmService.mAccessibilityController.onWindowTransition(this, transit); } } - final boolean isAnimating = mAnimatingExit || isExitAnimationRunningSelfOrParent(); + final boolean isAnimating = allowExitAnimation + && (mAnimatingExit || isExitAnimationRunningSelfOrParent()); final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null && mActivityRecord.isLastWindow(this); // We delay the removal of a window if it has a showing surface that can be used to run @@ -3602,6 +3609,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mAnimatingExit = false; ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=destroySurface win=%s", this); + // Clear the flag so the buffer requested for the next new surface won't be dropped by + // mistaking the surface size needs to update. + mReportOrientationChanged = false; + if (useBLASTSync()) { immediatelyNotifyBlastSync(); } @@ -5284,12 +5295,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mControllableInsetProvider != null) { return; } - if (getDisplayContent().inTransition()) { - // Skip because the animation is usually unnoticeable (e.g. covered by rotation - // animation) and the animation bounds could be inconsistent, such as depending - // on when the window applies its draw transaction with new rotation. - return; - } final DisplayInfo displayInfo = getDisplayInfo(); anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(), diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index c9523ec16b8f..529def3697cd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -27,6 +27,7 @@ import static android.app.AlarmManager.FLAG_STANDALONE; import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE; import static android.app.AlarmManager.RTC; import static android.app.AlarmManager.RTC_WAKEUP; +import static android.app.AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT; import static android.app.AlarmManager.WINDOW_EXACT; import static android.app.AlarmManager.WINDOW_HEURISTIC; import static android.app.AppOpsManager.MODE_ALLOWED; @@ -122,6 +123,7 @@ import android.app.IAlarmListener; import android.app.IAlarmManager; import android.app.PendingIntent; import android.app.compat.CompatChanges; +import android.app.role.RoleManager; import android.app.usage.UsageStatsManagerInternal; import android.content.ContentResolver; import android.content.Context; @@ -184,6 +186,7 @@ import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -232,6 +235,8 @@ public class AlarmManagerServiceTest { @Mock private PackageManagerInternal mPackageManagerInternal; @Mock + private RoleManager mRoleManager; + @Mock private AppStateTrackerImpl mAppStateTracker; @Mock private AlarmManagerService.ClockReceiver mClockReceiver; @@ -457,6 +462,7 @@ public class AlarmManagerServiceTest { when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); when(mMockContext.getSystemService(BatteryManager.class)).thenReturn(mBatteryManager); + when(mMockContext.getSystemService(RoleManager.class)).thenReturn(mRoleManager); registerAppIds(new String[]{TEST_CALLING_PACKAGE}, new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)}); @@ -3191,6 +3197,70 @@ public class AlarmManagerServiceTest { } @Test + public void isScheduleExactAlarmAllowedByDefault() { + final String package1 = "priv"; + final String package2 = "signed"; + final String package3 = "normal"; + final String package4 = "wellbeing"; + final int uid1 = 1294; + final int uid2 = 8321; + final int uid3 = 3412; + final int uid4 = 4591; + + when(mPackageManagerInternal.isUidPrivileged(uid1)).thenReturn(true); + when(mPackageManagerInternal.isUidPrivileged(uid2)).thenReturn(false); + when(mPackageManagerInternal.isUidPrivileged(uid3)).thenReturn(false); + when(mPackageManagerInternal.isUidPrivileged(uid4)).thenReturn(false); + + when(mPackageManagerInternal.isPlatformSigned(package1)).thenReturn(false); + when(mPackageManagerInternal.isPlatformSigned(package2)).thenReturn(true); + when(mPackageManagerInternal.isPlatformSigned(package3)).thenReturn(false); + when(mPackageManagerInternal.isPlatformSigned(package4)).thenReturn(false); + + when(mRoleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_WELLBEING)).thenReturn( + Arrays.asList(package4)); + + mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true); + mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = false; + mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] { + package1, + package3, + }); + + // Deny listed packages will be false. + assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2)); + assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4)); + + mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, false); + mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = true; + mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] { + package1, + package3, + }); + + // Same as above, deny listed packages will be false. + assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2)); + assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4)); + + mockChangeEnabled(SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, true); + mService.mConstants.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT = true; + mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[] { + package1, + package3, + }); + + // Deny list doesn't matter now, only exemptions should be true. + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package1, uid1)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package2, uid2)); + assertFalse(mService.isScheduleExactAlarmAllowedByDefault(package3, uid3)); + assertTrue(mService.isScheduleExactAlarmAllowedByDefault(package4, uid4)); + } + + @Test public void alarmScheduledAtomPushed() { for (int i = 0; i < 10; i++) { final PendingIntent pi = getNewMockPendingIntent(); diff --git a/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml b/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml index 0c531de1827e..a4558acdc274 100644 --- a/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml +++ b/services/tests/servicestests/res/xml/test_account_type1_authenticator.xml @@ -18,4 +18,5 @@ android:accountType="@string/test_account_type1" android:icon="@drawable/icon1" android:smallIcon="@drawable/icon1" + android:customTokens="true" android:label="@string/test_account_type1_authenticator_label" /> diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 997a138cf58f..d5c5745d6680 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -26,9 +26,11 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.never; import static org.mockito.Mockito.nullable; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.accounts.AbstractAccountAuthenticator; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerInternal; @@ -1698,13 +1700,14 @@ public class AccountManagerServiceTest extends AndroidTestCase { final CountDownLatch latch = new CountDownLatch(1); Response response = new Response(latch, mMockAccountManagerResponse); + long expiryEpochTimeInMillis = System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND; mAms.getAuthToken( response, // response AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "authTokenType", // authTokenType true, // notifyOnAuthFailure false, // expectActivityLaunch - createGetAuthTokenOptions()); + createGetAuthTokenOptionsWithExpiry(expiryEpochTimeInMillis)); waitForLatch(latch); verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture()); @@ -1715,6 +1718,58 @@ public class AccountManagerServiceTest extends AndroidTestCase { AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS); assertEquals(result.getString(AccountManager.KEY_ACCOUNT_TYPE), AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1); + assertEquals(result.getLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY), + expiryEpochTimeInMillis); + } + + @SmallTest + public void testGetAuthTokenCachedSuccess() throws Exception { + unlockSystemUser(); + when(mMockContext.createPackageContextAsUser( + anyString(), anyInt(), any(UserHandle.class))).thenReturn(mMockContext); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + String[] list = new String[]{AccountManagerServiceTestFixtures.CALLER_PACKAGE}; + when(mMockPackageManager.getPackagesForUid(anyInt())).thenReturn(list); + + final CountDownLatch latch = new CountDownLatch(1); + Response response = new Response(latch, mMockAccountManagerResponse); + long expiryEpochTimeInMillis = System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND; + mAms.getAuthToken( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, + "authTokenType", // authTokenType + true, // notifyOnAuthFailure + false, // expectActivityLaunch + createGetAuthTokenOptionsWithExpiry(expiryEpochTimeInMillis)); + waitForLatch(latch); + + // Make call for cached token. + mAms.getAuthToken( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, + "authTokenType", // authTokenType + true, // notifyOnAuthFailure + false, // expectActivityLaunch + createGetAuthTokenOptionsWithExpiry(expiryEpochTimeInMillis + 10)); + waitForLatch(latch); + + verify(mMockAccountManagerResponse, times(2)).onResult(mBundleCaptor.capture()); + List<Bundle> result = mBundleCaptor.getAllValues(); + assertGetTokenResponse(result.get(0), expiryEpochTimeInMillis); + // cached token was returned with the same expiration time as first token. + assertGetTokenResponse(result.get(1), expiryEpochTimeInMillis); + } + + private void assertGetTokenResponse(Bundle result, long expiryEpochTimeInMillis) { + assertEquals(result.getString(AccountManager.KEY_AUTHTOKEN), + AccountManagerServiceTestFixtures.AUTH_TOKEN); + assertEquals(result.getString(AccountManager.KEY_ACCOUNT_NAME), + AccountManagerServiceTestFixtures.ACCOUNT_NAME_SUCCESS); + assertEquals(result.getString(AccountManager.KEY_ACCOUNT_TYPE), + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1); + assertEquals(result.getLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY), + expiryEpochTimeInMillis); + } @SmallTest @@ -3241,11 +3296,16 @@ public class AccountManagerServiceTest extends AndroidTestCase { } private Bundle createGetAuthTokenOptions() { + return createGetAuthTokenOptionsWithExpiry( + System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND); + } + + private Bundle createGetAuthTokenOptionsWithExpiry(long expiryEpochTimeInMillis) { Bundle options = new Bundle(); options.putString(AccountManager.KEY_ANDROID_PACKAGE_NAME, AccountManagerServiceTestFixtures.CALLER_PACKAGE); options.putLong(AccountManagerServiceTestFixtures.KEY_TOKEN_EXPIRY, - System.currentTimeMillis() + ONE_DAY_IN_MILLISECOND); + expiryEpochTimeInMillis); return options; } diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java new file mode 100644 index 000000000000..049c745fc128 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/backup/restore/FullRestoreEngineTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.restore; + +import static com.google.common.truth.Truth.assertWithMessage; + +import android.app.backup.BackupAgent; +import android.platform.test.annotations.Presubmit; +import android.system.OsConstants; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.backup.FileMetadata; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@RunWith(AndroidJUnit4.class) +public class FullRestoreEngineTest { + private static final String DEFAULT_PACKAGE_NAME = "package"; + private static final String DEFAULT_DOMAIN_NAME = "domain"; + private static final String NEW_PACKAGE_NAME = "new_package"; + private static final String NEW_DOMAIN_NAME = "new_domain"; + + private FullRestoreEngine mRestoreEngine; + + @Before + public void setUp() { + mRestoreEngine = new FullRestoreEngine(); + } + + @Test + public void shouldSkipReadOnlyDir_skipsAllReadonlyDirsAndTheirChildren() { + // Create the file tree. + TestFile[] testFiles = new TestFile[] { + TestFile.dir("root"), + TestFile.file("root/auth_token"), + TestFile.dir("root/media"), + TestFile.file("root/media/picture1.png"), + TestFile.file("root/push_token.txt"), + TestFile.dir("root/read-only-dir-1").markReadOnly().expectSkipped(), + TestFile.dir("root/read-only-dir-1/writable-subdir").expectSkipped(), + TestFile.file("root/read-only-dir-1/writable-subdir/writable-file").expectSkipped(), + TestFile.dir("root/read-only-dir-1/writable-subdir/read-only-subdir-2") + .markReadOnly().expectSkipped(), + TestFile.file("root/read-only-dir-1/writable-file").expectSkipped(), + TestFile.file("root/random-stuff.txt"), + TestFile.dir("root/database"), + TestFile.file("root/database/users.db"), + TestFile.dir("root/read-only-dir-2").markReadOnly().expectSkipped(), + TestFile.file("root/read-only-dir-2/writable-file-1").expectSkipped(), + TestFile.file("root/read-only-dir-2/writable-file-2").expectSkipped(), + }; + + assertCorrectItemsAreSkipped(testFiles); + } + + @Test + public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSamePackage() { + TestFile[] testFiles = new TestFile[]{ + TestFile.dir("read-only-dir").markReadOnly().expectSkipped(), + TestFile.file("read-only-dir/file").expectSkipped(), + TestFile.file("read-only-dir/file-from-different-package") + .setPackage(NEW_PACKAGE_NAME), + }; + + assertCorrectItemsAreSkipped(testFiles); + } + + @Test + public void shouldSkipReadOnlyDir_onlySkipsChildrenUnderTheSameDomain() { + TestFile[] testFiles = new TestFile[]{ + TestFile.dir("read-only-dir").markReadOnly().expectSkipped(), + TestFile.file("read-only-dir/file").expectSkipped(), + TestFile.file("read-only-dir/file-from-different-domain") + .setDomain(NEW_DOMAIN_NAME), + }; + + assertCorrectItemsAreSkipped(testFiles); + } + + private void assertCorrectItemsAreSkipped(TestFile[] testFiles) { + // Verify all directories marked with .expectSkipped are skipped. + for (TestFile testFile : testFiles) { + boolean actualExcluded = mRestoreEngine.shouldSkipReadOnlyDir(testFile.mMetadata); + boolean expectedExcluded = testFile.mShouldSkip; + assertWithMessage(testFile.mMetadata.path).that(actualExcluded).isEqualTo( + expectedExcluded); + } + } + + private static class TestFile { + private final FileMetadata mMetadata; + private boolean mShouldSkip; + + static TestFile dir(String path) { + return new TestFile(path, BackupAgent.TYPE_DIRECTORY); + } + + static TestFile file(String path) { + return new TestFile(path, BackupAgent.TYPE_FILE); + } + + TestFile markReadOnly() { + mMetadata.mode = 0; + return this; + } + + TestFile expectSkipped() { + mShouldSkip = true; + return this; + } + + TestFile setPackage(String packageName) { + mMetadata.packageName = packageName; + return this; + } + + TestFile setDomain(String domain) { + mMetadata.domain = domain; + return this; + } + + private TestFile(String path, int type) { + FileMetadata metadata = new FileMetadata(); + metadata.path = path; + metadata.type = type; + metadata.packageName = DEFAULT_PACKAGE_NAME; + metadata.domain = DEFAULT_DOMAIN_NAME; + metadata.mode = OsConstants.S_IWUSR; // Mark as writable. + mMetadata = metadata; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java index 33ea7108a705..b9ae6702c37e 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java @@ -25,6 +25,8 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; + import static junit.framework.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyInt; @@ -48,6 +50,7 @@ import android.os.Looper; import android.os.Process; import android.os.ResultReceiver; import android.os.ShellCallback; +import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; @@ -370,6 +373,19 @@ public class LockSettingsShellCommandTest { mUserId); } + @Test + public void testRequireStrongAuth_STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN() throws Exception { + when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true); + + assertEquals(0, mCommand.exec(new Binder(), in, out, err, + new String[] { "require-strong-auth", "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN"}, + mShellCallback, mResultReceiver)); + + verify(mLockPatternUtils).requireStrongAuth( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, + UserHandle.USER_ALL); + } + private List<LockPatternView.Cell> stringToPattern(String str) { return LockPatternUtils.byteArrayToPattern(str.getBytes()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 9902e83c3648..908de34352c9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -1139,18 +1139,8 @@ public class ActivityStarterTests extends WindowTestsBase { true /* createdByOrganizer */); sourceRecord.getTask().addChild(taskFragment, POSITION_TOP); - starter.startActivityInner( - /* r */targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */true, - /* options */null, - /* inTask */null, - /* inTaskFragment */ taskFragment, - /* restrictedBgActivity */false, - /* intentGrants */null); + startActivityInner(starter, targetRecord, sourceRecord, null /* options */, + null /* inTask */, taskFragment); assertFalse(taskFragment.hasChild()); } @@ -1167,18 +1157,8 @@ public class ActivityStarterTests extends WindowTestsBase { taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class), SYSTEM_UID, "system_uid"); - starter.startActivityInner( - /* r */targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */true, - /* options */null, - /* inTask */null, - /* inTaskFragment */ taskFragment, - /* restrictedBgActivity */false, - /* intentGrants */null); + startActivityInner(starter, targetRecord, sourceRecord, null /* options */, + null /* inTask */, taskFragment); assertTrue(taskFragment.hasChild()); } @@ -1195,18 +1175,8 @@ public class ActivityStarterTests extends WindowTestsBase { taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class), targetRecord.getUid(), "test_process_name"); - starter.startActivityInner( - /* r */targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */true, - /* options */null, - /* inTask */null, - /* inTaskFragment */ taskFragment, - /* restrictedBgActivity */false, - /* intentGrants */null); + startActivityInner(starter, targetRecord, sourceRecord, null /* options */, + null /* inTask */, taskFragment); assertTrue(taskFragment.hasChild()); } @@ -1231,18 +1201,8 @@ public class ActivityStarterTests extends WindowTestsBase { doReturn(true).when(signingDetails).hasAncestorOrSelfWithDigest(any()); doReturn(signingDetails).when(androidPackage).getSigningDetails(); - starter.startActivityInner( - /* r */targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */true, - /* options */null, - /* inTask */null, - /* inTaskFragment */ taskFragment, - /* restrictedBgActivity */false, - /* intentGrants */null); + startActivityInner(starter, targetRecord, sourceRecord, null /* options */, + null /* inTask */, taskFragment); assertTrue(taskFragment.hasChild()); } @@ -1258,23 +1218,30 @@ public class ActivityStarterTests extends WindowTestsBase { targetRecord.info.flags |= ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING; - starter.startActivityInner( - /* r */targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */true, - /* options */null, - /* inTask */null, - /* inTaskFragment */ taskFragment, - /* restrictedBgActivity */false, - /* intentGrants */null); + startActivityInner(starter, targetRecord, sourceRecord, null /* options */, + null /* inTask */, taskFragment); assertTrue(taskFragment.hasChild()); } @Test + public void testStartActivityInner_inTask() { + final ActivityStarter starter = prepareStarter(0, false); + // Simulate an app uses AppTask to create a non-attached task, and then it requests to + // start activity in the task. + final Task inTask = new TaskBuilder(mSupervisor).setTaskDisplayArea(null).setTaskId(123) + .build(); + inTask.inRecents = true; + assertFalse(inTask.isAttached()); + final ActivityRecord target = new ActivityBuilder(mAtm).build(); + startActivityInner(starter, target, null /* source */, null /* options */, inTask, + null /* inTaskFragment */); + + assertTrue(inTask.isAttached()); + assertEquals(inTask, target.getTask()); + } + + @Test public void testLaunchCookie_newAndExistingTask() { final ActivityStarter starter = prepareStarter(0, false); @@ -1322,21 +1289,20 @@ public class ActivityStarterTests extends WindowTestsBase { // Start the target launch-into-pip activity from a source final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build(); - starter.startActivityInner( - /* r */ targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */ null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */ true, - /* options */ opts, - /* inTask */ null, - /* inTaskFragment */ null, - /* restrictedBgActivity */ false, - /* intentGrants */ null); + startActivityInner(starter, targetRecord, sourceRecord, opts, + null /* inTask */, null /* inTaskFragment */); // Verify the ActivityRecord#getLaunchIntoPipHostActivity points to sourceRecord. assertThat(targetRecord.getLaunchIntoPipHostActivity()).isNotNull(); assertEquals(targetRecord.getLaunchIntoPipHostActivity(), sourceRecord); } + + private static void startActivityInner(ActivityStarter starter, ActivityRecord target, + ActivityRecord source, ActivityOptions options, Task inTask, + TaskFragment inTaskFragment) { + starter.startActivityInner(target, source, null /* voiceSession */, + null /* voiceInteractor */, 0 /* startFlags */, true /* doResume */, + options, inTask, inTaskFragment, false /* restrictedBgActivity */, + null /* intentGrants */); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java index f9689990c5e8..a82826006f17 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerHelperTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityManager.START_ABORTED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; @@ -26,6 +27,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; import android.app.WindowConfiguration; import android.content.ComponentName; @@ -45,13 +47,13 @@ import java.util.List; import java.util.Set; /** - * Tests for the {@link DisplayWindowPolicyControllerHelper} class. + * Tests for the {@link DisplayWindowPolicyController} class. * * Build/Install/Run: - * atest WmTests:DisplayWindowPolicyControllerHelperTests + * atest WmTests:DisplayWindowPolicyControllerTests */ @RunWith(WindowTestRunner.class) -public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase { +public class DisplayWindowPolicyControllerTests extends WindowTestsBase { private static final int TEST_USER_0_ID = 0; private static final int TEST_USER_1_ID = 10; @@ -152,8 +154,51 @@ public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase { assertTrue(mSecondaryDisplay.mDwpcHelper.isWindowingModeSupported(WINDOWING_MODE_PINNED)); } + @Test + public void testInterestedWindowFlags() { + final int fakeFlag1 = 0x00000010; + final int fakeFlag2 = 0x00000100; + final int fakeSystemFlag1 = 0x00000010; + final int fakeSystemFlag2 = 0x00000100; + + mDwpc.setInterestedWindowFlags(fakeFlag1, fakeSystemFlag1); + + assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag1, fakeSystemFlag1)); + assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag1, fakeSystemFlag2)); + assertTrue(mDwpc.isInterestedWindowFlags(fakeFlag2, fakeSystemFlag1)); + assertFalse(mDwpc.isInterestedWindowFlags(fakeFlag2, fakeSystemFlag2)); + } + + @Test + public void testCanContainActivities() { + ActivityStarter starter = new ActivityStarter(mock(ActivityStartController.class), mAtm, + mSupervisor, mock(ActivityStartInterceptor.class)); + final Task task = new TaskBuilder(mSupervisor).setDisplay(mSecondaryDisplay).build(); + final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord disallowedRecord = + new ActivityBuilder(mAtm).setComponent(mDwpc.DISALLOWED_ACTIVITY).build(); + + int result = starter.startActivityInner( + disallowedRecord, + sourceRecord, + /* voiceSession */null, + /* voiceInteractor */ null, + /* startFlags */ 0, + /* doResume */true, + /* options */null, + /* inTask */null, + /* inTaskFragment */ null, + /* restrictedBgActivity */false, + /* intentGrants */null); + + assertEquals(result, START_ABORTED); + } + private class TestDisplayWindowPolicyController extends DisplayWindowPolicyController { + public ComponentName DISALLOWED_ACTIVITY = + new ComponentName("fake.package", "DisallowedActivity"); + ComponentName mTopActivity = null; int mTopActivityUid = UserHandle.USER_NULL; ArraySet<Integer> mRunningUids = new ArraySet<>(); @@ -161,7 +206,14 @@ public class DisplayWindowPolicyControllerHelperTests extends WindowTestsBase { @Override public boolean canContainActivities(@NonNull List<ActivityInfo> activities, @WindowConfiguration.WindowingMode int windowingMode) { - return false; + final int activityCount = activities.size(); + for (int i = 0; i < activityCount; i++) { + final ActivityInfo aInfo = activities.get(i); + if (aInfo.getComponentName().equals(DISALLOWED_ACTIVITY)) { + return false; + } + } + return true; } @Override diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 8d7fab4d101f..a2266fb3a929 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1959,6 +1959,13 @@ public class CarrierConfigManager { "nr_advanced_threshold_bandwidth_khz_int"; /** + * Boolean indicating if operator name should be shown in the status bar + * @hide + */ + public static final String KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL = + "show_operator_name_in_statusbar_bool"; + + /** * The string is used to filter redundant string from PLMN Network Name that's supplied by * specific carrier. * @@ -8913,6 +8920,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY, null); sDefaults.putBoolean(KEY_WORLD_MODE_ENABLED_BOOL, false); sDefaults.putString(KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING, ""); + sDefaults.putBoolean(KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false); sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false); sDefaults.putBoolean(KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL, false); diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java index 3843a6240b43..249f740c482b 100644 --- a/telephony/java/android/telephony/UiccCardInfo.java +++ b/telephony/java/android/telephony/UiccCardInfo.java @@ -21,6 +21,9 @@ import android.content.pm.PackageManager; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.telephony.util.TelephonyUtils; +import com.android.telephony.Rlog; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -227,7 +230,6 @@ public final class UiccCardInfo implements Parcelable { this.mIccIdAccessRestricted = iccIdAccessRestricted; } - @Override public boolean equals(Object obj) { if (this == obj) { @@ -261,7 +263,7 @@ public final class UiccCardInfo implements Parcelable { + ", mCardId=" + mCardId + ", mEid=" - + mEid + + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid) + ", mPhysicalSlotIndex=" + mPhysicalSlotIndex + ", mIsRemovable=" diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java index 17ce45063d41..dd3639a9a7b9 100644 --- a/telephony/java/android/telephony/UiccSlotInfo.java +++ b/telephony/java/android/telephony/UiccSlotInfo.java @@ -279,7 +279,7 @@ public class UiccSlotInfo implements Parcelable { + ", mIsEuicc=" + mIsEuicc + ", mCardId=" - + mCardId + + SubscriptionInfo.givePrintableIccid(mCardId) + ", cardState=" + mCardStateInfo + ", mIsExtendedApduSupported=" diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt new file mode 100644 index 000000000000..172c4330c3c6 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.helpers + +import android.app.Instrumentation +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.server.wm.traces.common.FlickerComponentName +import com.android.server.wm.traces.parser.toFlickerComponent +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper + +class ImeEditorPopupDialogAppHelper @JvmOverloads constructor( + instr: Instrumentation, + private val rotation: Int, + private val imePackageName: String = IME_PACKAGE, + launcherName: String = ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME, + component: FlickerComponentName = + ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME.toFlickerComponent() +) : ImeAppHelper(instr, launcherName, component) { + override fun openIME( + device: UiDevice, + wmHelper: WindowManagerStateHelper? + ) { + val editText = device.wait(Until.findObject(By.text("focused editText")), FIND_TIMEOUT) + + require(editText != null) { + "Text field not found, this usually happens when the device " + + "was left in an unknown state (e.g. in split screen)" + } + editText.click() + waitIMEShown(device, wmHelper) + } + + fun dismissDialog(wmHelper: WindowManagerStateHelper) { + val dismissButton = uiDevice.wait( + Until.findObject(By.text("Dismiss")), FIND_TIMEOUT) + + // Pressing back key to dismiss the dialog + if (dismissButton != null) { + dismissButton.click() + wmHelper.waitForAppTransitionIdle() + } + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt new file mode 100644 index 000000000000..bff099e2e7a1 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.ime + +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit +import android.view.Surface +import android.view.WindowManagerPolicyConstants +import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.annotation.Group4 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper +import com.android.server.wm.flicker.navBarWindowIsVisible +import com.android.server.wm.flicker.statusBarWindowIsVisible +import com.android.server.wm.flicker.traces.region.RegionSubject +import com.android.server.wm.traces.common.FlickerComponentName +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group4 +class CloseImeEditorPopupDialogTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation, testSpec.startRotation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + setup { + eachRun { + imeTestApp.launchViaIntent(wmHelper) + imeTestApp.openIME(device, wmHelper) + } + } + transitions { + imeTestApp.dismissDialog(wmHelper) + instrumentation.uiAutomation.syncInputTransactions() + } + teardown { + eachRun { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + imeTestApp.exit() + } + } + } + } + + @Postsubmit + @Test + fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() + + @Postsubmit + @Test + fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() + + @Postsubmit + @Test + fun imeWindowBecameInvisible() = testSpec.imeWindowBecomesInvisible() + + @Postsubmit + @Test + fun imeLayerAndImeSnapshotVisibleOnScreen() { + testSpec.assertLayers { + this.isVisible(FlickerComponentName.IME) + .then() + .isVisible(FlickerComponentName.IME_SNAPSHOT) + .then() + .isInvisible(FlickerComponentName.IME) + } + } + + @Postsubmit + @Test + fun imeSnapshotAssociatedOnAppVisibleRegion() { + testSpec.assertLayers { + this.invoke("imeSnapshotAssociatedOnAppVisibleRegion") { + val imeSnapshotLayers = it.subjects.filter { + subject -> subject.name.contains( + FlickerComponentName.IME_SNAPSHOT.toLayerName()) && subject.isVisible + } + if (imeSnapshotLayers.isNotEmpty()) { + val visibleAreas = imeSnapshotLayers.mapNotNull { imeSnapshotLayer -> + imeSnapshotLayer.layer?.visibleRegion }.toTypedArray() + val imeVisibleRegion = RegionSubject.assertThat(visibleAreas, this, timestamp) + val appVisibleRegion = it.visibleRegion(imeTestApp.component) + if (imeVisibleRegion.region.isNotEmpty) { + imeVisibleRegion.coversAtMost(appVisibleRegion.region) + } + } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + repetitions = 2, + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ), + supportedRotations = listOf(Surface.ROTATION_0) + ) + } + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt index 7f4966375b98..1b60403ac354 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt @@ -79,7 +79,7 @@ class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestPar /** * Checks that [FlickerComponentName.IME] layer is visible at the end of the transition */ - @Presubmit + @FlakyTest(bugId = 227142436) @Test fun imeLayerExistsEnd() { testSpec.assertLayersEnd { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt index 9c9dedc23ff9..4b8a8c80cd45 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt @@ -17,6 +17,7 @@ package com.android.server.wm.flicker.quickswitch import android.platform.test.annotations.RequiresDevice +import androidx.test.filters.FlakyTest import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.annotation.Group1 @@ -44,6 +45,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group1 +@FlakyTest(bugId = 228009808) open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit(testSpec: FlickerTestParameter) : QuickSwitchBetweenTwoAppsForwardTest(testSpec) { @Before diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 739fe020d555..7f513b21957f 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -119,5 +119,16 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + <activity android:name=".ImeEditorPopupDialogActivity" + android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity" + android:configChanges="orientation|screenSize" + android:theme="@style/CutoutShortEdges" + android:label="ImeEditorPopupDialogActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> </application> </manifest> diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java index 3040a09f2345..18c95cf7bbff 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java @@ -56,6 +56,7 @@ public class ActivityOptions { public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME = new ComponentName(FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity"); + public static final String DIALOG_THEMED_ACTIVITY = "DialogThemedActivity"; public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME = new ComponentName(FLICKER_APP_PACKAGE, @@ -65,4 +66,10 @@ public class ActivityOptions { public static final ComponentName PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME = new ComponentName(FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".PortraitOnlyActivity"); + + public static final String EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME = + "ImeEditorPopupDialogActivity"; + public static final ComponentName EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME = + new ComponentName(FLICKER_APP_PACKAGE, + FLICKER_APP_PACKAGE + ".ImeEditorPopupDialogActivity"); } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java new file mode 100644 index 000000000000..a8613f531e1c --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.testapp; + +import android.app.Activity; +import android.app.AlertDialog; +import android.os.Bundle; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.LinearLayout; + +public class ImeEditorPopupDialogActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + WindowManager.LayoutParams p = getWindow().getAttributes(); + p.layoutInDisplayCutoutMode = WindowManager.LayoutParams + .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + getWindow().setAttributes(p); + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + setContentView(R.layout.activity_simple); + + final EditText editText = new EditText(this); + editText.setHint("focused editText"); + final AlertDialog dialog = new AlertDialog.Builder(this) + .setView(editText) + .setPositiveButton("Dismiss", (d, which) -> d.dismiss()) + .create(); + dialog.show(); + } +} diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index bd0a4bc44e18..bfb32854a374 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -165,6 +165,7 @@ cc_library_host_static { ], proto: { export_proto_headers: true, + type: "full", }, defaults: ["aapt2_defaults"], } |