From bd7aba8cbf44be345d087db80f15f73ef894a0f4 Mon Sep 17 00:00:00 2001
From: Kevin Hufnagle
Date: Tue, 24 Nov 2020 18:30:38 -0500
Subject: [PATCH 001/288] docs: Remove error in requestPermissions doc
If an app has already been granted a permission and it requests that
permission again, the user shouldn't see a system dialog. Update the
documentation to reflect this intended behavior.
Test: m ds-docs-java
Bug: 157530031
Exempt-From-Owner-Approval: Docs-only change
Change-Id: I7096e17480831324dee72f4093cdaa064e3b9165
---
core/java/android/app/Activity.java | 6 ------
1 file changed, 6 deletions(-)
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3772755beca18..1265ce8650e1e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5183,12 +5183,6 @@ public class Activity extends ContextThemeWrapper
* #checkSelfPermission(String)}.
*
*
- * Calling this API for permissions already granted to your app would show UI
- * to the user to decide whether the app can still hold these permissions. This
- * can be useful if the way your app uses data guarded by the permissions
- * changes significantly.
- *
- *
* You cannot request a permission if your activity sets {@link
* android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
* true because in this case the activity would not receive
From 09080dc177288035c4694690a0f2dcd752acb3ba Mon Sep 17 00:00:00 2001
From: Nate Myren
Date: Mon, 30 Nov 2020 12:06:26 -0800
Subject: [PATCH 002/288] Revoke storage on SDK downgrade or new full storage
request
When an app downgrades from above API 28 to below, or when an app begins
requesting full storage in api 29, revoke storage permissions
Test: Atest StorageEscalationTest
Bug: 171430330
Merged-In: Ia6bd5658b77a26ab15fea013ec875050457473e0
Change-Id: Ia6bd5658b77a26ab15fea013ec875050457473e0
---
.../server/pm/PackageManagerService.java | 1 +
.../permission/PermissionManagerService.java | 62 +++++++++++++++++++
.../PermissionManagerServiceInternal.java | 11 ++++
3 files changed, 74 insertions(+)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e8b2f3c428bce..bb7f17d4524a4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -12486,6 +12486,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (hasOldPkg) {
mPermissionManager.revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg,
allPackageNames);
+ mPermissionManager.revokeStoragePermissionsIfScopeExpanded(pkg, oldPkg);
}
if (hasPermissionDefinitionChanges) {
mPermissionManager.revokeRuntimePermissionsIfPermissionDefinitionChanged(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d864cfb75d51a..7f0796fc99ac0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -206,6 +206,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private static final int USER_PERMISSION_FLAGS = FLAG_PERMISSION_USER_SET
| FLAG_PERMISSION_USER_FIXED;
+ /** All storage permissions */
+ private static final List STORAGE_PERMISSIONS = new ArrayList<>();
+
/** If the permission of the value is granted, so is the key */
private static final Map FULLER_PERMISSION_MAP = new HashMap<>();
@@ -214,6 +217,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
Manifest.permission.ACCESS_FINE_LOCATION);
FULLER_PERMISSION_MAP.put(Manifest.permission.INTERACT_ACROSS_USERS,
Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+ STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
}
/** Lock to protect internal data access */
@@ -2265,6 +2271,49 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return protectionLevel;
}
+ /**
+ * If the app is updated, and has scoped storage permissions, then it is possible that the
+ * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
+ * @param newPackage The new package that was installed
+ * @param oldPackage The old package that was updated
+ */
+ private void revokeStoragePermissionsIfScopeExpanded(
+ @NonNull AndroidPackage newPackage,
+ @NonNull AndroidPackage oldPackage,
+ @NonNull PermissionCallback permissionCallback) {
+ boolean downgradedSdk = oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q
+ && newPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q;
+ boolean upgradedSdk = oldPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q
+ && newPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q;
+ boolean newlyRequestsLegacy = !upgradedSdk && !oldPackage.isRequestLegacyExternalStorage()
+ && newPackage.isRequestLegacyExternalStorage();
+
+ if (!newlyRequestsLegacy && !downgradedSdk) {
+ return;
+ }
+
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(newPackage.getUid());
+ int numRequestedPermissions = newPackage.getRequestedPermissions().size();
+ for (int i = 0; i < numRequestedPermissions; i++) {
+ PermissionInfo permInfo = getPermissionInfo(newPackage.getRequestedPermissions().get(i),
+ newPackage.getPackageName(), 0);
+ if (permInfo == null || !STORAGE_PERMISSIONS.contains(permInfo.name)) {
+ continue;
+ }
+
+ EventLog.writeEvent(0x534e4554, "171430330", newPackage.getUid(),
+ "Revoking permission " + permInfo.name + " from package "
+ + newPackage.getPackageName() + " as either the sdk downgraded "
+ + downgradedSdk + " or newly requested legacy full storage "
+ + newlyRequestsLegacy);
+
+ revokeRuntimePermissionInternal(permInfo.name, newPackage.getPackageName(),
+ false, callingUid, userId, null, permissionCallback);
+ }
+
+ }
+
/**
* We might auto-grant permissions if any permission of the group is already granted. Hence if
* the group of a granted permission changes we need to revoke it to avoid having permissions of
@@ -4726,6 +4775,19 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@UserIdInt int userId) {
return PermissionManagerService.this.isPermissionsReviewRequired(pkg, userId);
}
+ /**
+ * If the app is updated, and has scoped storage permissions, then it is possible that the
+ * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
+ * @param newPackage The new package that was installed
+ * @param oldPackage The old package that was updated
+ */
+ public void revokeStoragePermissionsIfScopeExpanded(
+ @NonNull AndroidPackage newPackage,
+ @NonNull AndroidPackage oldPackage
+ ) {
+ PermissionManagerService.this.revokeStoragePermissionsIfScopeExpanded(newPackage,
+ oldPackage, mDefaultPermissionCallback);
+ }
@Override
public void revokeRuntimePermissionsIfGroupChanged(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 31a65ba35f5eb..df0edfa16924b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -265,6 +265,17 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
@NonNull List permissionsToRevoke,
@NonNull ArrayList allPackageNames);
+ /**
+ * If the app is updated, and has scoped storage permissions, then it is possible that the
+ * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
+ * @param newPackage The new package that was installed
+ * @param oldPackage The old package that was updated
+ */
+ public abstract void revokeStoragePermissionsIfScopeExpanded(
+ @NonNull AndroidPackage newPackage,
+ @NonNull AndroidPackage oldPackage
+ );
+
/**
* Add all permissions in the given package.
*
From 10f79644fc7db512daa6dcf71685dc4f71f8f11b Mon Sep 17 00:00:00 2001
From: arangelov
Date: Fri, 30 Oct 2020 20:37:40 +0000
Subject: [PATCH 003/288] [RESTRICT AUTOMERGE] Remove DPMS#getProfileOwner in
favor of getProfileOwnerAsUser
Test: atest DevicePolicyManagerTest
Test: atest CrossProfileAppsTest
Bug: 153995973
Change-Id: I103c45ea61814e476eea197d357ef0ce75edefdc
(cherry picked from commit 790348fbf190dc647188fc10d68f6d18cbf06b14)
---
.../app/admin/DevicePolicyManager.java | 2 +-
.../app/admin/IDevicePolicyManager.aidl | 1 -
.../devicepolicy/CertificateMonitor.java | 2 +-
.../DevicePolicyManagerService.java | 30 ++++++++-----------
.../devicepolicy/DevicePolicyManagerTest.java | 10 +++++++
5 files changed, 24 insertions(+), 21 deletions(-)
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 322cac81d58b3..d6056d23699e6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6994,7 +6994,7 @@ public class DevicePolicyManager {
throwIfParentInstance("isProfileOwnerApp");
if (mService != null) {
try {
- ComponentName profileOwner = mService.getProfileOwner(myUserId());
+ ComponentName profileOwner = mService.getProfileOwnerAsUser(myUserId());
return profileOwner != null
&& profileOwner.getPackageName().equals(packageName);
} catch (RemoteException re) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9c6a274ccf8cf..86058d9933df2 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -156,7 +156,6 @@ interface IDevicePolicyManager {
boolean setProfileOwner(in ComponentName who, String ownerName, int userHandle);
ComponentName getProfileOwnerAsUser(int userHandle);
- ComponentName getProfileOwner(int userHandle);
ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent(in UserHandle userHandle);
String getProfileOwnerName(int userHandle);
void setProfileEnabled(in ComponentName who);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
index aa38880d6d7db..a33a512b3be0e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
@@ -177,7 +177,7 @@ public class CertificateMonitor {
int parentUserId = userHandle.getIdentifier();
- if (mService.getProfileOwner(userHandle.getIdentifier()) != null) {
+ if (mService.getProfileOwnerAsUser(userHandle.getIdentifier()) != null) {
contentText = resources.getString(R.string.ssl_ca_cert_noti_managed,
mService.getProfileOwnerName(userHandle.getIdentifier()));
smallIconId = R.drawable.stat_sys_certificate_info;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c6b93d6ca4f48..15cb5bff01d9d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4714,7 +4714,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean isSeparateProfileChallengeAllowed(int userHandle) {
enforceSystemCaller("query separate challenge support");
- ComponentName profileOwner = getProfileOwner(userHandle);
+ ComponentName profileOwner = getProfileOwnerAsUser(userHandle);
// Profile challenge is supported on N or newer release.
return profileOwner != null &&
getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M;
@@ -6622,7 +6622,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final UserHandle caller = mInjector.binderGetCallingUserHandle();
// If there is a profile owner, redirect to that; otherwise query the device owner.
- ComponentName aliasChooser = getProfileOwner(caller.getIdentifier());
+ ComponentName aliasChooser = getProfileOwnerAsUser(caller.getIdentifier());
if (aliasChooser == null && caller.isSystem()) {
synchronized (getLockObject()) {
final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
@@ -8744,7 +8744,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
public boolean isProfileOwner(ComponentName who, int userId) {
- final ComponentName profileOwner = getProfileOwner(userId);
+ final ComponentName profileOwner = getProfileOwnerAsUser(userId);
return who != null && who.equals(profileOwner);
}
@@ -9302,16 +9302,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public ComponentName getProfileOwnerAsUser(int userHandle) {
- enforceCrossUsersPermission(userHandle);
-
- return getProfileOwner(userHandle);
- }
-
- @Override
- public ComponentName getProfileOwner(int userHandle) {
if (!mHasFeature) {
return null;
}
+ enforceCrossUsersPermission(userHandle);
synchronized (getLockObject()) {
return mOwners.getProfileOwnerComponent(userHandle);
}
@@ -9354,9 +9348,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mInjector.binderWithCleanCallingIdentity(() -> {
for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
if (userInfo.isManagedProfile()) {
- if (getProfileOwner(userInfo.id) != null
+ if (getProfileOwnerAsUser(userInfo.id) != null
&& isProfileOwnerOfOrganizationOwnedDevice(userInfo.id)) {
- ComponentName who = getProfileOwner(userInfo.id);
+ ComponentName who = getProfileOwnerAsUser(userInfo.id);
return getActiveAdminUncheckedLocked(who, userInfo.id);
}
}
@@ -9396,7 +9390,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
enforceManageUsers();
- ComponentName profileOwner = getProfileOwner(userHandle);
+ ComponentName profileOwner = getProfileOwnerAsUser(userHandle);
if (profileOwner == null) {
return null;
}
@@ -9793,7 +9787,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
- final ComponentName profileOwner = getProfileOwner(userId);
+ final ComponentName profileOwner = getProfileOwnerAsUser(userId);
if (profileOwner == null) {
return false;
}
@@ -13368,7 +13362,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Managed-profiles cannot be setup on the system user.
return CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
}
- if (getProfileOwner(callingUserId) != null) {
+ if (getProfileOwnerAsUser(callingUserId) != null) {
// Managed user cannot have a managed profile.
return CODE_USER_HAS_PROFILE_OWNER;
}
@@ -14022,7 +14016,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return true;
}
- final ComponentName profileOwner = getProfileOwner(userId);
+ final ComponentName profileOwner = getProfileOwnerAsUser(userId);
if (profileOwner == null) {
return false;
}
@@ -14232,7 +14226,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
enforceUserUnlocked(userId);
- final ComponentName profileOwner = getProfileOwner(userId);
+ final ComponentName profileOwner = getProfileOwnerAsUser(userId);
if (profileOwner != null && packageName.equals(profileOwner.getPackageName())) {
throw new IllegalArgumentException("Cannot uninstall a package with a profile owner");
}
@@ -15768,7 +15762,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final List admins = new ArrayList<>();
int[] users = mUserManager.getProfileIdsWithDisabled(UserHandle.getCallingUserId());
for (int i = 0; i < users.length; i++) {
- final ComponentName componentName = getProfileOwner(users[i]);
+ final ComponentName componentName = getProfileOwnerAsUser(users[i]);
if (componentName != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(componentName, users[i]);
if (admin != null) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index daaabf8141fff..82c23e5051c93 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -666,6 +666,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
*/
public void testRemoveActiveAdmin_fromDifferentUserWithINTERACT_ACROSS_USERS_FULL() {
mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS);
// Add admin1.
@@ -2684,6 +2685,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS);
// Check that the system user is unaffiliated.
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
@@ -4220,6 +4222,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
public void testGetBindDeviceAdminTargetUsers() throws Exception {
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS);
+
// Setup device owner.
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
@@ -6051,6 +6055,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
public void testGetAllCrossProfilePackages_notSet_returnsEmpty() throws Exception {
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS);
addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
mContext.packageName = admin1.getPackageName();
@@ -6062,6 +6067,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
public void testGetAllCrossProfilePackages_notSet_dpmsReinitialized_returnsEmpty()
throws Exception {
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS);
addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
mContext.packageName = admin1.getPackageName();
@@ -6073,6 +6079,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
public void testGetAllCrossProfilePackages_whenSet_returnsCombinedSet() throws Exception {
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS);
addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
final Set packages = Sets.newSet("TEST_PACKAGE", "TEST_COMMON_PACKAGE");
mContext.packageName = admin1.getPackageName();
@@ -6090,6 +6097,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
public void testGetAllCrossProfilePackages_whenSet_dpmsReinitialized_returnsCombinedSet()
throws Exception {
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS);
addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
final Set packages = Sets.newSet("TEST_PACKAGE", "TEST_COMMON_PACKAGE");
mContext.packageName = admin1.getPackageName();
@@ -6261,6 +6269,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
public void testSetAccountTypesWithManagementDisabledOnOrgOwnedManagedProfile()
throws Exception {
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS);
+
final int managedProfileUserId = 15;
final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
From c33703be7408a50819909dfffbda1b0b78a31da3 Mon Sep 17 00:00:00 2001
From: Alex Kershaw
Date: Mon, 30 Nov 2020 17:18:42 +0000
Subject: [PATCH 004/288] Protect DeviceAdminReceiver intents
Fixes: 170639543
Change-Id: Ic12812efebe29aa99da93b59b6cf783097f3de76
(cherry picked from commit 74ad72137b1b4acea452c7300b1b632c2a531117)
---
core/res/AndroidManifest.xml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 162fa70dd16af..529a3d83a7f6a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -115,6 +115,12 @@
+
+
+
+
+
+
From d7f514abbe0bee01003ef694a67abfaa30667b4d Mon Sep 17 00:00:00 2001
From: Eugene Susla
Date: Mon, 26 Oct 2020 09:08:49 -0700
Subject: [PATCH 005/288] Prevent non-system overlays from showing over CDM UI
Since CDM grants privileges, it should have the same overlay
policy as permission UI
Test: use an app wit ha visible overlay to ensure
the overlay disappears when CDM is shown
Fixes: 171221090
Change-Id: I004843edf5a156dc07fb961edf80272f5cb50163
(cherry picked from commit ea4ce4d48eb22e05f03b36e281149f1e944674fa)
Merged-In: I004843edf5a156dc07fb961edf80272f5cb50163
---
.../android/companiondevicemanager/DeviceChooserActivity.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 16ef59f201f17..7bc9993751d40 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -17,6 +17,7 @@
package com.android.companiondevicemanager;
import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.app.Activity;
import android.companion.CompanionDeviceManager;
@@ -56,6 +57,8 @@ public class DeviceChooserActivity extends Activity {
Log.e(LOG_TAG, "About to show UI, but no devices to show");
}
+ getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
if (getService().mRequest.isSingleDevice()) {
setContentView(R.layout.device_confirmation);
final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
From f717d7195fa8654a7c9e4e28bbe473f8513eef64 Mon Sep 17 00:00:00 2001
From: Eugene Susla
Date: Fri, 4 Dec 2020 16:01:46 -0800
Subject: [PATCH 006/288] Allow CDM to hide overlays
Since CDM has sensitive user consent UIs, it should be able to hide
non-system overlays
Test: use a 3p overlay app with a visible overlay to ensure overlay disappears when CDM is shown
Bug: 171221090
Change-Id: I78673b7b95f4c3cb31fab180201eb17c9123bddf
Merged-In: I78673b7b95f4c3cb31fab180201eb17c9123bddf
---
packages/CompanionDeviceManager/AndroidManifest.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index ea9b52cbf3d51..e4e5b9fa781a2 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -31,6 +31,7 @@
+
Date: Mon, 26 Oct 2020 09:08:49 -0700
Subject: [PATCH 007/288] RESTRICT AUTOMERGE Prevent non-system overlays from
showing over CDM UI
Since CDM grants privileges, it should have the same overlay
policy as permission UI
Test: use an app wit ha visible overlay to ensure
the overlay disappears when CDM is shown
Fixes: 171221090
Change-Id: I4daaee7d8b710a72f6166cbb2252ef8af84c2c60
---
.../android/companiondevicemanager/DeviceChooserActivity.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 16ef59f201f17..7bc9993751d40 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -17,6 +17,7 @@
package com.android.companiondevicemanager;
import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.app.Activity;
import android.companion.CompanionDeviceManager;
@@ -56,6 +57,8 @@ public class DeviceChooserActivity extends Activity {
Log.e(LOG_TAG, "About to show UI, but no devices to show");
}
+ getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
if (getService().mRequest.isSingleDevice()) {
setContentView(R.layout.device_confirmation);
final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
From 30e9d28c22e7c21ab8e06ba54d2df665fba9ed5e Mon Sep 17 00:00:00 2001
From: Eugene Susla
Date: Fri, 4 Dec 2020 16:01:46 -0800
Subject: [PATCH 008/288] RESTRICT AUTOMERGE Allow CDM to hide overlays
Since CDM has sensitive user consent UIs, it should be able to hide
non-system overlays
Test: use a 3p overlay app with a visible overlay to ensure overlay disappears when CDM is shown
Bug: 171221090
Change-Id: I3274cb7f03f63e1fa99a9ca06759972ce2a51309
---
packages/CompanionDeviceManager/AndroidManifest.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 42885e8193a72..c975c95b8b7cc 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -29,6 +29,7 @@
+
Date: Mon, 26 Oct 2020 09:08:49 -0700
Subject: [PATCH 009/288] RESTRICT AUTOMERGE Prevent non-system overlays from
showing over CDM UI
Since CDM grants privileges, it should have the same overlay
policy as permission UI
Test: use an app wit ha visible overlay to ensure
the overlay disappears when CDM is shown
Fixes: 171221090
Change-Id: I4daaee7d8b710a72f6166cbb2252ef8af84c2c60
---
.../companiondevicemanager/DeviceChooserActivity.java | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 16ef59f201f17..34139a8e58e9e 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -17,6 +17,7 @@
package com.android.companiondevicemanager;
import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.app.Activity;
import android.companion.CompanionDeviceManager;
@@ -56,6 +57,8 @@ public class DeviceChooserActivity extends Activity {
Log.e(LOG_TAG, "About to show UI, but no devices to show");
}
+ getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
if (getService().mRequest.isSingleDevice()) {
setContentView(R.layout.device_confirmation);
final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
@@ -157,4 +160,4 @@ public class DeviceChooserActivity extends Activity {
new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device));
finish();
}
-}
\ No newline at end of file
+}
From 486773ff4567bbfd63f7f57dc9cb1f0ffb8a6c12 Mon Sep 17 00:00:00 2001
From: Eugene Susla
Date: Fri, 4 Dec 2020 16:01:46 -0800
Subject: [PATCH 010/288] RESTRICT AUTOMERGE Allow CDM to hide overlays
Since CDM has sensitive user consent UIs, it should be able to hide
non-system overlays
Test: use a 3p overlay app with a visible overlay to ensure overlay disappears when CDM is shown
Bug: 171221090
Change-Id: I3274cb7f03f63e1fa99a9ca06759972ce2a51309
---
packages/CompanionDeviceManager/AndroidManifest.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 34bc4ebcd0aa2..2224dfc6a81ba 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -28,6 +28,7 @@
+
Date: Mon, 26 Oct 2020 09:08:49 -0700
Subject: [PATCH 011/288] RESTRICT AUTOMERGE Prevent non-system overlays from
showing over CDM UI
Since CDM grants privileges, it should have the same overlay
policy as permission UI
Test: use an app wit ha visible overlay to ensure
the overlay disappears when CDM is shown
Fixes: 171221090
Change-Id: I4daaee7d8b710a72f6166cbb2252ef8af84c2c60
---
.../companiondevicemanager/DeviceChooserActivity.java | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 7e23ee152ed94..18cec01dadf05 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -17,6 +17,7 @@
package com.android.companiondevicemanager;
import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.app.Activity;
import android.companion.CompanionDeviceManager;
@@ -55,6 +56,8 @@ public class DeviceChooserActivity extends Activity {
Log.e(LOG_TAG, "About to show UI, but no devices to show");
}
+ getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
if (getService().mRequest.isSingleDevice()) {
setContentView(R.layout.device_confirmation);
final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
@@ -155,4 +158,4 @@ public class DeviceChooserActivity extends Activity {
new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device));
finish();
}
-}
\ No newline at end of file
+}
From 6a5777dd5020378507b97511925a0369f8f408b4 Mon Sep 17 00:00:00 2001
From: Eugene Susla
Date: Fri, 4 Dec 2020 16:01:46 -0800
Subject: [PATCH 012/288] RESTRICT AUTOMERGE Allow CDM to hide overlays
Since CDM has sensitive user consent UIs, it should be able to hide
non-system overlays
Test: use a 3p overlay app with a visible overlay to ensure overlay disappears when CDM is shown
Bug: 171221090
Change-Id: I3274cb7f03f63e1fa99a9ca06759972ce2a51309
---
packages/CompanionDeviceManager/AndroidManifest.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 34bc4ebcd0aa2..2224dfc6a81ba 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -28,6 +28,7 @@
+
Date: Mon, 19 Oct 2020 19:37:30 +0100
Subject: [PATCH 013/288] Enforce BACKUP permission on Service end.
BackupManager runs in the client process, whereas BackupManagerService
runs in the system server process. Therefore, apps permissions need should
be enforced on the service side.
Bug: 158482162
Test: Manually checked that a sample app encounters SecurityException
after but not before this CL, when running code similar to the
sample in http://b/158482162#comment1 to figure out whether
isBackupServiceActive() for its own uid.
Change-Id: I59693819542a80a065a9c88373393b0ba0dbef65
Merged-In: I59693819542a80a065a9c88373393b0ba0dbef65
(cherry picked from commit b2ab2c414fcac5f68edd8d790d3773f1f9330e3b)
---
core/java/android/app/backup/BackupManager.java | 2 --
.../java/com/android/server/backup/BackupManagerService.java | 2 ++
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 3bc043ee09126..56e9c36fe4b4c 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -401,8 +401,6 @@ public class BackupManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.BACKUP)
public boolean isBackupServiceActive(UserHandle user) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "isBackupServiceActive");
checkServiceBinder();
if (sService != null) {
try {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index b13bef2de1512..19ada753f33d7 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -506,6 +506,8 @@ public class BackupManagerService extends IBackupManager.Stub {
*/
@Override
public boolean isBackupServiceActive(int userId) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "isBackupServiceActive");
synchronized (mStateLock) {
return !mGlobalDisable && isBackupActivatedForUser(userId);
}
From b50cdde5f009ca1e8a548605c8b364d860110b67 Mon Sep 17 00:00:00 2001
From: Tobias Thierer
Date: Mon, 19 Oct 2020 19:37:30 +0100
Subject: [PATCH 014/288] BackupManagerService: Make new behavior conditional
on ChangeId.
Base CL ag/12885739 introduced unconditional enforcement of the BACKUP
permission for callers of BackupManagerService.isBackupServiceActive()
in the service, but dropped the enforcement on the app process side
(BackupManager).
This CL makes the behavior change conditional on a compat ChangeId.
Bug: 158482162
Test: Manually checked that an app similar to the code sample from
http://b/158482162#comment1 can reproduce the behavior.
This is true both before the base CL and after this CL, when
the app targets an old SDK version (26).
Test: Checked that both (a) before this CL, (b) after this CL where
the change is manually enabled for the app via the below commands,
the app runs into a SecurityException instead:
$ adb shell am compat enable 158482162 com.example.tester
$ adb shell dumpsys platform_compat | grep 158482162
ChangeId(158482162; name=IS_BACKUP_SERVICE_ACTIVE_ENFORCE_PERMISSION_IN_SERVICE; enableSinceTargetSdk=31; packageOverrides={com.example.tester=true})
Change-Id: I58e5d2a0b438296137fd76720636c8fdce740ded
Merged-In: I58e5d2a0b438296137fd76720636c8fdce740ded
(cherry picked from commit 7671f0d95c7e92a2342ed05a053a38d8aa16a17f)
---
.../android/app/backup/BackupManager.java | 20 +++++++++++++++++++
.../server/backup/BackupManagerService.java | 9 +++++++--
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 56e9c36fe4b4c..acaea5fdd6098 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -21,10 +21,14 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -391,6 +395,17 @@ public class BackupManager {
return false;
}
+
+ /**
+ * If this change is enabled, the {@code BACKUP} permission needed for
+ * {@code isBackupServiceActive()} will be enforced on the service end
+ * rather than client-side in {@link BackupManager}.
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ public static final long IS_BACKUP_SERVICE_ACTIVE_ENFORCE_PERMISSION_IN_SERVICE = 158482162;
+
/**
* Report whether the backup mechanism is currently active.
* When it is inactive, the device will not perform any backup operations, nor will it
@@ -401,6 +416,11 @@ public class BackupManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.BACKUP)
public boolean isBackupServiceActive(UserHandle user) {
+ if (!CompatChanges.isChangeEnabled(
+ IS_BACKUP_SERVICE_ACTIVE_ENFORCE_PERMISSION_IN_SERVICE)) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "isBackupServiceActive");
+ }
checkServiceBinder();
if (sService != null) {
try {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 19ada753f33d7..445297a839ba6 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -30,6 +30,7 @@ import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreSession;
import android.app.backup.ISelectBackupTransportCallback;
+import android.app.compat.CompatChanges;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
@@ -506,8 +507,12 @@ public class BackupManagerService extends IBackupManager.Stub {
*/
@Override
public boolean isBackupServiceActive(int userId) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
- "isBackupServiceActive");
+ int callingUid = Binder.getCallingUid();
+ if (CompatChanges.isChangeEnabled(
+ BackupManager.IS_BACKUP_SERVICE_ACTIVE_ENFORCE_PERMISSION_IN_SERVICE, callingUid)) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ "isBackupServiceActive");
+ }
synchronized (mStateLock) {
return !mGlobalDisable && isBackupActivatedForUser(userId);
}
From d8b61b515938cecd441fae16fc001f1e49274780 Mon Sep 17 00:00:00 2001
From: Tobias Thierer
Date: Thu, 10 Dec 2020 13:46:08 +0000
Subject: [PATCH 015/288] Fix DevicePolicyManager.isBackupServiceEnabled()
breakage.
http://ag/12885739 introduced a enforceCallingPermission(BACKUP) check
but callers of this API do not hold that permission. This CL fixes
this by changing the check to enforceCallingOrSelfPermission(BACKUP),
and clearing the binder identity in DevicePolicyManagerService, which
makes the system server process the owner of that call.
Bug: 158482162
Bug: 172466964
Test: atest com.android.cts.devicepolicy.{Device,Profile}OwnerTest#testBackupServiceEnabling
Change-Id: I11d863229c4d62a058aaf37446a694b9c73ae5b8
Merged-In: I11d863229c4d62a058aaf37446a694b9c73ae5b8
(cherry picked from commit 630dec9eb496bc70db44c4f0b4196ea91e7df6cb)
---
.../server/backup/BackupManagerService.java | 2 +-
.../DevicePolicyManagerService.java | 18 ++++++++++--------
2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 445297a839ba6..89a6eca8fdaad 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -510,7 +510,7 @@ public class BackupManagerService extends IBackupManager.Stub {
int callingUid = Binder.getCallingUid();
if (CompatChanges.isChangeEnabled(
BackupManager.IS_BACKUP_SERVICE_ACTIVE_ENFORCE_PERMISSION_IN_SERVICE, callingUid)) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"isBackupServiceActive");
}
synchronized (mStateLock) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ce3cdeab232a5..33a346d1ac9b4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -14438,15 +14438,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
enforceProfileOrDeviceOwner(admin);
- synchronized (getLockObject()) {
- try {
- IBackupManager ibm = mInjector.getIBackupManager();
- return ibm != null && ibm.isBackupServiceActive(
- mInjector.userHandleGetCallingUserId());
- } catch (RemoteException e) {
- throw new IllegalStateException("Failed requesting backup service state.", e);
+ final int userId = mInjector.userHandleGetCallingUserId();
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ synchronized (getLockObject()) {
+ try {
+ IBackupManager ibm = mInjector.getIBackupManager();
+ return ibm != null && ibm.isBackupServiceActive(userId);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed requesting backup service state.", e);
+ }
}
- }
+ });
}
@Override
From 638ebb32547a75cdc94a27acb3731e5a89434448 Mon Sep 17 00:00:00 2001
From: Evan Laird
Date: Wed, 30 Sep 2020 15:04:37 -0400
Subject: [PATCH 016/288] Listen for EXTRA_PRESENT changes
ACTION_BATTERY_CHANGED can pass a boolean EXTRA_PRESENT which would
alert listeners that a battery may or may not be present. SystemUI now
wants to show an error battery state instead of a potentially misleading
status in this situation.
Bug: 169935148
Test: adb shell cmd battery set present 1
Change-Id: I7d576248bb9931059100facb67ca6b46e81e554e
Merged-In: I7d576248bb9931059100facb67ca6b46e81e554e
---
.../settingslib/fuelgauge/BatteryStatus.java | 6 +-
.../res/drawable/ic_battery_unknown.xml | 24 +++
packages/SystemUI/res/values/config.xml | 5 +
packages/SystemUI/res/values/strings.xml | 9 +
.../keyguard/KeyguardUpdateMonitor.java | 9 +-
.../android/systemui/BatteryMeterView.java | 51 +++++-
.../com/android/systemui/SystemUIService.java | 11 +-
.../KeyguardIndicationController.java | 160 ++++++++++--------
.../statusbar/policy/BatteryController.java | 3 +
.../policy/BatteryControllerImpl.java | 28 +++
.../statusbar/policy/BatteryStateNotifier.kt | 90 ++++++++++
.../KeyguardIndicationControllerTest.java | 15 +-
.../policy/BatteryControllerTest.java | 37 ++++
.../policy/BatteryStateNotifierTest.kt | 81 +++++++++
14 files changed, 446 insertions(+), 83 deletions(-)
create mode 100644 packages/SystemUI/res/drawable/ic_battery_unknown.xml
create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
index ce60fafaa31b7..b3205d7563b2c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
@@ -25,6 +25,7 @@ import static android.os.BatteryManager.EXTRA_LEVEL;
import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
import static android.os.BatteryManager.EXTRA_PLUGGED;
+import static android.os.BatteryManager.EXTRA_PRESENT;
import static android.os.BatteryManager.EXTRA_STATUS;
import android.content.Context;
@@ -50,14 +51,16 @@ public class BatteryStatus {
public final int plugged;
public final int health;
public final int maxChargingWattage;
+ public final boolean present;
public BatteryStatus(int status, int level, int plugged, int health,
- int maxChargingWattage) {
+ int maxChargingWattage, boolean present) {
this.status = status;
this.level = level;
this.plugged = plugged;
this.health = health;
this.maxChargingWattage = maxChargingWattage;
+ this.present = present;
}
public BatteryStatus(Intent batteryChangedIntent) {
@@ -65,6 +68,7 @@ public class BatteryStatus {
plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0);
level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0);
health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
+ present = batteryChangedIntent.getBooleanExtra(EXTRA_PRESENT, true);
final int maxChargingMicroAmp = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT,
-1);
diff --git a/packages/SystemUI/res/drawable/ic_battery_unknown.xml b/packages/SystemUI/res/drawable/ic_battery_unknown.xml
new file mode 100644
index 0000000000000..8b2ba12fe0bef
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_battery_unknown.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 5e5df6bfd9880..9530a74d0ece2 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -579,4 +579,9 @@
320
- 1.25
+
+
+ false
+
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 824521ecd1e76..174f5c737d147 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -437,6 +437,8 @@
Battery three bars.
Battery full.
+
+ Battery percentage unknown.
No phone.
@@ -2870,4 +2872,11 @@
Couldn\'t connect. Try again.
Pair new device
+
+
+ Problem reading your battery meter
+
+ Tap for more information
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 60cd24019b973..deaa4255c2f1e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1696,7 +1696,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
// Take a guess at initial SIM state, battery status and PLMN until we get an update
- mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0);
+ mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0, true);
// Watch for interesting updates
final IntentFilter filter = new IntentFilter();
@@ -2563,6 +2563,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean wasPluggedIn = old.isPluggedIn();
final boolean stateChangedWhilePluggedIn = wasPluggedIn && nowPluggedIn
&& (old.status != current.status);
+ final boolean nowPresent = current.present;
+ final boolean wasPresent = old.present;
// change in plug state is always interesting
if (wasPluggedIn != nowPluggedIn || stateChangedWhilePluggedIn) {
@@ -2584,6 +2586,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return true;
}
+ // Battery either showed up or disappeared
+ if (wasPresent != nowPresent) {
+ return true;
+ }
+
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index a46ab3a9e35b6..fc30be416c0f9 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -33,6 +33,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
@@ -95,12 +96,15 @@ public class BatteryMeterView extends LinearLayout implements
private int mTextColor;
private int mLevel;
private int mShowPercentMode = MODE_DEFAULT;
- private boolean mForceShowPercent;
private boolean mShowPercentAvailable;
// Some places may need to show the battery conditionally, and not obey the tuner
private boolean mIgnoreTunerUpdates;
private boolean mIsSubscribedForTunerUpdates;
private boolean mCharging;
+ // Error state where we know nothing about the current battery state
+ private boolean mBatteryStateUnknown;
+ // Lazily-loaded since this is expected to be a rare-if-ever state
+ private Drawable mUnknownStateDrawable;
private DualToneHandler mDualToneHandler;
private int mUser;
@@ -350,6 +354,11 @@ public class BatteryMeterView extends LinearLayout implements
}
private void updatePercentText() {
+ if (mBatteryStateUnknown) {
+ setContentDescription(getContext().getString(R.string.accessibility_battery_unknown));
+ return;
+ }
+
if (mBatteryController == null) {
return;
}
@@ -390,9 +399,13 @@ public class BatteryMeterView extends LinearLayout implements
final boolean systemSetting = 0 != whitelistIpcs(() -> Settings.System
.getIntForUser(getContext().getContentResolver(),
SHOW_BATTERY_PERCENT, 0, mUser));
+ boolean shouldShow =
+ (mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF)
+ || mShowPercentMode == MODE_ON
+ || mShowPercentMode == MODE_ESTIMATE;
+ shouldShow = shouldShow && !mBatteryStateUnknown;
- if ((mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF)
- || mShowPercentMode == MODE_ON || mShowPercentMode == MODE_ESTIMATE) {
+ if (shouldShow) {
if (!showing) {
mBatteryPercentView = loadPercentView();
if (mPercentageStyleId != 0) { // Only set if specified as attribute
@@ -418,6 +431,32 @@ public class BatteryMeterView extends LinearLayout implements
scaleBatteryMeterViews();
}
+ private Drawable getUnknownStateDrawable() {
+ if (mUnknownStateDrawable == null) {
+ mUnknownStateDrawable = mContext.getDrawable(R.drawable.ic_battery_unknown);
+ mUnknownStateDrawable.setTint(mTextColor);
+ }
+
+ return mUnknownStateDrawable;
+ }
+
+ @Override
+ public void onBatteryUnknownStateChanged(boolean isUnknown) {
+ if (mBatteryStateUnknown == isUnknown) {
+ return;
+ }
+
+ mBatteryStateUnknown = isUnknown;
+
+ if (mBatteryStateUnknown) {
+ mBatteryIconView.setImageDrawable(getUnknownStateDrawable());
+ } else {
+ mBatteryIconView.setImageDrawable(mDrawable);
+ }
+
+ updateShowPercent();
+ }
+
/**
* Looks up the scale factor for status bar icons and scales the battery view by that amount.
*/
@@ -458,6 +497,10 @@ public class BatteryMeterView extends LinearLayout implements
if (mBatteryPercentView != null) {
mBatteryPercentView.setTextColor(singleToneColor);
}
+
+ if (mUnknownStateDrawable != null) {
+ mUnknownStateDrawable.setTint(singleToneColor);
+ }
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -467,8 +510,8 @@ public class BatteryMeterView extends LinearLayout implements
pw.println(" mDrawable.getPowerSave: " + powerSave);
pw.println(" mBatteryPercentView.getText(): " + percent);
pw.println(" mTextColor: #" + Integer.toHexString(mTextColor));
+ pw.println(" mBatteryStateUnknown: " + mBatteryStateUnknown);
pw.println(" mLevel: " + mLevel);
- pw.println(" mForceShowPercent: " + mForceShowPercent);
}
private final class SettingObserver extends ContentObserver {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 708002d5b9460..1f41038c260fa 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -32,6 +32,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.LogBufferFreezer;
import com.android.systemui.dump.SystemUIAuxiliaryDumpService;
+import com.android.systemui.statusbar.policy.BatteryStateNotifier;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,18 +45,21 @@ public class SystemUIService extends Service {
private final DumpHandler mDumpHandler;
private final BroadcastDispatcher mBroadcastDispatcher;
private final LogBufferFreezer mLogBufferFreezer;
+ private final BatteryStateNotifier mBatteryStateNotifier;
@Inject
public SystemUIService(
@Main Handler mainHandler,
DumpHandler dumpHandler,
BroadcastDispatcher broadcastDispatcher,
- LogBufferFreezer logBufferFreezer) {
+ LogBufferFreezer logBufferFreezer,
+ BatteryStateNotifier batteryStateNotifier) {
super();
mMainHandler = mainHandler;
mDumpHandler = dumpHandler;
mBroadcastDispatcher = broadcastDispatcher;
mLogBufferFreezer = logBufferFreezer;
+ mBatteryStateNotifier = batteryStateNotifier;
}
@Override
@@ -68,6 +72,11 @@ public class SystemUIService extends Service {
// Finish initializing dump logic
mLogBufferFreezer.attach(mBroadcastDispatcher);
+ // If configured, set up a battery notification
+ if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) {
+ mBatteryStateNotifier.startListening();
+ }
+
// For debugging RescueParty
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
throw new RuntimeException();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 39d2f71e7e0bd..155ec5ea72de2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -123,6 +123,7 @@ public class KeyguardIndicationController implements StateListener,
private int mChargingSpeed;
private int mChargingWattage;
private int mBatteryLevel;
+ private boolean mBatteryPresent = true;
private long mChargingTimeRemaining;
private float mDisclosureMaxAlpha;
private String mMessageToShowOnScreenOn;
@@ -391,86 +392,103 @@ public class KeyguardIndicationController implements StateListener,
mWakeLock.setAcquired(false);
}
- if (mVisible) {
- // Walk down a precedence-ordered list of what indication
- // should be shown based on user or device state
- if (mDozing) {
- // When dozing we ignore any text color and use white instead, because
- // colors can be hard to read in low brightness.
- mTextView.setTextColor(Color.WHITE);
- if (!TextUtils.isEmpty(mTransientIndication)) {
- mTextView.switchIndication(mTransientIndication);
- } else if (!TextUtils.isEmpty(mAlignmentIndication)) {
- mTextView.switchIndication(mAlignmentIndication);
- mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
- } else if (mPowerPluggedIn || mEnableBatteryDefender) {
- String indication = computePowerIndication();
- if (animate) {
- animateText(mTextView, indication);
- } else {
- mTextView.switchIndication(indication);
- }
- } else {
- String percentage = NumberFormat.getPercentInstance()
- .format(mBatteryLevel / 100f);
- mTextView.switchIndication(percentage);
- }
- return;
- }
+ if (!mVisible) {
+ return;
+ }
- int userId = KeyguardUpdateMonitor.getCurrentUser();
- String trustGrantedIndication = getTrustGrantedIndication();
- String trustManagedIndication = getTrustManagedIndication();
+ // A few places might need to hide the indication, so always start by making it visible
+ mIndicationArea.setVisibility(View.VISIBLE);
- String powerIndication = null;
- if (mPowerPluggedIn || mEnableBatteryDefender) {
- powerIndication = computePowerIndication();
- }
-
- boolean isError = false;
- if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
- mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
- } else if (!TextUtils.isEmpty(mTransientIndication)) {
- if (powerIndication != null && !mTransientIndication.equals(powerIndication)) {
- String indication = mContext.getResources().getString(
- R.string.keyguard_indication_trust_unlocked_plugged_in,
- mTransientIndication, powerIndication);
- mTextView.switchIndication(indication);
- } else {
- mTextView.switchIndication(mTransientIndication);
- }
- isError = mTransientTextIsError;
- } else if (!TextUtils.isEmpty(trustGrantedIndication)
- && mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
- if (powerIndication != null) {
- String indication = mContext.getResources().getString(
- R.string.keyguard_indication_trust_unlocked_plugged_in,
- trustGrantedIndication, powerIndication);
- mTextView.switchIndication(indication);
- } else {
- mTextView.switchIndication(trustGrantedIndication);
- }
+ // Walk down a precedence-ordered list of what indication
+ // should be shown based on user or device state
+ if (mDozing) {
+ // When dozing we ignore any text color and use white instead, because
+ // colors can be hard to read in low brightness.
+ mTextView.setTextColor(Color.WHITE);
+ if (!TextUtils.isEmpty(mTransientIndication)) {
+ mTextView.switchIndication(mTransientIndication);
+ } else if (!mBatteryPresent) {
+ // If there is no battery detected, hide the indication and bail
+ mIndicationArea.setVisibility(View.GONE);
} else if (!TextUtils.isEmpty(mAlignmentIndication)) {
mTextView.switchIndication(mAlignmentIndication);
- isError = true;
+ mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
} else if (mPowerPluggedIn || mEnableBatteryDefender) {
- if (DEBUG_CHARGING_SPEED) {
- powerIndication += ", " + (mChargingWattage / 1000) + " mW";
- }
+ String indication = computePowerIndication();
if (animate) {
- animateText(mTextView, powerIndication);
+ animateText(mTextView, indication);
} else {
- mTextView.switchIndication(powerIndication);
+ mTextView.switchIndication(indication);
}
- } else if (!TextUtils.isEmpty(trustManagedIndication)
- && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
- && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
- mTextView.switchIndication(trustManagedIndication);
} else {
- mTextView.switchIndication(mRestingIndication);
+ String percentage = NumberFormat.getPercentInstance()
+ .format(mBatteryLevel / 100f);
+ mTextView.switchIndication(percentage);
}
- mTextView.setTextColor(isError ? Utils.getColorError(mContext)
- : mInitialTextColorState);
+ return;
+ }
+
+ int userId = KeyguardUpdateMonitor.getCurrentUser();
+ String trustGrantedIndication = getTrustGrantedIndication();
+ String trustManagedIndication = getTrustManagedIndication();
+
+ String powerIndication = null;
+ if (mPowerPluggedIn || mEnableBatteryDefender) {
+ powerIndication = computePowerIndication();
+ }
+
+ // Some cases here might need to hide the indication (if the battery is not present)
+ boolean hideIndication = false;
+ boolean isError = false;
+ if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
+ mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
+ } else if (!TextUtils.isEmpty(mTransientIndication)) {
+ if (powerIndication != null && !mTransientIndication.equals(powerIndication)) {
+ String indication = mContext.getResources().getString(
+ R.string.keyguard_indication_trust_unlocked_plugged_in,
+ mTransientIndication, powerIndication);
+ mTextView.switchIndication(indication);
+ hideIndication = !mBatteryPresent;
+ } else {
+ mTextView.switchIndication(mTransientIndication);
+ }
+ isError = mTransientTextIsError;
+ } else if (!TextUtils.isEmpty(trustGrantedIndication)
+ && mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
+ if (powerIndication != null) {
+ String indication = mContext.getResources().getString(
+ R.string.keyguard_indication_trust_unlocked_plugged_in,
+ trustGrantedIndication, powerIndication);
+ mTextView.switchIndication(indication);
+ hideIndication = !mBatteryPresent;
+ } else {
+ mTextView.switchIndication(trustGrantedIndication);
+ }
+ } else if (!TextUtils.isEmpty(mAlignmentIndication)) {
+ mTextView.switchIndication(mAlignmentIndication);
+ isError = true;
+ hideIndication = !mBatteryPresent;
+ } else if (mPowerPluggedIn || mEnableBatteryDefender) {
+ if (DEBUG_CHARGING_SPEED) {
+ powerIndication += ", " + (mChargingWattage / 1000) + " mW";
+ }
+ if (animate) {
+ animateText(mTextView, powerIndication);
+ } else {
+ mTextView.switchIndication(powerIndication);
+ }
+ hideIndication = !mBatteryPresent;
+ } else if (!TextUtils.isEmpty(trustManagedIndication)
+ && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
+ && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
+ mTextView.switchIndication(trustManagedIndication);
+ } else {
+ mTextView.switchIndication(mRestingIndication);
+ }
+ mTextView.setTextColor(isError ? Utils.getColorError(mContext)
+ : mInitialTextColorState);
+ if (hideIndication) {
+ mIndicationArea.setVisibility(View.GONE);
}
}
@@ -654,6 +672,7 @@ public class KeyguardIndicationController implements StateListener,
pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
pw.println(" mDozing: " + mDozing);
pw.println(" mBatteryLevel: " + mBatteryLevel);
+ pw.println(" mBatteryPresent: " + mBatteryPresent);
pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
pw.println(" computePowerIndication(): " + computePowerIndication());
}
@@ -694,6 +713,7 @@ public class KeyguardIndicationController implements StateListener,
mBatteryLevel = status.level;
mBatteryOverheated = status.isOverheated();
mEnableBatteryDefender = mBatteryOverheated && status.isPluggedIn();
+ mBatteryPresent = status.present;
try {
mChargingTimeRemaining = mPowerPluggedIn
? mBatteryInfo.computeChargeTimeRemaining() : -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index e5a46797d0356..5009fce5216d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -97,6 +97,9 @@ public interface BatteryController extends DemoMode, Dumpable,
default void onPowerSaveChanged(boolean isPowerSave) {
}
+ default void onBatteryUnknownStateChanged(boolean isUnknown) {
+ }
+
default void onReverseChanged(boolean isReverse, int level, String name) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index d30f01a658f6b..ea79c246f0145 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static android.os.BatteryManager.EXTRA_PRESENT;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -70,6 +72,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
protected int mLevel;
protected boolean mPluggedIn;
protected boolean mCharging;
+ private boolean mStateUnknown = false;
private boolean mCharged;
protected boolean mPowerSave;
private boolean mAodPowerSave;
@@ -126,6 +129,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
pw.print(" mCharging="); pw.println(mCharging);
pw.print(" mCharged="); pw.println(mCharged);
pw.print(" mPowerSave="); pw.println(mPowerSave);
+ pw.print(" mStateUnknown="); pw.println(mStateUnknown);
}
@Override
@@ -139,8 +143,11 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mChangeCallbacks.add(cb);
}
if (!mHasReceivedBattery) return;
+
+ // Make sure new callbacks get the correct initial state
cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
cb.onPowerSaveChanged(mPowerSave);
+ cb.onBatteryUnknownStateChanged(mStateUnknown);
}
@Override
@@ -168,6 +175,13 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mWirelessCharging = mCharging && intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
== BatteryManager.BATTERY_PLUGGED_WIRELESS;
+ boolean present = intent.getBooleanExtra(EXTRA_PRESENT, true);
+ boolean unknown = !present;
+ if (unknown != mStateUnknown) {
+ mStateUnknown = unknown;
+ fireBatteryUnknownStateChanged();
+ }
+
fireBatteryLevelChanged();
} else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
updatePowerSave();
@@ -316,6 +330,15 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
}
}
+ private void fireBatteryUnknownStateChanged() {
+ synchronized (mChangeCallbacks) {
+ final int n = mChangeCallbacks.size();
+ for (int i = 0; i < n; i++) {
+ mChangeCallbacks.get(i).onBatteryUnknownStateChanged(mStateUnknown);
+ }
+ }
+ }
+
private void firePowerSaveChanged() {
synchronized (mChangeCallbacks) {
final int N = mChangeCallbacks.size();
@@ -340,6 +363,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
String level = args.getString("level");
String plugged = args.getString("plugged");
String powerSave = args.getString("powersave");
+ String present = args.getString("present");
if (level != null) {
mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
}
@@ -350,6 +374,10 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mPowerSave = powerSave.equals("true");
firePowerSaveChanged();
}
+ if (present != null) {
+ mStateUnknown = !present.equals("true");
+ fireBatteryUnknownStateChanged();
+ }
fireBatteryLevelChanged();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
new file mode 100644
index 0000000000000..92e5b78f776a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import com.android.systemui.R
+import com.android.systemui.util.concurrency.DelayableExecutor
+import javax.inject.Inject
+
+/**
+ * Listens for important battery states and sends non-dismissible system notifications if there is a
+ * problem
+ */
+class BatteryStateNotifier @Inject constructor(
+ val controller: BatteryController,
+ val noMan: NotificationManager,
+ val delayableExecutor: DelayableExecutor,
+ val context: Context
+) : BatteryController.BatteryStateChangeCallback {
+ var stateUnknown = false
+
+ fun startListening() {
+ controller.addCallback(this)
+ }
+
+ fun stopListening() {
+ controller.removeCallback(this)
+ }
+
+ override fun onBatteryUnknownStateChanged(isUnknown: Boolean) {
+ stateUnknown = isUnknown
+ if (stateUnknown) {
+ val channel = NotificationChannel("battery_status", "Battery status",
+ NotificationManager.IMPORTANCE_DEFAULT)
+ noMan.createNotificationChannel(channel)
+
+ val intent = Intent(Intent.ACTION_VIEW,
+ Uri.parse(context.getString(R.string.config_batteryStateUnknownUrl)))
+ val pi = PendingIntent.getActivity(context, 0, intent, 0)
+
+ val builder = Notification.Builder(context, channel.id)
+ .setAutoCancel(false)
+ .setContentTitle(
+ context.getString(R.string.battery_state_unknown_notification_title))
+ .setContentText(
+ context.getString(R.string.battery_state_unknown_notification_text))
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setContentIntent(pi)
+ .setAutoCancel(true)
+ .setOngoing(true)
+
+ noMan.notify(TAG, ID, builder.build())
+ } else {
+ scheduleNotificationCancel()
+ }
+ }
+
+ private fun scheduleNotificationCancel() {
+ val r = {
+ if (!stateUnknown) {
+ noMan.cancel(ID)
+ }
+ }
+ delayableExecutor.executeDelayed(r, DELAY_MILLIS)
+ }
+}
+
+private const val TAG = "BatteryStateNotifier"
+private const val ID = 666
+private const val DELAY_MILLIS: Long = 4 * 60 * 60 * 1000
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 052f3382099f6..91144be7347d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -495,7 +495,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
80 /* level */, BatteryManager.BATTERY_PLUGGED_WIRELESS, 100 /* health */,
- 0 /* maxChargingWattage */);
+ 0 /* maxChargingWattage */, true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
verify(mIBatteryStats).computeChargeTimeRemaining();
@@ -507,7 +507,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
80 /* level */, 0 /* plugged */, 100 /* health */,
- 0 /* maxChargingWattage */);
+ 0 /* maxChargingWattage */, true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
verify(mIBatteryStats, never()).computeChargeTimeRemaining();
@@ -553,7 +553,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
80 /* level */, BatteryManager.BATTERY_PLUGGED_AC,
- BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */);
+ BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */,
+ true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
mController.setVisible(true);
@@ -569,7 +570,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_DISCHARGING,
80 /* level */, BatteryManager.BATTERY_PLUGGED_AC,
- BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */);
+ BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */,
+ true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
mController.setVisible(true);
@@ -585,7 +587,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
100 /* level */, BatteryManager.BATTERY_PLUGGED_AC,
- BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */);
+ BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */,
+ true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
mController.setVisible(true);
@@ -599,7 +602,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_DISCHARGING,
90 /* level */, 0 /* plugged */, BatteryManager.BATTERY_HEALTH_OVERHEAT,
- 0 /* maxChargingWattage */);
+ 0 /* maxChargingWattage */, true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
mController.setDozing(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index eca48c8c2ee12..e985a61c7a56e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -16,7 +16,11 @@
package com.android.systemui.statusbar.policy;
+import static android.os.BatteryManager.EXTRA_PRESENT;
+
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Intent;
@@ -30,6 +34,7 @@ import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import org.junit.Assert;
import org.junit.Before;
@@ -93,4 +98,36 @@ public class BatteryControllerTest extends SysuiTestCase {
Assert.assertFalse(mBatteryController.isAodPowerSave());
}
+ @Test
+ public void testBatteryPresentState_notPresent() {
+ // GIVEN a battery state callback listening for changes
+ BatteryStateChangeCallback cb = mock(BatteryStateChangeCallback.class);
+ mBatteryController.addCallback(cb);
+
+ // WHEN the state of the battery becomes unknown
+ Intent i = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ i.putExtra(EXTRA_PRESENT, false);
+ mBatteryController.onReceive(getContext(), i);
+
+ // THEN the callback is notified
+ verify(cb, atLeastOnce()).onBatteryUnknownStateChanged(true);
+ }
+
+ @Test
+ public void testBatteryPresentState_callbackAddedAfterStateChange() {
+ // GIVEN a battery state callback
+ BatteryController.BatteryStateChangeCallback cb =
+ mock(BatteryController.BatteryStateChangeCallback.class);
+
+ // GIVEN the state has changed before adding a new callback
+ Intent i = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ i.putExtra(EXTRA_PRESENT, false);
+ mBatteryController.onReceive(getContext(), i);
+
+ // WHEN a callback is added
+ mBatteryController.addCallback(cb);
+
+ // THEN it is informed about the battery state
+ verify(cb, atLeastOnce()).onBatteryUnknownStateChanged(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
new file mode 100644
index 0000000000000..dcd57f137d713
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.app.NotificationManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+
+import androidx.test.filters.SmallTest
+
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+private fun anyObject(): T {
+ return Mockito.anyObject()
+}
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper()
+@SmallTest
+class BatteryStateNotifierTest : SysuiTestCase() {
+ @Mock private lateinit var batteryController: BatteryController
+ @Mock private lateinit var noMan: NotificationManager
+
+ private val clock = FakeSystemClock()
+ private val executor = FakeExecutor(clock)
+
+ private lateinit var notifier: BatteryStateNotifier
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ notifier = BatteryStateNotifier(batteryController, noMan, executor, context)
+ notifier.startListening()
+
+ context.ensureTestableResources()
+ }
+
+ @Test
+ fun testNotifyWhenStateUnknown() {
+ notifier.onBatteryUnknownStateChanged(true)
+ verify(noMan).notify(anyString(), anyInt(), anyObject())
+ }
+
+ @Test
+ fun testCancelAfterDelay() {
+ notifier.onBatteryUnknownStateChanged(true)
+ notifier.onBatteryUnknownStateChanged(false)
+
+ clock.advanceTime(DELAY_MILLIS + 1)
+ verify(noMan).cancel(anyInt())
+ }
+}
+
+// From BatteryStateNotifier.kt
+private const val DELAY_MILLIS: Long = 40 * 60 * 60 * 1000
From a68473ce978f3e3ad73bd127cdcfcf663d9fd4d9 Mon Sep 17 00:00:00 2001
From: Riddle Hsu
Date: Thu, 3 Dec 2020 15:57:36 +0800
Subject: [PATCH 017/288] Only update native InputApplicationHandle once
This makes sure the write operation (NativeInputApplicationHandle
::updateInfo) is always called from window manager side once when
calling SurfaceControl.Transaction#setInputWindowInfo or
InputManagerService#setFocusedApplication. If the info of input
application handle is changed, a new instance will be created.
That avoids the race condition of reading the fields of the same
InputApplicationInfo instance from input dispatcher.
Bug: 171857140
Bug: 161334769
Bug: 174768985
Test: WindowInputTests
Merged-In: Ief84bbe6e6fa4da5309912059904932ccf775b75
Merged-In: I70de9835c7699fe6f56fc3655b0fee5c317ecc3a
Change-Id: I70de9835c7699fe6f56fc3655b0fee5c317ecc3a
---
.../android/view/InputApplicationHandle.java | 10 +++++++---
core/java/android/view/InputWindowHandle.java | 2 +-
...d_hardware_input_InputApplicationHandle.cpp | 5 +++++
.../com/android/server/wm/ActivityRecord.java | 18 ++++++++++++++++--
.../java/com/android/server/wm/DragState.java | 6 ++----
.../android/server/wm/InputConsumerImpl.java | 6 ++----
.../com/android/server/wm/InputMonitor.java | 14 ++++----------
.../com/android/server/wm/TaskPositioner.java | 6 ++----
.../com/android/server/wm/WindowState.java | 3 ++-
9 files changed, 41 insertions(+), 29 deletions(-)
diff --git a/core/java/android/view/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java
index 3d05e2a0b9f68..9b96f7fd1c5c3 100644
--- a/core/java/android/view/InputApplicationHandle.java
+++ b/core/java/android/view/InputApplicationHandle.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.NonNull;
import android.os.IBinder;
/**
@@ -31,17 +32,20 @@ public final class InputApplicationHandle {
private long ptr;
// Application name.
- public String name;
+ public final @NonNull String name;
// Dispatching timeout.
- public long dispatchingTimeoutNanos;
+ public final long dispatchingTimeoutNanos;
public final IBinder token;
private native void nativeDispose();
- public InputApplicationHandle(IBinder token) {
+ public InputApplicationHandle(@NonNull IBinder token, @NonNull String name,
+ long dispatchingTimeoutNanos) {
this.token = token;
+ this.name = name;
+ this.dispatchingTimeoutNanos = dispatchingTimeoutNanos;
}
public InputApplicationHandle(InputApplicationHandle handle) {
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 5f74b2a510ca5..71d26b8880f7e 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -36,7 +36,7 @@ public final class InputWindowHandle {
private long ptr;
// The input application handle.
- public final InputApplicationHandle inputApplicationHandle;
+ public InputApplicationHandle inputApplicationHandle;
// The token associates input data with a window and its input channel. The client input
// channel and the server input channel will both contain this token.
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 71edfd553e7ea..c1ecae8618273 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -58,6 +58,11 @@ bool NativeInputApplicationHandle::updateInfo() {
if (!obj) {
return false;
}
+ if (mInfo.token.get() != nullptr) {
+ // The java fields are immutable, so it doesn't need to update again.
+ env->DeleteLocalRef(obj);
+ return true;
+ }
mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "");
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 41e48b8ec9db5..af921e2018618 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -416,7 +416,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// mOccludesParent field.
final boolean hasWallpaper;
// Input application handle used by the input dispatcher.
- final InputApplicationHandle mInputApplicationHandle;
+ private InputApplicationHandle mInputApplicationHandle;
final int launchedFromPid; // always the pid who started the activity.
final int launchedFromUid; // always the uid who started the activity.
@@ -1501,7 +1501,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
info = aInfo;
mUserId = UserHandle.getUserId(info.applicationInfo.uid);
packageName = info.applicationInfo.packageName;
- mInputApplicationHandle = new InputApplicationHandle(appToken);
intent = _intent;
// If the class name in the intent doesn't match that of the target, this is probably an
@@ -1685,6 +1684,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return lockTaskLaunchMode;
}
+ @NonNull InputApplicationHandle getInputApplicationHandle(boolean update) {
+ if (mInputApplicationHandle == null) {
+ mInputApplicationHandle = new InputApplicationHandle(appToken, toString(),
+ mInputDispatchingTimeoutNanos);
+ } else if (update) {
+ final String name = toString();
+ if (mInputDispatchingTimeoutNanos != mInputApplicationHandle.dispatchingTimeoutNanos
+ || !name.equals(mInputApplicationHandle.name)) {
+ mInputApplicationHandle = new InputApplicationHandle(appToken, name,
+ mInputDispatchingTimeoutNanos);
+ }
+ }
+ return mInputApplicationHandle;
+ }
+
@Override
ActivityRecord asActivityRecord() {
// I am an activity record!
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index f840d9273f606..7b562a9f44460 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -269,10 +269,8 @@ class DragState {
mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
mService.mH.getLooper(), mDragDropController);
- mDragApplicationHandle = new InputApplicationHandle(new Binder());
- mDragApplicationHandle.name = "drag";
- mDragApplicationHandle.dispatchingTimeoutNanos =
- WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ mDragApplicationHandle = new InputApplicationHandle(new Binder(), "drag",
+ WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS);
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
display.getDisplayId());
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 3b39b6ba18c53..19185736fc892 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -67,10 +67,8 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
}
mService.mInputManager.registerInputChannel(mServerChannel);
- mApplicationHandle = new InputApplicationHandle(new Binder());
- mApplicationHandle.name = name;
- mApplicationHandle.dispatchingTimeoutNanos =
- WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ mApplicationHandle = new InputApplicationHandle(new Binder(), name,
+ WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS);
mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId);
mWindowHandle.name = name;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0216db4718436..fe9bf12ec96a6 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -43,7 +43,6 @@ import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
-import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
@@ -258,6 +257,8 @@ final class InputMonitor {
final boolean hasFocus, final boolean hasWallpaper) {
// Add a window to our list of input windows.
inputWindowHandle.name = child.toString();
+ inputWindowHandle.inputApplicationHandle = child.mActivityRecord != null
+ ? child.mActivityRecord.getInputApplicationHandle(false /* update */) : null;
flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags);
inputWindowHandle.layoutParamsFlags = flags;
inputWindowHandle.layoutParamsType = type;
@@ -376,15 +377,8 @@ final class InputMonitor {
public void setFocusedAppLw(ActivityRecord newApp) {
// Focused app has changed.
- if (newApp == null) {
- mService.mInputManager.setFocusedApplication(mDisplayId, null);
- } else {
- final InputApplicationHandle handle = newApp.mInputApplicationHandle;
- handle.name = newApp.toString();
- handle.dispatchingTimeoutNanos = newApp.mInputDispatchingTimeoutNanos;
-
- mService.mInputManager.setFocusedApplication(mDisplayId, handle);
- }
+ mService.mInputManager.setFocusedApplication(mDisplayId,
+ newApp != null ? newApp.getInputApplicationHandle(true /* update */) : null);
}
public void pauseDispatchingLw(WindowToken window) {
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index c68b660bb76fd..44f4a58af7ab1 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -228,10 +228,8 @@ class TaskPositioner implements IBinder.DeathRecipient {
mClientChannel, mService.mAnimationHandler.getLooper(),
mService.mAnimator.getChoreographer());
- mDragApplicationHandle = new InputApplicationHandle(new Binder());
- mDragApplicationHandle.name = TAG;
- mDragApplicationHandle.dispatchingTimeoutNanos =
- WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ mDragApplicationHandle = new InputApplicationHandle(new Binder(), TAG,
+ WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS);
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
displayContent.getDisplayId());
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 11db705f4e045..f357d7be53fd0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -942,7 +942,8 @@ class WindowState extends WindowContainer implements WindowManagerP
mLastRequestedHeight = 0;
mLayer = 0;
mInputWindowHandle = new InputWindowHandle(
- mActivityRecord != null ? mActivityRecord.mInputApplicationHandle : null,
+ mActivityRecord != null
+ ? mActivityRecord.getInputApplicationHandle(false /* update */) : null,
getDisplayId());
// Make sure we initial all fields before adding to parentWindow, to prevent exception
From 6ff42dd2d264cd89acdf45cf403147a870a14086 Mon Sep 17 00:00:00 2001
From: Dmitry Dementyev
Date: Wed, 9 Dec 2020 07:24:43 -0800
Subject: [PATCH 018/288] Protect account chooser activities against overlay.
Bug: 172841550
Test: manual
Change-Id: I1a16808426934f4a8d12410576d769443e4c2a04
Merged-In: I3cd5a94386f15cf60a7fe3095b00815e4a6485ae
---
core/java/android/accounts/ChooseAccountActivity.java | 7 +++++--
.../android/accounts/ChooseAccountTypeActivity.java | 5 ++++-
.../accounts/ChooseTypeAndAccountActivity.java | 11 ++++++-----
3 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/core/java/android/accounts/ChooseAccountActivity.java b/core/java/android/accounts/ChooseAccountActivity.java
index 4af22bfac92de..b826def55e92c 100644
--- a/core/java/android/accounts/ChooseAccountActivity.java
+++ b/core/java/android/accounts/ChooseAccountActivity.java
@@ -16,7 +16,6 @@
package android.accounts;
import android.app.Activity;
-import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -25,8 +24,8 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.os.Process;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.view.LayoutInflater;
@@ -37,6 +36,7 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+
import com.android.internal.R;
import java.util.HashMap;
@@ -60,6 +60,9 @@ public class ChooseAccountActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ getWindow().addSystemFlags(
+ android.view.WindowManager.LayoutParams
+ .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
mAccounts = getIntent().getParcelableArrayExtra(AccountManager.KEY_ACCOUNTS);
mAccountManagerResponse =
getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_MANAGER_RESPONSE);
diff --git a/core/java/android/accounts/ChooseAccountTypeActivity.java b/core/java/android/accounts/ChooseAccountTypeActivity.java
index e3352bc856685..300fdd4ee4192 100644
--- a/core/java/android/accounts/ChooseAccountTypeActivity.java
+++ b/core/java/android/accounts/ChooseAccountTypeActivity.java
@@ -31,6 +31,7 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+
import com.android.internal.R;
import java.util.ArrayList;
@@ -51,7 +52,9 @@ public class ChooseAccountTypeActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+ getWindow().addSystemFlags(
+ android.view.WindowManager.LayoutParams
+ .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "ChooseAccountTypeActivity.onCreate(savedInstanceState="
+ savedInstanceState + ")");
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 57c1083267064..8cdc6a71caf84 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -15,11 +15,8 @@
*/
package android.accounts;
-import android.app.ActivityTaskManager;
-import com.google.android.collect.Sets;
-
import android.app.Activity;
-import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
@@ -39,6 +36,8 @@ import android.widget.TextView;
import com.android.internal.R;
+import com.google.android.collect.Sets;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
@@ -143,7 +142,9 @@ public class ChooseTypeAndAccountActivity extends Activity
Log.v(TAG, "ChooseTypeAndAccountActivity.onCreate(savedInstanceState="
+ savedInstanceState + ")");
}
-
+ getWindow().addSystemFlags(
+ android.view.WindowManager.LayoutParams
+ .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
String message = null;
try {
From fedbadf57eeeb82813d8fd43aade685659b909c2 Mon Sep 17 00:00:00 2001
From: Dmitry Dementyev
Date: Wed, 9 Dec 2020 14:58:46 -0800
Subject: [PATCH 019/288] Protect account chooser activities against overlay.
Bug: 172841550
Test: manual
Merged-In: I1a16808426934f4a8d12410576d769443e4c2a04
Merged-In: I3cd5a94386f15cf60a7fe3095b00815e4a6485ae
Change-Id: I35dc86b5721a4531447a6d99d6c30f23543130cb
---
core/java/android/accounts/ChooseAccountActivity.java | 6 +++++-
core/java/android/accounts/ChooseAccountTypeActivity.java | 5 ++++-
.../android/accounts/ChooseTypeAndAccountActivity.java | 8 +++++---
3 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/core/java/android/accounts/ChooseAccountActivity.java b/core/java/android/accounts/ChooseAccountActivity.java
index 6a436869a0ec9..0502087e14d71 100644
--- a/core/java/android/accounts/ChooseAccountActivity.java
+++ b/core/java/android/accounts/ChooseAccountActivity.java
@@ -24,8 +24,8 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.os.Process;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.view.LayoutInflater;
@@ -36,6 +36,7 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+
import com.android.internal.R;
import java.util.HashMap;
@@ -59,6 +60,9 @@ public class ChooseAccountActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ getWindow().addPrivateFlags(
+ android.view.WindowManager.LayoutParams
+ .PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
mAccounts = getIntent().getParcelableArrayExtra(AccountManager.KEY_ACCOUNTS);
mAccountManagerResponse =
getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_MANAGER_RESPONSE);
diff --git a/core/java/android/accounts/ChooseAccountTypeActivity.java b/core/java/android/accounts/ChooseAccountTypeActivity.java
index e3352bc856685..4b998e86fc8a1 100644
--- a/core/java/android/accounts/ChooseAccountTypeActivity.java
+++ b/core/java/android/accounts/ChooseAccountTypeActivity.java
@@ -31,6 +31,7 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+
import com.android.internal.R;
import java.util.ArrayList;
@@ -51,7 +52,9 @@ public class ChooseAccountTypeActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+ getWindow().addPrivateFlags(
+ android.view.WindowManager.LayoutParams
+ .PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "ChooseAccountTypeActivity.onCreate(savedInstanceState="
+ savedInstanceState + ")");
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 6680ce6acb043..887ba18822f89 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -15,8 +15,6 @@
*/
package android.accounts;
-import com.google.android.collect.Sets;
-
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Intent;
@@ -38,6 +36,8 @@ import android.widget.TextView;
import com.android.internal.R;
+import com.google.android.collect.Sets;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
@@ -142,7 +142,9 @@ public class ChooseTypeAndAccountActivity extends Activity
Log.v(TAG, "ChooseTypeAndAccountActivity.onCreate(savedInstanceState="
+ savedInstanceState + ")");
}
-
+ getWindow().addPrivateFlags(
+ android.view.WindowManager.LayoutParams
+ .PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
String message = null;
try {
From d0ad4beb52aed0352dbe9b27fece992df5962a89 Mon Sep 17 00:00:00 2001
From: Robert Carr
Date: Fri, 11 Dec 2020 12:59:08 -0800
Subject: [PATCH 020/288] DO NOT MERGE: WM: Only allow system to use
NO_INPUT_CHANNEL.
NO_INPUT_CHANNEL is a hidden WM flag that allows creation of a window
without an input channel. Unfortunately in releases prior to Android R
this would allow creation of a Window which will not be known to the
InputDispatcher at all. This means that the logic generating
FLAG_OBSCURED will work and a window will be able to overlay another
window without the overlayed window being notified. In Android R and
later this isn't a problem as the InputDispatcher is informed of all
windows, input channel or not. For past Android releases, this patch
disables NO_INPUT_CHANNEL for use outside of the WM.
Bug: 152064592
Test: Existing tests pass
Change-Id: I7e1f45cba139eab92e7df88d1e052baba0ae2cc6
---
core/java/android/view/InputChannel.java | 7 +++++++
core/java/android/view/ViewRootImpl.java | 7 ++-----
.../java/com/android/server/wm/WindowManagerService.java | 9 +++++++--
3 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index ecb727c790160..346628bbdabda 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -110,6 +110,13 @@ public final class InputChannel implements Parcelable {
return name != null ? name : "uninitialized";
}
+ /**
+ * @hide
+ */
+ public boolean isValid() {
+ return mPtr != 0;
+ }
+
/**
* Disposes the input channel.
* Explicitly releases the reference this object is holding on the input channel.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 57a01a32e1b83..afc4ff4377780 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -875,10 +875,7 @@ public final class ViewRootImpl implements ViewParent,
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
- if ((mWindowAttributes.inputFeatures
- & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
- mInputChannel = new InputChannel();
- }
+ mInputChannel = new InputChannel();
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
@@ -971,7 +968,7 @@ public final class ViewRootImpl implements ViewParent,
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
- if (mInputChannel != null) {
+ if (mInputChannel.isValid()) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a9f157d985a61..d08533e3ee036 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1462,8 +1462,13 @@ public class WindowManagerService extends IWindowManager.Stub
return res;
}
- final boolean openInputChannels = (outInputChannel != null
- && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
+ boolean openInputChannels = (outInputChannel != null
+ && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
+ if (callingUid != SYSTEM_UID) {
+ Slog.e(TAG_WM,
+ "App trying to use insecure INPUT_FEATURE_NO_INPUT_CHANNEL flag. Ignoring");
+ openInputChannels = true;
+ }
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
From 0fed2d8e7e2b9757c7c40cd445772d52398478b1 Mon Sep 17 00:00:00 2001
From: Robert Carr
Date: Fri, 11 Dec 2020 12:59:08 -0800
Subject: [PATCH 021/288] DO NOT MERGE: WM: Only allow system to use
NO_INPUT_CHANNEL.
NO_INPUT_CHANNEL is a hidden WM flag that allows creation of a window
without an input channel. Unfortunately in releases prior to Android R
this would allow creation of a Window which will not be known to the
InputDispatcher at all. This means that the logic generating
FLAG_OBSCURED will work and a window will be able to overlay another
window without the overlayed window being notified. In Android R and
later this isn't a problem as the InputDispatcher is informed of all
windows, input channel or not. For past Android releases, this patch
disables NO_INPUT_CHANNEL for use outside of the WM.
Bug: 152064592
Test: Existing tests pass
Change-Id: I7e1f45cba139eab92e7df88d1e052baba0ae2cc6
---
core/java/android/view/InputChannel.java | 7 +++++++
core/java/android/view/ViewRootImpl.java | 7 ++-----
.../java/com/android/server/wm/WindowManagerService.java | 9 +++++++--
3 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index de195ae524c8b..6223bc10222c4 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -103,6 +103,13 @@ public final class InputChannel implements Parcelable {
return name != null ? name : "uninitialized";
}
+ /**
+ * @hide
+ */
+ public boolean isValid() {
+ return mPtr != 0;
+ }
+
/**
* Disposes the input channel.
* Explicitly releases the reference this object is holding on the input channel.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a1c0967f0ab17..950fb125366f6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -745,10 +745,7 @@ public final class ViewRootImpl implements ViewParent,
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
- if ((mWindowAttributes.inputFeatures
- & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
- mInputChannel = new InputChannel();
- }
+ mInputChannel = new InputChannel();
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
@@ -838,7 +835,7 @@ public final class ViewRootImpl implements ViewParent,
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
- if (mInputChannel != null) {
+ if (mInputChannel.isValid()) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b438d044d102c..96379488ba684 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1348,8 +1348,13 @@ public class WindowManagerService extends IWindowManager.Stub
return res;
}
- final boolean openInputChannels = (outInputChannel != null
- && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
+ boolean openInputChannels = (outInputChannel != null
+ && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
+ if (callingUid != SYSTEM_UID) {
+ Slog.e(TAG_WM,
+ "App trying to use insecure INPUT_FEATURE_NO_INPUT_CHANNEL flag. Ignoring");
+ openInputChannels = true;
+ }
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
From 514b3297768f4a7469117a7005556663124040f9 Mon Sep 17 00:00:00 2001
From: Robert Carr
Date: Fri, 11 Dec 2020 12:59:08 -0800
Subject: [PATCH 022/288] DO NOT MERGE: WM: Only allow system to use
NO_INPUT_CHANNEL.
NO_INPUT_CHANNEL is a hidden WM flag that allows creation of a window
without an input channel. Unfortunately in releases prior to Android R
this would allow creation of a Window which will not be known to the
InputDispatcher at all. This means that the logic generating
FLAG_OBSCURED will work and a window will be able to overlay another
window without the overlayed window being notified. In Android R and
later this isn't a problem as the InputDispatcher is informed of all
windows, input channel or not. For past Android releases, this patch
disables NO_INPUT_CHANNEL for use outside of the WM.
Bug: 152064592
Test: Existing tests pass
Change-Id: I7e1f45cba139eab92e7df88d1e052baba0ae2cc6
---
core/java/android/view/InputChannel.java | 7 +++++++
core/java/android/view/ViewRootImpl.java | 7 ++-----
.../java/com/android/server/wm/WindowManagerService.java | 9 +++++++--
3 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index ecb727c790160..346628bbdabda 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -110,6 +110,13 @@ public final class InputChannel implements Parcelable {
return name != null ? name : "uninitialized";
}
+ /**
+ * @hide
+ */
+ public boolean isValid() {
+ return mPtr != 0;
+ }
+
/**
* Disposes the input channel.
* Explicitly releases the reference this object is holding on the input channel.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index aceb276e9fc04..735678cd096d6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -851,10 +851,7 @@ public final class ViewRootImpl implements ViewParent,
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
- if ((mWindowAttributes.inputFeatures
- & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
- mInputChannel = new InputChannel();
- }
+ mInputChannel = new InputChannel();
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
@@ -947,7 +944,7 @@ public final class ViewRootImpl implements ViewParent,
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
- if (mInputChannel != null) {
+ if (mInputChannel.isValid()) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2de8ae155f817..11e89fbe5b6a1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1441,8 +1441,13 @@ public class WindowManagerService extends IWindowManager.Stub
return res;
}
- final boolean openInputChannels = (outInputChannel != null
- && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
+ boolean openInputChannels = (outInputChannel != null
+ && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
+ if (callingUid != SYSTEM_UID) {
+ Slog.e(TAG_WM,
+ "App trying to use insecure INPUT_FEATURE_NO_INPUT_CHANNEL flag. Ignoring");
+ openInputChannels = true;
+ }
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
From 9661bf7a40d33470bcd35f15758ab71c7c635c63 Mon Sep 17 00:00:00 2001
From: Robert Carr
Date: Fri, 11 Dec 2020 12:59:08 -0800
Subject: [PATCH 023/288] DO NOT MERGE: WM: Only allow system to use
NO_INPUT_CHANNEL.
NO_INPUT_CHANNEL is a hidden WM flag that allows creation of a window
without an input channel. Unfortunately in releases prior to Android R
this would allow creation of a Window which will not be known to the
InputDispatcher at all. This means that the logic generating
FLAG_OBSCURED will work and a window will be able to overlay another
window without the overlayed window being notified. In Android R and
later this isn't a problem as the InputDispatcher is informed of all
windows, input channel or not. For past Android releases, this patch
disables NO_INPUT_CHANNEL for use outside of the WM.
Bug: 152064592
Test: Existing tests pass
Change-Id: I7e1f45cba139eab92e7df88d1e052baba0ae2cc6
---
core/java/android/view/InputChannel.java | 7 +++++++
core/java/android/view/ViewRootImpl.java | 7 ++-----
.../java/com/android/server/wm/WindowManagerService.java | 9 +++++++--
3 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index de195ae524c8b..6223bc10222c4 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -103,6 +103,13 @@ public final class InputChannel implements Parcelable {
return name != null ? name : "uninitialized";
}
+ /**
+ * @hide
+ */
+ public boolean isValid() {
+ return mPtr != 0;
+ }
+
/**
* Disposes the input channel.
* Explicitly releases the reference this object is holding on the input channel.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3f1ea34c37ab0..51d6c09fcc77f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -713,10 +713,7 @@ public final class ViewRootImpl implements ViewParent,
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
- if ((mWindowAttributes.inputFeatures
- & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
- mInputChannel = new InputChannel();
- }
+ mInputChannel = new InputChannel();
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
@@ -805,7 +802,7 @@ public final class ViewRootImpl implements ViewParent,
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
- if (mInputChannel != null) {
+ if (mInputChannel.isValid()) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c0e56688bf8ad..756af3cad50e0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1413,8 +1413,13 @@ public class WindowManagerService extends IWindowManager.Stub
return res;
}
- final boolean openInputChannels = (outInputChannel != null
- && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
+ boolean openInputChannels = (outInputChannel != null
+ && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
+ if (callingUid != SYSTEM_UID) {
+ Slog.e(TAG_WM,
+ "App trying to use insecure INPUT_FEATURE_NO_INPUT_CHANNEL flag. Ignoring");
+ openInputChannels = true;
+ }
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
From 3995f0db93b6b5015fbdb6d89eefcc1952999b57 Mon Sep 17 00:00:00 2001
From: Dave Mankoff
Date: Fri, 4 Dec 2020 17:42:20 -0500
Subject: [PATCH 024/288] Unregister Touch Sensors for DOZE_AOD_DOCKED
DOZE_AOD_DOCKED should not have screen based trigger sensors
enabled. In fact, it can end up in inconsistent states currently.
When first entering it, the sensors are not enabled. But if the screen
then turns off (in a dark room) and then back on, the sensors are
left on from the prior state.
This change ensures that, when we ask to turn off touch based sensors,
they actually turn off.
Fixes: 172412931
Test: manual
Change-Id: I86f800cb75fee9f2d7e60655af4803e616a68fb2
Merged-In: I86f800cb75fee9f2d7e60655af4803e616a68fb2
---
.../android/systemui/doze/DozeSensors.java | 23 ++++++++++++------
.../android/systemui/doze/DozeTriggers.java | 24 ++++++++-----------
.../systemui/doze/DozeSensorsTest.java | 10 ++++----
.../systemui/doze/DozeTriggersTest.java | 1 +
4 files changed, 31 insertions(+), 27 deletions(-)
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index aebf41b884c4f..2eadbd0365e46 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -46,6 +46,7 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -80,6 +81,7 @@ public class DozeSensors {
private long mDebounceFrom;
private boolean mSettingRegistered;
private boolean mListening;
+ private boolean mListeningTouchScreenSensors;
@VisibleForTesting
public enum DozeSensorsUiEvent implements UiEventLogger.UiEventEnum {
@@ -222,22 +224,25 @@ public class DozeSensors {
/**
* If sensors should be registered and sending signals.
*/
- public void setListening(boolean listen) {
- if (mListening == listen) {
+ public void setListening(boolean listen, boolean includeTouchScreenSensors) {
+ if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors) {
return;
}
mListening = listen;
+ mListeningTouchScreenSensors = includeTouchScreenSensors;
updateListening();
}
/**
* Registers/unregisters sensors based on internal state.
*/
- public void updateListening() {
+ private void updateListening() {
boolean anyListening = false;
for (TriggerSensor s : mSensors) {
- s.setListening(mListening);
- if (mListening) {
+ boolean listen = mListening
+ && (!s.mRequiresTouchscreen || mListeningTouchScreenSensors);
+ s.setListening(listen);
+ if (listen) {
anyListening = true;
}
}
@@ -309,10 +314,14 @@ public class DozeSensors {
/** Dump current state */
public void dump(PrintWriter pw) {
+ pw.println("mListening=" + mListening);
+ pw.println("mListeningTouchScreenSensors=" + mListeningTouchScreenSensors);
+ IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " ");
+ idpw.increaseIndent();
for (TriggerSensor s : mSensors) {
- pw.println(" Sensor: " + s.toString());
+ idpw.println("Sensor: " + s.toString());
}
- pw.println(" ProxSensor: " + mProximitySensor.toString());
+ idpw.println("ProxSensor: " + mProximitySensor.toString());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index cbf8f578744c2..043edee44492f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -38,6 +38,7 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.systemui.Dependency;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
@@ -408,15 +409,12 @@ public class DozeTriggers implements DozeMachine.Part {
break;
case DOZE_PULSE_DONE:
mDozeSensors.requestTemporaryDisable();
- // A pulse will temporarily disable sensors that require a touch screen.
- // Let's make sure that they are re-enabled when the pulse is over.
- mDozeSensors.updateListening();
break;
case FINISH:
mBroadcastReceiver.unregister(mBroadcastDispatcher);
mDozeHost.removeCallback(mHostCallback);
mDockManager.removeListener(mDockEventListener);
- mDozeSensors.setListening(false);
+ mDozeSensors.setListening(false, false);
mDozeSensors.setProxListening(false);
mWantSensors = false;
mWantProx = false;
@@ -424,20 +422,16 @@ public class DozeTriggers implements DozeMachine.Part {
break;
default:
}
+ mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors);
}
@Override
public void onScreenState(int state) {
mDozeSensors.onScreenState(state);
- if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND
- || state == Display.STATE_OFF) {
- mDozeSensors.setProxListening(mWantProx);
- mDozeSensors.setListening(mWantSensors);
- mDozeSensors.setTouchscreenSensorsListening(mWantTouchScreenSensors);
- } else {
- mDozeSensors.setProxListening(false);
- mDozeSensors.setListening(mWantSensors);
- }
+ mDozeSensors.setProxListening(mWantProx && (state == Display.STATE_DOZE
+ || state == Display.STATE_DOZE_SUSPEND
+ || state == Display.STATE_OFF));
+ mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors);
}
private void checkTriggersAtInit() {
@@ -513,7 +507,9 @@ public class DozeTriggers implements DozeMachine.Part {
pw.println(" pulsePending=" + mPulsePending);
pw.println("DozeSensors:");
- mDozeSensors.dump(pw);
+ IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " ");
+ idpw.increaseIndent();
+ mDozeSensors.dump(idpw);
}
private class TriggerReceiver extends BroadcastReceiver {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index ebd2c3afe646a..35e3bb392f7c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -77,8 +77,6 @@ public class DozeSensorsTest extends SysuiTestCase {
@Mock
private Consumer mProxCallback;
@Mock
- private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
- @Mock
private TriggerSensor mTriggerSensor;
@Mock
private DozeLog mDozeLog;
@@ -110,7 +108,7 @@ public class DozeSensorsTest extends SysuiTestCase {
@Test
public void testSensorDebounce() {
- mDozeSensors.setListening(true);
+ mDozeSensors.setListening(true, true);
mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
mTestableLooper.processAllMessages();
@@ -128,7 +126,7 @@ public class DozeSensorsTest extends SysuiTestCase {
@Test
public void testSetListening_firstTrue_registerSettingsObserver() {
verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt());
- mDozeSensors.setListening(true);
+ mDozeSensors.setListening(true, true);
verify(mTriggerSensor).registerSettingsObserver(any(ContentObserver.class));
}
@@ -136,8 +134,8 @@ public class DozeSensorsTest extends SysuiTestCase {
@Test
public void testSetListening_twiceTrue_onlyRegisterSettingsObserverOnce() {
verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt());
- mDozeSensors.setListening(true);
- mDozeSensors.setListening(true);
+ mDozeSensors.setListening(true, true);
+ mDozeSensors.setListening(true, true);
verify(mTriggerSensor, times(1)).registerSettingsObserver(any(ContentObserver.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 655f933d28fe4..be4fdb0babc6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -149,6 +149,7 @@ public class DozeTriggersTest extends SysuiTestCase {
clearInvocations(mSensors);
mTriggers.transitionTo(DozeMachine.State.DOZE_PULSING, DozeMachine.State.DOZE_PULSE_DONE);
+ mTriggers.transitionTo(DozeMachine.State.DOZE_PULSE_DONE, DozeMachine.State.DOZE_AOD);
waitForSensorManager();
verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor));
}
From 346eed7325d47d8ed453266f613ccad1b4260c67 Mon Sep 17 00:00:00 2001
From: Hui Yu
Date: Thu, 6 Aug 2020 16:55:19 -0700
Subject: [PATCH 025/288] RESTRICT AUTOMERGE: Set
mAllowWhileInUsePermissionInFgs correctly when bindService() from background.
Pass original callingPid and callingUid into
shouldAllowWhileInUsePermissionInFgsLocked() after
Binder.clearCallingIdentity().
Bug: 163084228, 173516292
Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testFgsLocationStartFromBGWithBind
Change-Id: I7a240621267648ea54e7dc737ebc55bc665e39b6
Merged-In: I7a240621267648ea54e7dc737ebc55bc665e39b6
(cherry picked from commit 86bd39db3595842bae77abe7e768226e412591c8)
---
.../java/com/android/server/am/ActiveServices.java | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 3f867f656c245..dd0e1f6458f99 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1836,11 +1836,13 @@ public final class ActiveServices {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
+ " type=" + resolvedType + " conn=" + connection.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
- + " (pid=" + Binder.getCallingPid()
+ + " (pid=" + callingPid
+ ") when binding service " + service);
}
@@ -1880,19 +1882,19 @@ public final class ActiveServices {
}
if ((flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0 && !isCallerSystem) {
- throw new SecurityException("Non-system caller (pid=" + Binder.getCallingPid()
+ throw new SecurityException("Non-system caller (pid=" + callingPid
+ ") set BIND_SCHEDULE_LIKE_TOP_APP when binding service " + service);
}
if ((flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0 && !isCallerSystem) {
throw new SecurityException(
- "Non-system caller " + caller + " (pid=" + Binder.getCallingPid()
+ "Non-system caller " + caller + " (pid=" + callingPid
+ ") set BIND_ALLOW_WHITELIST_MANAGEMENT when binding service " + service);
}
if ((flags & Context.BIND_ALLOW_INSTANT) != 0 && !isCallerSystem) {
throw new SecurityException(
- "Non-system caller " + caller + " (pid=" + Binder.getCallingPid()
+ "Non-system caller " + caller + " (pid=" + callingPid
+ ") set BIND_ALLOW_INSTANT when binding service " + service);
}
@@ -1908,7 +1910,7 @@ public final class ActiveServices {
ServiceLookupResult res =
retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
- Binder.getCallingPid(), Binder.getCallingUid(), userId, true,
+ callingPid, callingUid, userId, true,
callerFg, isBindExternal, allowInstant);
if (res == null) {
return 0;
@@ -2068,7 +2070,7 @@ public final class ActiveServices {
if (!s.mAllowWhileInUsePermissionInFgs) {
s.mAllowWhileInUsePermissionInFgs =
shouldAllowWhileInUsePermissionInFgsLocked(callingPackage,
- Binder.getCallingPid(), Binder.getCallingUid(),
+ callingPid, callingUid,
service, s, false);
}
From 23abf11a3a8518a665f12f173c53d77993a27a32 Mon Sep 17 00:00:00 2001
From: Jack Yu
Date: Fri, 13 Nov 2020 18:34:42 +0800
Subject: [PATCH 026/288] Nfc adapter randomly returns null on devices having
Nfc
Try to get NfcAdapter from NfcManager only when nfc service interface is
ready.
Bug: 173184157
Test: manaul test
Change-Id: Ifcb0f60e23f01cafd8b6b5f6bc8522773ece23a6
(cherry picked from commit 3217255a4d1f6dcbc28a0351dc74f136331f2d39)
---
core/java/android/nfc/NfcAdapter.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index c61f10f50c93b..dc4ce039bf77e 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -677,6 +677,12 @@ public final class NfcAdapter {
throw new IllegalArgumentException(
"context not associated with any application (using a mock context?)");
}
+
+ if (getServiceInterface() == null) {
+ // NFC is not available
+ return null;
+ }
+
/* use getSystemService() for consistency */
NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
if (manager == null) {
From 7d5eb9543fd3deba691cf0994f7add204e26f1b7 Mon Sep 17 00:00:00 2001
From: Beth Thibodeau
Date: Mon, 21 Dec 2020 15:46:01 -0500
Subject: [PATCH 027/288] Use default color when artwork is too small
Fixes: 175833333
Test: atest SystemUITests:MediaDataManagerTest
Change-Id: I12e0bd8c32f3f5f824f83312d4b3b3cf181fd486
(cherry picked from commit 8d15cc48fb51e359d7c7296be19d4c1ac212a3b9)
---
.../systemui/media/MediaDataManager.kt | 9 +++++---
.../systemui/media/MediaDataManagerTest.kt | 23 +++++++++++++++++++
2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 2be509a138163..2d7aafc497d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -70,6 +70,7 @@ private const val DEBUG = true
private const val DEFAULT_LUMINOSITY = 0.25f
private const val LUMINOSITY_THRESHOLD = 0.05f
private const val SATURATION_MULTIPLIER = 0.8f
+const val DEFAULT_COLOR = Color.DKGRAY
private val LOADING = MediaData(-1, false, 0, null, null, null, null, null,
emptyList(), emptyList(), "INVALID", null, null, null, true, null)
@@ -380,7 +381,7 @@ class MediaDataManager(
} else {
null
}
- val bgColor = artworkBitmap?.let { computeBackgroundColor(it) } ?: Color.DKGRAY
+ val bgColor = artworkBitmap?.let { computeBackgroundColor(it) } ?: DEFAULT_COLOR
val mediaAction = getResumeMediaAction(resumeAction)
foregroundExecutor.execute {
@@ -559,12 +560,14 @@ class MediaDataManager(
private fun computeBackgroundColor(artworkBitmap: Bitmap?): Int {
var color = Color.WHITE
- if (artworkBitmap != null) {
- // If we have art, get colors from that
+ if (artworkBitmap != null && artworkBitmap.width > 1 && artworkBitmap.height > 1) {
+ // If we have valid art, get colors from that
val p = MediaNotificationProcessor.generateArtworkPaletteBuilder(artworkBitmap)
.generate()
val swatch = MediaNotificationProcessor.findBackgroundSwatch(p)
color = swatch.rgb
+ } else {
+ return DEFAULT_COLOR
}
// Adapt background color, so it's always subdued and text is legible
val tmpHsl = floatArrayOf(0f, 0f, 0f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index b47ee29213803..2f99b2a2a449f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -2,6 +2,7 @@ package com.android.systemui.media
import android.app.Notification.MediaStyle
import android.app.PendingIntent
+import android.graphics.Bitmap
import android.media.MediaDescription
import android.media.MediaMetadata
import android.media.session.MediaController
@@ -246,4 +247,26 @@ class MediaDataManagerTest : SysuiTestCase() {
verify(listener).onMediaDataRemoved(eq(KEY))
}
+
+ @Test
+ fun testBadArtwork_doesNotUse() {
+ // WHEN notification has a too-small artwork
+ val artwork = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+ val notif = SbnBuilder().run {
+ setPkg(PACKAGE_NAME)
+ modifyNotification(context).also {
+ it.setSmallIcon(android.R.drawable.ic_media_pause)
+ it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+ it.setLargeIcon(artwork)
+ }
+ build()
+ }
+ mediaDataManager.onNotificationAdded(KEY, notif)
+
+ // THEN it loads and uses the default background color
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
+ assertThat(mediaDataCaptor.value!!.backgroundColor).isEqualTo(DEFAULT_COLOR)
+ }
}
From f20d333adacbf0d0876161f739a6e9f9de1de56c Mon Sep 17 00:00:00 2001
From: Varun Shah
Date: Tue, 29 Dec 2020 15:24:43 -0800
Subject: [PATCH 028/288] DO NOT MERGE: Disable StrictMode VM Policy check.
Disable the VM policy violation for intents that contain a content URI
but don't have FLAG_GRANT_URI_PERMISSION when scheduling alarms.
Fixes: 160426113
Test: atest CalendarProvider2Test
Change-Id: If6e4a03609fc3d03dde66bcaa3136b23662ccaeb
(cherry picked from commit 224822ecafdf09a3a2cda11d95645f3056ab58dc)
---
core/java/android/provider/CalendarContract.java | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 17fae1cafe62f..5e3f852229b7d 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -39,6 +39,7 @@ import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.RemoteException;
+import android.os.StrictMode;
import android.text.format.DateUtils;
import android.text.format.TimeMigrationUtils;
import android.util.Log;
@@ -2618,7 +2619,13 @@ public final class CalendarContract {
intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime));
intent.putExtra(ALARM_TIME, alarmTime);
intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+
+ // Disable strict mode VM policy violations temporarily for intents that contain a
+ // content URI but don't have FLAG_GRANT_READ_URI_PERMISSION.
+ StrictMode.VmPolicy oldVmPolicy = StrictMode.allowVmViolations();
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
+ StrictMode.setVmPolicy(oldVmPolicy);
+
manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pi);
}
From ab6fd7362d48745785e89d10979d8d1f43616220 Mon Sep 17 00:00:00 2001
From: Danny Epstein
Date: Mon, 14 Dec 2020 18:14:59 -0800
Subject: [PATCH 029/288] DO NOT MERGE Focus clock by default
When nudging up to the status bar, focus the clock by default.
Test: Nudge up to status bar, switch from Launcher to Home
Bug: 169250454
Change-Id: Id55c236a447b86d502d46e799814b51c984916e4
---
packages/CarSystemUI/res/layout/car_top_navigation_bar.xml | 4 ++--
.../res/layout/car_top_navigation_bar_unprovisioned.xml | 3 ++-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index cdc29eec21cde..cb1aadaf7fe74 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -77,8 +77,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
- systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$QuickSettingActivity;launchFlags=0x24000000;end"
- />
+ android:focusedByDefault="true"
+ systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$QuickSettingActivity;launchFlags=0x24000000;end"/>
+ android:background="@null"
+ android:focusedByDefault="true"/>
Date: Tue, 29 Dec 2020 10:42:33 -0800
Subject: [PATCH 030/288] Clean up dead code in NetInitiatedActivity
Bug: 174151048
Test: on device
Change-Id: I5a3a77557720571b455ffec296cdd4e2e1a02f7b
(cherry picked from commit b5765cc3e75d9f4a7ff9e805b2225de5790d3cfe)
---
.../internal/app/NetInitiatedActivity.java | 31 -------------------
.../location/GpsNetInitiatedHandler.java | 3 --
2 files changed, 34 deletions(-)
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 92e9fe492442d..5efeb0fc928e7 100644
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -17,18 +17,14 @@
package com.android.internal.app;
import android.app.AlertDialog;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.IntentFilter;
import android.location.LocationManagerInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
-import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.location.GpsNetInitiatedHandler;
@@ -43,7 +39,6 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa
private static final String TAG = "NetInitiatedActivity";
private static final boolean DEBUG = true;
- private static final boolean VERBOSE = false;
private static final int POSITIVE_BUTTON = AlertDialog.BUTTON_POSITIVE;
private static final int NEGATIVE_BUTTON = AlertDialog.BUTTON_NEGATIVE;
@@ -55,17 +50,6 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa
private int default_response = -1;
private int default_response_timeout = 6;
- /** Used to detect when NI request is received */
- private BroadcastReceiver mNetInitiatedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) Log.d(TAG, "NetInitiatedReceiver onReceive: " + intent.getAction());
- if (intent.getAction() == GpsNetInitiatedHandler.ACTION_NI_VERIFY) {
- handleNIVerify(intent);
- }
- }
- };
-
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -109,14 +93,12 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa
protected void onResume() {
super.onResume();
if (DEBUG) Log.d(TAG, "onResume");
- registerReceiver(mNetInitiatedReceiver, new IntentFilter(GpsNetInitiatedHandler.ACTION_NI_VERIFY));
}
@Override
protected void onPause() {
super.onPause();
if (DEBUG) Log.d(TAG, "onPause");
- unregisterReceiver(mNetInitiatedReceiver);
}
/**
@@ -141,17 +123,4 @@ public class NetInitiatedActivity extends AlertActivity implements DialogInterfa
LocationManagerInternal lm = LocalServices.getService(LocationManagerInternal.class);
lm.sendNiResponse(notificationId, response);
}
-
- @UnsupportedAppUsage
- private void handleNIVerify(Intent intent) {
- int notifId = intent.getIntExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_NOTIF_ID, -1);
- notificationId = notifId;
-
- if (DEBUG) Log.d(TAG, "handleNIVerify action: " + intent.getAction());
- }
-
- private void showNIError() {
- Toast.makeText(this, "NI error" /* com.android.internal.R.string.usb_storage_error_message */,
- Toast.LENGTH_LONG).show();
- }
}
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 67a040dba3e7f..a3765151a6f74 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -51,9 +51,6 @@ public class GpsNetInitiatedHandler {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- // NI verify activity for bringing up UI (not used yet)
- public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY";
-
// string constants for defining data fields in NI Intent
public static final String NI_INTENT_KEY_NOTIF_ID = "notif_id";
public static final String NI_INTENT_KEY_TITLE = "title";
From 72704403aabf41e2807cc67388faafdf4457f924 Mon Sep 17 00:00:00 2001
From: Alex Kershaw
Date: Sun, 29 Nov 2020 18:21:57 +0000
Subject: [PATCH 031/288] [RESTRICT AUTOMERGE] Make WPMS look for DOs and POs
in the correct calling user
Currently, it will always look in user 0 since it uses the DPM from
mContext, which will always be from user 0 as WPMS is in the system
server process.
Extend DPMI to provide the necessary external helper API. This is
preferable to just using createContextAsUser before getting the DPM
instance since it avoids a second binding.
Fixes: 144048540
Fixes: 172682826
Bug: 153995973
Bug: 174642338
Test: atest com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testSetWallpaper_disallowed
Change-Id: I52b71000fac31ff6725ddded58206f69b263ae33
(cherry picked from commit 5b36ee3f1d2c60473f4c30161cca6a62cd9ea71f)
---
.../admin/DevicePolicyManagerInternal.java | 7 +++++++
.../wallpaper/WallpaperManagerService.java | 10 +++++-----
.../DevicePolicyManagerService.java | 20 +++++++++++++++++++
3 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 62ac84b2b1e60..d24694faff930 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -16,6 +16,7 @@
package android.app.admin;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
@@ -221,6 +222,7 @@ public abstract class DevicePolicyManagerInternal {
/**
* Returns the profile owner component for the given user, or {@code null} if there is not one.
*/
+ @Nullable
public abstract ComponentName getProfileOwnerAsUser(int userHandle);
/**
@@ -234,4 +236,9 @@ public abstract class DevicePolicyManagerInternal {
* {@link #supportsResetOp(int)} is true.
*/
public abstract void resetOp(int op, String packageName, @UserIdInt int userId);
+
+ /**
+ * Returns whether the given package is a device owner or a profile owner in the calling user.
+ */
+ public abstract boolean isDeviceOrProfileOwnerInCallingUser(String packageName);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 1300e657333c7..aaf27d32452d5 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -39,7 +39,7 @@ import android.app.WallpaperColors;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
import android.app.WallpaperManager.SetWallpaperFlags;
-import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.backup.WallpaperBackupHelper;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -2861,10 +2861,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (!uidMatchPackage) {
return false; // callingPackage was faked.
}
-
- // TODO(b/144048540): DPM needs to take into account the userId, not just the package.
- final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) {
+ DevicePolicyManagerInternal devicePolicyManagerInternal =
+ LocalServices.getService(DevicePolicyManagerInternal.class);
+ if (devicePolicyManagerInternal != null &&
+ devicePolicyManagerInternal.isDeviceOrProfileOwnerInCallingUser(callingPackage)) {
return true;
}
final int callingUserId = UserHandle.getCallingUserId();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b8a20ed4fe5b7..86e130577ea8f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -12839,6 +12839,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
? AppOpsManager.MODE_ALLOWED
: AppOpsManager.opToDefaultMode(AppOpsManager.OP_INTERACT_ACROSS_PROFILES);
}
+
+ public boolean isDeviceOrProfileOwnerInCallingUser(String packageName) {
+ return isDeviceOwnerInCallingUser(packageName)
+ || isProfileOwnerInCallingUser(packageName);
+ }
+
+ private boolean isDeviceOwnerInCallingUser(String packageName) {
+ final ComponentName deviceOwnerInCallingUser =
+ DevicePolicyManagerService.this.getDeviceOwnerComponent(
+ /* callingUserOnly= */ true);
+ return deviceOwnerInCallingUser != null
+ && packageName.equals(deviceOwnerInCallingUser.getPackageName());
+ }
+
+ private boolean isProfileOwnerInCallingUser(String packageName) {
+ final ComponentName profileOwnerInCallingUser =
+ getProfileOwnerAsUser(UserHandle.getCallingUserId());
+ return profileOwnerInCallingUser != null
+ && packageName.equals(profileOwnerInCallingUser.getPackageName());
+ }
}
private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
From e8bd5284d33386d1c2a7d3109d9ad6cbd08e3423 Mon Sep 17 00:00:00 2001
From: Tiger Huang
Date: Thu, 3 Sep 2020 23:01:21 +0800
Subject: [PATCH 032/288] Notify visibility change if source visibility is
changed
This makes view root dispatch the up-to-date WindowInsets to the app.
Fix: 166619182
Test: Steps in the bug (comment#12)
Change-Id: I8c47e3be84357f65084a7d9994f7d7b68f15611e
(cherry picked from commit fd9435130557b054d203835eab6f287fee58ec1b)
---
core/java/android/view/InsetsSourceConsumer.java | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 700dc66fab556..ba40459692f79 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -113,13 +113,20 @@ public class InsetsSourceConsumer {
InsetsState.typeToString(control.getType()),
mController.getHost().getRootViewTitle()));
}
- // We are loosing control
if (mSourceControl == null) {
+ // We are loosing control
mController.notifyControlRevoked(this);
- // Restore server visibility.
- mState.getSource(getType()).setVisible(
- mController.getLastDispatchedState().getSource(getType()).isVisible());
+ // Check if we need to restore server visibility.
+ final InsetsSource source = mState.getSource(mType);
+ final boolean serverVisibility =
+ mController.getLastDispatchedState().getSourceOrDefaultVisibility(mType);
+ if (source.isVisible() != serverVisibility) {
+ source.setVisible(serverVisibility);
+ mController.notifyVisibilityChanged();
+ }
+
+ // For updateCompatSysUiVisibility
applyLocalVisibilityOverride();
} else {
// We are gaining control, and need to run an animation since previous state
From 13070106c6fb95adf1c5418586fefcc2f734ad48 Mon Sep 17 00:00:00 2001
From: Bill Yi
Date: Fri, 8 Jan 2021 06:49:10 -0800
Subject: [PATCH 033/288] Import translations. DO NOT MERGE ANYWHERE
Auto-generated-cl: translation import
Change-Id: I216f1736a0b63c8cf0c8b4a73e9ba57d5142fd32
---
packages/SettingsLib/res/values-ar/strings.xml | 2 +-
packages/SettingsLib/res/values-b+sr+Latn/strings.xml | 2 +-
packages/SettingsLib/res/values-bg/strings.xml | 2 +-
packages/SettingsLib/res/values-ca/strings.xml | 2 +-
packages/SettingsLib/res/values-el/strings.xml | 2 +-
packages/SettingsLib/res/values-eu/strings.xml | 2 +-
packages/SettingsLib/res/values-gl/strings.xml | 2 +-
packages/SettingsLib/res/values-hy/strings.xml | 2 +-
packages/SettingsLib/res/values-kk/strings.xml | 2 +-
packages/SettingsLib/res/values-kn/strings.xml | 2 +-
packages/SettingsLib/res/values-ml/strings.xml | 2 +-
packages/SettingsLib/res/values-mn/strings.xml | 2 +-
packages/SettingsLib/res/values-nb/strings.xml | 2 +-
packages/SettingsLib/res/values-pa/strings.xml | 2 +-
packages/SettingsLib/res/values-pl/strings.xml | 2 +-
packages/SettingsLib/res/values-si/strings.xml | 2 +-
packages/SettingsLib/res/values-sl/strings.xml | 2 +-
packages/SettingsLib/res/values-sr/strings.xml | 2 +-
packages/SettingsLib/res/values-te/strings.xml | 2 +-
packages/SettingsLib/res/values-th/strings.xml | 2 +-
packages/SettingsLib/res/values-uk/strings.xml | 2 +-
packages/SettingsLib/res/values-uz/strings.xml | 2 +-
packages/SettingsLib/res/values-zh-rHK/strings.xml | 2 +-
packages/SettingsLib/res/values-zh-rTW/strings.xml | 2 +-
24 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index d1c60113656e3..ce0654a39e844 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"%1$s إلى أن يتم شحن الجهاز بالكامل"
"%1$s - %2$s إلى أن يتم شحن الجهاز بالكامل"
- "%1$s - التحسين لسلامة البطارية"
+ "%1$s - التحسين للحفاظ على سلامة البطارية"
"غير معروف"
"جارٍ الشحن"
"جارٍ الشحن سريعًا"
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index f3106d5e58c6d..a410b63481a22 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -446,7 +446,7 @@
"%1$s – %2$s"
"Napuniće se za %1$s"
"%1$s – napuniće se za %2$s"
- "%1$s – Optimizuje se radi stanja baterije"
+ "%1$s – Optimizuje se radi boljeg stanja baterije"
"Nepoznato"
"Puni se"
"Brzo se puni"
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 4b7e9a88da46e..70ec30e70deaa 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -446,7 +446,7 @@
"%1$s – %2$s"
"Оставащо време до пълно зареждане: %1$s"
"%1$s – %2$s до пълно зареждане"
- "%1$s – Оптимизиране за състоян. на батерията"
+ "%1$s – Оптимизиране с цел състоянието на батерията"
"Неизвестно"
"Зарежда се"
"Зарежда се бързо"
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index f8fc174883f64..3ad8b9da9a980 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -446,7 +446,7 @@
"%1$s: %2$s"
"%1$s per completar la càrrega"
"%1$s: %2$s per completar la càrrega"
- "%1$s: optimitzant per a l\'estat de la bateria"
+ "%1$s: s\'està optimitzant per preservar l\'estat de la bateria"
"Desconegut"
"S\'està carregant"
"Carregant ràpidament"
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 10b8c77b56194..c53f38a0c78c0 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"Απομένουν %1$s για ολοκλήρωση της φόρτισης"
"%1$s - %2$s για την ολοκλήρωση της φόρτισης"
- "%1$s - Βελτιστοποίηση κατάστασης μπαταρίας"
+ "%1$s - Βελτιστοποίηση για τη διατήρηση της καλής κατάστασης της μπαταρίας"
"Άγνωστο"
"Φόρτιση"
"Ταχεία φόρτιση"
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index af869d6cec965..3584975737f62 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"%1$s guztiz kargatu arte"
"%1$s - %2$s guztiz kargatu arte"
- "%1$s - Optimizatzen bateria egoera onean mantentzeko"
+ "%1$s - Optimizatzen, bateria egoera onean mantentzeko"
"Ezezaguna"
"Kargatzen"
"Bizkor kargatzen"
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index d9450ba1d54c2..8f143e4d583ae 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"%1$s para completar a carga"
"%1$s: %2$s para completar a carga"
- "%1$s: optimizando para manter a batería en bo estado"
+ "%1$s: optimizando a preservación da batería"
"Descoñecido"
"Cargando"
"Cargando rapidamente"
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 14ee4c9a16dc5..c9c4f25d9e5dc 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"%1$s մինչև լիցքավորումը"
"%1$s – %2$s մինչև լիցքավորումը"
- "%1$s – Օպտիմալացվում է"
+ "%1$s – Օպտիմալացվում է մարտկոցի պահպանման համար"
"Անհայտ"
"Լիցքավորում"
"Արագ լիցքավորում"
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index bc12fee6a9eb9..32d8091dd01c8 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"Зарядталғанға дейін %1$s қалды"
"%1$s – зарядталғанға дейін %2$s"
- "%1$s - Батареяның жұмыс істеу қабілеті оңтайландырылуда"
+ "%1$s - Батарея жұмысын оңтайландыру"
"Белгісіз"
"Зарядталуда"
"Жылдам зарядталуда"
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index df25363d0cbf7..682f4d5f9ca4f 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"ಚಾರ್ಜ್ ಆಗಲು %1$s ಸಮಯ ಬಾಕಿ ಇದೆ"
"%1$s - ಚಾರ್ಜ್ ಆಗಲು %2$s ಸಮಯ ಬೇಕು"
- "%1$s - ಬ್ಯಾಟರಿಯ ಆರೋಗ್ಯಕ್ಕಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ"
+ "%1$s - ಬ್ಯಾಟರಿಯ ಸುಸ್ಥಿತಿಗಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ"
"ಅಪರಿಚಿತ"
"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"
"ವೇಗದ ಚಾರ್ಜಿಂಗ್"
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index e9f707003fc23..a5c32816a41ed 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"പൂർണ്ണമായി ചാർജാവാൻ %1$s ശേഷിക്കുന്നു"
"%1$s - പൂർണ്ണമായി ചാർജാവാൻ %2$s"
- "%1$s - ബാറ്ററി നില ഒപ്റ്റിമൈസ് ചെയ്യുന്നു"
+ "%1$s - ബാറ്ററിയുടെ ആയുസിനായി ഒപ്റ്റിമൈസ് ചെയ്യുന്നു"
"അജ്ഞാതം"
"ചാർജ് ചെയ്യുന്നു"
"അതിവേഗ ചാർജിംഗ്"
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 6b628bdf02bf7..ec79127b9029b 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"Цэнэглэх хүртэл үлдсэн %1$s"
"%1$s - цэнэглэх хүртэл %2$s"
- "%1$s - Батарейн чанарыг оновчилж байна"
+ "%1$s - Батарейн барилтыг оновчилж байна"
"Тодорхойгүй"
"Цэнэглэж байна"
"Хурдан цэнэглэж байна"
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index b8a010a6ed7ba..d4385758d3469 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -446,7 +446,7 @@
"%1$s – %2$s"
"%1$s til batteriet er fulladet"
"%1$s – %2$s til batteriet er fulladet"
- "%1$s – Optimaliserer batteritilstanden"
+ "%1$s – optimaliserer batteritilstanden"
"Ukjent"
"Lader"
"Lader raskt"
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 177e29377ee3b..b1735a3294e38 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"ਚਾਰਜ ਹੋਣ ਵਿੱਚ %1$s ਬਾਕੀ"
"%1$s - %2$s ਤੱਕ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"
- "%1$s - ਬੈਟਰੀ ਸਥਿਤੀ ਲਈ ਅਨੁਕੂਲ ਬਣਾਇਆ ਜਾ ਰਿਹਾ"
+ "%1$s - ਬੈਟਰੀ ਦੀ ਸਥਿਤੀ ਲਈ ਅਨੁਕੂਲ ਬਣਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"
"ਅਗਿਆਤ"
"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"
"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 00cc98728729d..38bc6e1c3547e 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -446,7 +446,7 @@
"%1$s – %2$s"
"Do naładowania %1$s"
"%1$s – do naładowania %2$s"
- "%1$s – Optymalizuję, by utrzymać baterię w dobrym stanie"
+ "%1$s – Optymalizuję, aby utrzymać baterię w dobrym stanie"
"Nieznane"
"Ładowanie"
"Szybkie ładowanie"
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 9cc7a20819ba5..24b7710ce1b16 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"ආරෝපණය වන තෙක් %1$s ඇත"
"%1$s - ආරෝපණය වන තෙක් %2$s"
- "%1$s - බැටරි සෞඛ්යය සඳහා ප්රශස්ත කරමින්"
+ "%1$s - බැටරි ආයු කාලය වැඩි දියුණු කරමින්"
"නොදනී"
"ආරෝපණය වෙමින්"
"ශීඝ්ර ආරෝපණය"
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index be2e1a8778e8b..68e1e5deaf195 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -446,7 +446,7 @@
"%1$s – %2$s"
"Še %1$s do polne napolnjenosti"
"%1$s – %2$s do polne napolnjenosti"
- "%1$s – Optimizacija za ohran. zmog. baterije"
+ "%1$s – Optimizacija za ohranjanje zmogljivosti baterije"
"Neznano"
"Polnjenje"
"Hitro polnjenje"
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 2b59a92c6c3a5..947eb43516558 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -446,7 +446,7 @@
"%1$s – %2$s"
"Напуниће се за %1$s"
"%1$s – напуниће се за %2$s"
- "%1$s – Оптимизује се ради стања батерије"
+ "%1$s – Оптимизује се ради бољег стања батерије"
"Непознато"
"Пуни се"
"Брзо се пуни"
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 05dbd43f2daa2..6f2da08810ea0 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"ఛార్జ్ అవ్వడానికి %1$s సమయం మిగిలి ఉంది"
"%1$s - ఛార్జ్ అవ్వడానికి %2$s పడుతుంది"
- "%1$s - బ్యాటరీ స్థితిని ఆప్టిమైజ్ చేయడం కోసం"
+ "%1$s - బ్యాటరీ జీవితకాలాన్ని పెంచడం కోసం ఆప్టిమైజ్ చేస్తోంది"
"తెలియదు"
"ఛార్జ్ అవుతోంది"
"వేగవంతమైన ఛార్జింగ్"
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index d0b4a9a90c82b..d34455c1a6d72 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -284,7 +284,7 @@
"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"
"ลดการเปลืองแบตเตอรี่และเพิ่มประสิทธิภาพเครือข่าย"
"เมื่อเปิดใช้โหมดนี้ ที่อยู่ MAC ของอุปกรณ์นี้อาจเปลี่ยนทุกครั้งที่เชื่อมต่อกับเครือข่ายที่มีการเปิดใช้การสุ่ม MAC"
- "แบบจำกัดปริมาณอินเทอร์เน็ต"
+ "แบบจำกัดปริมาณ"
"ไม่มีการวัดปริมาณอินเทอร์เน็ต"
"ขนาดบัฟเฟอร์ของตัวบันทึก"
"เลือกขนาด Logger ต่อบัฟเฟอร์ไฟล์บันทึก"
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index ad83aa5496060..b1d5d43b1b0ce 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -446,7 +446,7 @@
"%1$s – %2$s"
"%1$s до повного заряду"
"%1$s – %2$s до повного заряду"
- "%1$s – стан акумулятора оптимізується"
+ "%1$s – Оптимізація для збереження заряду акумулятора"
"Невідомо"
"Заряджається"
"Швидке заряджання"
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 4ddb19fcfb17d..7f63b12fea1d9 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -446,7 +446,7 @@
"%1$s – %2$s"
"%1$s ichida toʻladi"
"%1$s – %2$s ichida toʻladi"
- "%1$s – Batareya quvvati muvozanatlanmoqda"
+ "%1$s – Batareya uchun optimizatsiya"
"Noma’lum"
"Quvvat olmoqda"
"Tezkor quvvat olmoqda"
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 556df308c1438..937439e257069 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"還需 %1$s才能充滿電"
"%1$s - 還需 %2$s才能充滿電"
- "%1$s - 正在優化電池狀態"
+ "%1$s - 優化電池效能"
"未知"
"充電中"
"正在快速充電"
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index efe13bd348926..704d265e4e236 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -446,7 +446,7 @@
"%1$s - %2$s"
"%1$s後充飽電"
"%1$s - %2$s後充飽電"
- "%1$s - 針對電池狀態進行最佳化調整"
+ "%1$s - 最佳化調整電池狀態"
"不明"
"充電中"
"快速充電中"
From d8409498161db3fb6c1b0a517df71f08de6cb29a Mon Sep 17 00:00:00 2001
From: Todd Kennedy
Date: Thu, 7 Jan 2021 16:31:39 -0800
Subject: [PATCH 034/288] Ensure OK/Cancel buttons always shown
There is some weirdness with the way the AlertActivity behaves when
it's populated and elements are enabled while it's not actually on
the screen. Work around this by populating the AlertActivity during
onResume() [eg. every time the activity is shown] instead of
onCreate() [eg. only the first time the activity is created].
Fixes: 172699073, 173159446
Test: Manual
Test: Start a staging install
Test: Turn off the phone screen
Test: Wait for the staging to complete
Test: Turn on the phone screen.
Test: See the install dialog has OK/Cancel buttons
Change-Id: Ia9525f20f98f42e6b2b865fa926b9707d416cde4
---
.../packageinstaller/PackageInstallerActivity.java | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 5675c9986ac9c..665d262d8067f 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -343,17 +343,19 @@ public class PackageInstallerActivity extends AlertActivity {
if (!wasSetUp) {
return;
}
-
- // load dummy layout with OK button disabled until we override this layout in
- // startInstallConfirm
- bindUi();
- checkIfAllowedAndInitiateInstall();
}
@Override
protected void onResume() {
super.onResume();
+ if (mAppSnippet != null) {
+ // load dummy layout with OK button disabled until we override this layout in
+ // startInstallConfirm
+ bindUi();
+ checkIfAllowedAndInitiateInstall();
+ }
+
if (mOk != null) {
mOk.setEnabled(mEnableOk);
}
From 23ed52f9716134e5ea7fe9c90bedab29cf90876c Mon Sep 17 00:00:00 2001
From: Ivan Chiang
Date: Tue, 10 Nov 2020 10:44:05 +0800
Subject: [PATCH 035/288] Fix renaming file and moving file issue in MTP
After the user renames/moves a file/directory, the database of
MediaProvider is not updated. It causes that the file can't be
opened via MediaProvider. Use scanFile to instead of MP.update
for the file/directory.
Handle the .nomedia case for create/copy/move/delete/rename.
Scan the parent directory to update the database.
Test: atest MtpTests
Test: atest --test-mapping packages/providers/MediaProvider
Test: manual. 1. Move/rename the file/directory.
2. Open the file in DocumentsUI
3. create/copy/move/delete/rename for .nomedia case
Bug: 169300366
Bug: 171538933
Bug: 172029805
Bug: 170798581
Change-Id: Ia37be7a027bfe502e3f7c96babe5a5bbed5f942c
Merged-In: Ia37be7a027bfe502e3f7c96babe5a5bbed5f942c
(cherry picked from commit 20079f10644e641bbb2c35c250e8a9e0b9f6c1b3)
---
media/java/android/mtp/MtpDatabase.java | 120 +++++-------------------
1 file changed, 24 insertions(+), 96 deletions(-)
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index ed56b4398c226..798bf6e2f8ee7 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -19,8 +19,7 @@ package android.mtp;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.ContentProviderClient;
-import android.content.ContentUris;
-import android.content.ContentValues;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -32,7 +31,6 @@ import android.media.ExifInterface;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.BatteryManager;
-import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.storage.StorageVolume;
import android.provider.MediaStore;
@@ -103,8 +101,6 @@ public class MtpDatabase implements AutoCloseable {
private MtpStorageManager mManager;
private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
- private static final String[] ID_PROJECTION = new String[] {Files.FileColumns._ID};
- private static final String[] PATH_PROJECTION = new String[] {Files.FileColumns.DATA};
private static final String NO_MEDIA = ".nomedia";
static {
@@ -431,7 +427,7 @@ public class MtpDatabase implements AutoCloseable {
}
// Add the new file to MediaProvider
if (succeeded) {
- MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile());
+ updateMediaStore(mContext, obj.getPath().toFile());
}
}
@@ -580,32 +576,8 @@ public class MtpDatabase implements AutoCloseable {
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
- // finally update MediaProvider
- ContentValues values = new ContentValues();
- values.put(Files.FileColumns.DATA, newPath.toString());
- String[] whereArgs = new String[]{oldPath.toString()};
- try {
- // note - we are relying on a special case in MediaProvider.update() to update
- // the paths for all children in the case where this is a directory.
- final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
- mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in mMediaProvider.update", e);
- }
-
- // check if nomedia status changed
- if (obj.isDir()) {
- // for directories, check if renamed from something hidden to something non-hidden
- if (oldPath.getFileName().startsWith(".") && !newPath.startsWith(".")) {
- MediaStore.scanFile(mContext.getContentResolver(), newPath.toFile());
- }
- } else {
- // for files, check if renamed from .nomedia to something else
- if (oldPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)
- && !newPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)) {
- MediaStore.scanFile(mContext.getContentResolver(), newPath.getParent().toFile());
- }
- }
+ updateMediaStore(mContext, oldPath.toFile());
+ updateMediaStore(mContext, newPath.toFile());
return MtpConstants.RESPONSE_OK;
}
@@ -635,48 +607,15 @@ public class MtpDatabase implements AutoCloseable {
Log.e(TAG, "Failed to end move object");
return;
}
-
obj = mManager.getObject(objId);
if (!success || obj == null)
return;
- // Get parent info from MediaProvider, since the id is different from MTP's
- ContentValues values = new ContentValues();
+
Path path = newParentObj.getPath().resolve(name);
Path oldPath = oldParentObj.getPath().resolve(name);
- values.put(Files.FileColumns.DATA, path.toString());
- if (obj.getParent().isRoot()) {
- values.put(Files.FileColumns.PARENT, 0);
- } else {
- int parentId = findInMedia(newParentObj, path.getParent());
- if (parentId != -1) {
- values.put(Files.FileColumns.PARENT, parentId);
- } else {
- // The new parent isn't in MediaProvider, so delete the object instead
- deleteFromMedia(obj, oldPath, obj.isDir());
- return;
- }
- }
- // update MediaProvider
- Cursor c = null;
- String[] whereArgs = new String[]{oldPath.toString()};
- try {
- int parentId = -1;
- if (!oldParentObj.isRoot()) {
- parentId = findInMedia(oldParentObj, oldPath.getParent());
- }
- if (oldParentObj.isRoot() || parentId != -1) {
- // Old parent exists in MediaProvider - perform a move
- // note - we are relying on a special case in MediaProvider.update() to update
- // the paths for all children in the case where this is a directory.
- final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
- mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
- } else {
- // Old parent doesn't exist - add the object
- MediaStore.scanFile(mContext.getContentResolver(), path.toFile());
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in mMediaProvider.update", e);
- }
+
+ updateMediaStore(mContext, oldPath.toFile());
+ updateMediaStore(mContext, path.toFile());
}
@VisibleForNative
@@ -699,7 +638,19 @@ public class MtpDatabase implements AutoCloseable {
if (!success) {
return;
}
- MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile());
+
+ updateMediaStore(mContext, obj.getPath().toFile());
+ }
+
+ private static void updateMediaStore(@NonNull Context context, @NonNull File file) {
+ final ContentResolver resolver = context.getContentResolver();
+ // For file, check whether the file name is .nomedia or not.
+ // If yes, scan the parent directory to update all files in the directory.
+ if (!file.isDirectory() && file.getName().toLowerCase(Locale.ROOT).endsWith(NO_MEDIA)) {
+ MediaStore.scanFile(resolver, file.getParentFile());
+ } else {
+ MediaStore.scanFile(resolver, file);
+ }
}
@VisibleForNative
@@ -928,26 +879,6 @@ public class MtpDatabase implements AutoCloseable {
deleteFromMedia(obj, obj.getPath(), obj.isDir());
}
- private int findInMedia(MtpStorageManager.MtpObject obj, Path path) {
- final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
-
- int ret = -1;
- Cursor c = null;
- try {
- c = mMediaProvider.query(objectsUri, ID_PROJECTION, PATH_WHERE,
- new String[]{path.toString()}, null, null);
- if (c != null && c.moveToNext()) {
- ret = c.getInt(0);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error finding " + path + " in MediaProvider");
- } finally {
- if (c != null)
- c.close();
- }
- return ret;
- }
-
private void deleteFromMedia(MtpStorageManager.MtpObject obj, Path path, boolean isDir) {
final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
try {
@@ -963,13 +894,10 @@ public class MtpDatabase implements AutoCloseable {
}
String[] whereArgs = new String[]{path.toString()};
- if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) > 0) {
- if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) {
- MediaStore.scanFile(mContext.getContentResolver(), path.getParent().toFile());
- }
- } else {
- Log.i(TAG, "Mediaprovider didn't delete " + path);
+ if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) == 0) {
+ Log.i(TAG, "MediaProvider didn't delete " + path);
}
+ updateMediaStore(mContext, path.toFile());
} catch (Exception e) {
Log.d(TAG, "Failed to delete " + path + " from MediaProvider");
}
From 557838f5506760076e5c523081590b2992b619ff Mon Sep 17 00:00:00 2001
From: Lais Andrade
Date: Thu, 3 Dec 2020 21:07:27 +0000
Subject: [PATCH 036/288] Check mode/boost index before accessing cached
support value
Bug: 174243830
Test: manual
Change-Id: I2f81bcd57181791fa95d35ad52e6af2d7d9c8dcf
Merged-In: If305a4b694a9d6fcc27da8279f1f53f9fd9cb685
Merged-In: I345d9ff828021da35556f2d51da512840dda8026
---
...ndroid_server_power_PowerManagerService.cpp | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 469838357f306..85d5c1f256930 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -190,19 +190,18 @@ static void setPowerBoostWithHandle(sp handle, Boost boost, int32_t
static std::array,
static_cast(Boost::DISPLAY_UPDATE_IMMINENT) + 1>
boostSupportedArray = {HalSupport::UNKNOWN};
+ size_t idx = static_cast(boost);
// Quick return if boost is not supported by HAL
- if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
- boostSupportedArray[static_cast(boost)] == HalSupport::OFF) {
+ if (idx >= boostSupportedArray.size() || boostSupportedArray[idx] == HalSupport::OFF) {
ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", toString(boost).c_str());
return;
}
- if (boostSupportedArray[static_cast(boost)] == HalSupport::UNKNOWN) {
+ if (boostSupportedArray[idx] == HalSupport::UNKNOWN) {
bool isSupported = false;
handle->isBoostSupported(boost, &isSupported);
- boostSupportedArray[static_cast(boost)] =
- isSupported ? HalSupport::ON : HalSupport::OFF;
+ boostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
if (!isSupported) {
ALOGV("Skipped setPowerBoost %s because HAL doesn't support it",
toString(boost).c_str());
@@ -230,19 +229,18 @@ static bool setPowerModeWithHandle(sp handle, Mode mode, bool enable
// Need to increase the array if more mode supported.
static std::array, static_cast(Mode::DISPLAY_INACTIVE) + 1>
modeSupportedArray = {HalSupport::UNKNOWN};
+ size_t idx = static_cast(mode);
// Quick return if mode is not supported by HAL
- if (mode > Mode::DISPLAY_INACTIVE ||
- modeSupportedArray[static_cast(mode)] == HalSupport::OFF) {
+ if (idx >= modeSupportedArray.size() || modeSupportedArray[idx] == HalSupport::OFF) {
ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
return false;
}
- if (modeSupportedArray[static_cast(mode)] == HalSupport::UNKNOWN) {
+ if (modeSupportedArray[idx] == HalSupport::UNKNOWN) {
bool isSupported = false;
handle->isModeSupported(mode, &isSupported);
- modeSupportedArray[static_cast(mode)] =
- isSupported ? HalSupport::ON : HalSupport::OFF;
+ modeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
if (!isSupported) {
ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
return false;
From 20b47da57ac2067429519c83f3053269195a38c8 Mon Sep 17 00:00:00 2001
From: Lais Andrade
Date: Thu, 3 Dec 2020 21:07:27 +0000
Subject: [PATCH 037/288] Check mode/boost index before accessing cached
support value
Bug: 174243830
Test: manual
Change-Id: I2f81bcd57181791fa95d35ad52e6af2d7d9c8dcf
Merged-In: If305a4b694a9d6fcc27da8279f1f53f9fd9cb685
Merged-In: I345d9ff828021da35556f2d51da512840dda8026
---
...ndroid_server_power_PowerManagerService.cpp | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 469838357f306..85d5c1f256930 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -190,19 +190,18 @@ static void setPowerBoostWithHandle(sp handle, Boost boost, int32_t
static std::array,
static_cast(Boost::DISPLAY_UPDATE_IMMINENT) + 1>
boostSupportedArray = {HalSupport::UNKNOWN};
+ size_t idx = static_cast(boost);
// Quick return if boost is not supported by HAL
- if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
- boostSupportedArray[static_cast(boost)] == HalSupport::OFF) {
+ if (idx >= boostSupportedArray.size() || boostSupportedArray[idx] == HalSupport::OFF) {
ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", toString(boost).c_str());
return;
}
- if (boostSupportedArray[static_cast(boost)] == HalSupport::UNKNOWN) {
+ if (boostSupportedArray[idx] == HalSupport::UNKNOWN) {
bool isSupported = false;
handle->isBoostSupported(boost, &isSupported);
- boostSupportedArray[static_cast(boost)] =
- isSupported ? HalSupport::ON : HalSupport::OFF;
+ boostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
if (!isSupported) {
ALOGV("Skipped setPowerBoost %s because HAL doesn't support it",
toString(boost).c_str());
@@ -230,19 +229,18 @@ static bool setPowerModeWithHandle(sp handle, Mode mode, bool enable
// Need to increase the array if more mode supported.
static std::array, static_cast(Mode::DISPLAY_INACTIVE) + 1>
modeSupportedArray = {HalSupport::UNKNOWN};
+ size_t idx = static_cast(mode);
// Quick return if mode is not supported by HAL
- if (mode > Mode::DISPLAY_INACTIVE ||
- modeSupportedArray[static_cast(mode)] == HalSupport::OFF) {
+ if (idx >= modeSupportedArray.size() || modeSupportedArray[idx] == HalSupport::OFF) {
ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
return false;
}
- if (modeSupportedArray[static_cast(mode)] == HalSupport::UNKNOWN) {
+ if (modeSupportedArray[idx] == HalSupport::UNKNOWN) {
bool isSupported = false;
handle->isModeSupported(mode, &isSupported);
- modeSupportedArray[static_cast(mode)] =
- isSupported ? HalSupport::ON : HalSupport::OFF;
+ modeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF;
if (!isSupported) {
ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
return false;
From bf88e9faf1e9d892dee24896c1bb5a944b355e0a Mon Sep 17 00:00:00 2001
From: Dave Mankoff
Date: Mon, 17 Aug 2020 15:33:21 -0400
Subject: [PATCH 038/288] Pass interaction type to FalsingManager.isFalse
This ensures that the falsing manager always is testing against the
correct interaction type when it is asked. Prior to this change, we
could end up in states where a user was swiping down (opening the
quick settings) but the falsing manager believed the user was trying
to unlock the phone.
Fixes: 160967364
Test: atest SystemUITests && manual
Change-Id: I176f54a768622dec3e758a7f01ed8aec26223648
Merged-In: I176f54a768622dec3e758a7f01ed8aec26223648
---
.../systemui/plugins/FalsingManager.java | 5 ++--
.../NotificationSwipeActionHelper.java | 11 +++++----
.../src/com/android/systemui/SwipeHelper.java | 9 +++++---
.../classifier/FalsingManagerFake.java | 2 +-
.../classifier/FalsingManagerImpl.java | 2 +-
.../classifier/FalsingManagerProxy.java | 4 ++--
.../brightline/BrightLineFalsingManager.java | 3 ++-
.../brightline/FalsingDataProvider.java | 5 +++-
.../media/MediaCarouselScrollHandler.kt | 4 +++-
.../systemui/statusbar/DragDownHelper.java | 4 +++-
.../statusbar/PulseExpansionHandler.kt | 3 ++-
.../stack/NotificationSwipeHelper.java | 2 +-
.../phone/KeyguardAffordanceHelper.java | 5 +++-
.../NotificationPanelViewController.java | 8 ++++---
.../statusbar/phone/PanelViewController.java | 23 +++++++++++++++----
15 files changed, 62 insertions(+), 28 deletions(-)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 9d52098f37d5c..63f8b1f5dbb8e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -30,7 +30,7 @@ import java.io.PrintWriter;
*/
@ProvidesInterface(version = FalsingManager.VERSION)
public interface FalsingManager {
- int VERSION = 4;
+ int VERSION = 5;
void onSuccessfulUnlock();
@@ -42,7 +42,8 @@ public interface FalsingManager {
boolean isUnlockingDisabled();
- boolean isFalseTouch();
+ /** Returns true if the gesture should be rejected. */
+ boolean isFalseTouch(int interactionType);
void onNotificatonStopDraggingDown();
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
index 02c4c5eff26e5..4b6efa91a7c8e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
@@ -14,16 +14,16 @@
package com.android.systemui.plugins.statusbar;
-import com.android.systemui.plugins.annotations.DependsOn;
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
-
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+
@ProvidesInterface(version = NotificationSwipeActionHelper.VERSION)
@DependsOn(target = SnoozeOption.class)
public interface NotificationSwipeActionHelper {
@@ -52,7 +52,8 @@ public interface NotificationSwipeActionHelper {
public boolean isDismissGesture(MotionEvent ev);
- public boolean isFalseGesture(MotionEvent ev);
+ /** Returns true if the gesture should be rejected. */
+ boolean isFalseGesture();
public boolean swipedFarEnough(float translation, float viewSize);
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index d17ca4041b31e..0bb8c9cd81f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -16,6 +16,8 @@
package com.android.systemui;
+import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -697,14 +699,15 @@ public class SwipeHelper implements Gefingerpoken {
float translation = getTranslation(mCurrView);
return ev.getActionMasked() == MotionEvent.ACTION_UP
&& !mFalsingManager.isUnlockingDisabled()
- && !isFalseGesture(ev) && (swipedFastEnough() || swipedFarEnough())
+ && !isFalseGesture() && (swipedFastEnough() || swipedFarEnough())
&& mCallback.canChildBeDismissedInDirection(mCurrView, translation > 0);
}
- public boolean isFalseGesture(MotionEvent ev) {
+ /** Returns true if the gesture should be rejected. */
+ public boolean isFalseGesture() {
boolean falsingDetected = mCallback.isAntiFalsingNeeded();
if (mFalsingManager.isClassifierEnabled()) {
- falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
+ falsingDetected = falsingDetected && mFalsingManager.isFalseTouch(NOTIFICATION_DISMISS);
} else {
falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index 646e62062dfb0..6961b45c3c375 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -70,7 +70,7 @@ public class FalsingManagerFake implements FalsingManager {
}
@Override
- public boolean isFalseTouch() {
+ public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
return mIsFalseTouch;
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index cc64fb53f15fc..decaec10e572f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -262,7 +262,7 @@ public class FalsingManagerImpl implements FalsingManager {
/**
* @return true if the classifier determined that this is not a human interacting with the phone
*/
- public boolean isFalseTouch() {
+ public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
if (FalsingLog.ENABLED) {
// We're getting some false wtfs from touches that happen after the device went
// to sleep. Only report missing sessions that happen when the device is interactive.
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index f35322bd2a777..be6c5f9a905f0 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -187,8 +187,8 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
}
@Override
- public boolean isFalseTouch() {
- return mInternalFalsingManager.isFalseTouch();
+ public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
+ return mInternalFalsingManager.isFalseTouch(interactionType);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index a50f9ce9713b0..9d847ca62465d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -189,7 +189,8 @@ public class BrightLineFalsingManager implements FalsingManager {
}
@Override
- public boolean isFalseTouch() {
+ public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
+ mDataProvider.setInteractionType(interactionType);
if (!mDataProvider.isDirty()) {
return mPreviousResult;
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
index ea46441c8fbe2..8d067489a8cce 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
@@ -116,7 +116,10 @@ public class FalsingDataProvider {
* interactionType is defined by {@link com.android.systemui.classifier.Classifier}.
*/
final void setInteractionType(@Classifier.InteractionType int interactionType) {
- this.mInteractionType = interactionType;
+ if (mInteractionType != interactionType) {
+ mInteractionType = interactionType;
+ mDirty = true;
+ }
}
public boolean isDirty() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index 77cac5023db37..486399979db7c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -30,6 +30,7 @@ import com.android.settingslib.Utils
import com.android.systemui.Gefingerpoken
import com.android.systemui.qs.PageIndicator
import com.android.systemui.R
+import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.util.animation.PhysicsAnimator
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -315,7 +316,8 @@ class MediaCarouselScrollHandler(
return false
}
- private fun isFalseTouch() = falsingProtectionNeeded && falsingManager.isFalseTouch
+ private fun isFalseTouch() = falsingProtectionNeeded &&
+ falsingManager.isFalseTouch(NOTIFICATION_DISMISS)
private fun getMaxTranslation() = if (showsSettingsButton) {
settingsButton.width
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 4fa782269c2d7..e61e05a7dc2f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -163,7 +165,7 @@ public class DragDownHelper implements Gefingerpoken {
if (!mDragDownCallback.isFalsingCheckNeeded()) {
return false;
}
- return mFalsingManager.isFalseTouch() || !mDraggedFarEnough;
+ return mFalsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) || !mDraggedFarEnough;
}
private void captureStartingChild(float x, float y) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 7b2585324b9f5..38f96f42bdd4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -30,6 +30,7 @@ import android.view.ViewConfiguration
import com.android.systemui.Gefingerpoken
import com.android.systemui.Interpolators
import com.android.systemui.R
+import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
@@ -106,7 +107,7 @@ constructor(
private var velocityTracker: VelocityTracker? = null
private val isFalseTouch: Boolean
- get() = falsingManager.isFalseTouch
+ get() = falsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN)
var qsExpanded: Boolean = false
var pulseExpandAbortListener: Runnable? = null
var bouncerShowing: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index d7d09e05c2386..ac528b245ce93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -209,7 +209,7 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
|| (isFastNonDismissGesture && isAbleToShowMenu);
int menuSnapTarget = menuRow.getMenuSnapTarget();
boolean isNonFalseMenuRevealingGesture =
- !isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu;
+ !isFalseGesture() && isMenuRevealingGestureAwayFromMenu;
if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture)
&& menuSnapTarget != 0) {
// Menu has not been snapped to previously and this is menu revealing gesture
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 858023dc6c625..ba94202658495 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -27,6 +27,7 @@ import android.view.ViewConfiguration;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.KeyguardAffordanceView;
@@ -317,7 +318,9 @@ public class KeyguardAffordanceHelper {
// We snap back if the current translation is not far enough
boolean snapBack = false;
if (mCallback.needsAntiFalsing()) {
- snapBack = snapBack || mFalsingManager.isFalseTouch();
+ snapBack = snapBack || mFalsingManager.isFalseTouch(
+ mTargetedView == mRightIcon
+ ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE);
}
snapBack = snapBack || isBelowFalsingThreshold();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 799e16cc6d8d4..53cbf02e57f05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone;
import static android.view.View.GONE;
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
@@ -69,6 +70,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.DejankUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.fragments.FragmentHostManager;
@@ -1204,7 +1206,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private boolean flingExpandsQs(float vel) {
- if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
+ if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) {
return false;
}
if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -1214,12 +1216,12 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
- private boolean isFalseTouch() {
+ private boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) {
return false;
}
if (mFalsingManager.isClassifierEnabled()) {
- return mFalsingManager.isFalseTouch();
+ return mFalsingManager.isFalseTouch(interactionType);
}
return !mQsTouchAboveFalsingThreshold;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index e942d85790c16..a4f9b06b86a51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -16,6 +16,10 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
+import static com.android.systemui.classifier.Classifier.UNLOCK;
+
import static java.lang.Float.isNaN;
import android.animation.Animator;
@@ -41,6 +45,7 @@ import com.android.internal.util.LatencyTracker;
import com.android.systemui.DejankUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -397,7 +402,12 @@ public abstract class PanelViewController {
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
}
- fling(vel, expand, isFalseTouch(x, y));
+ @Classifier.InteractionType int interactionType = vel > 0
+ ? QUICK_SETTINGS : (
+ mKeyguardStateController.canDismissLockScreen()
+ ? UNLOCK : BOUNCER_UNLOCK);
+
+ fling(vel, expand, isFalseTouch(x, y, interactionType));
onTrackingStopped(expand);
mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
if (mUpdateFlingOnLayout) {
@@ -492,7 +502,11 @@ public abstract class PanelViewController {
return true;
}
- if (isFalseTouch(x, y)) {
+ @Classifier.InteractionType int interactionType = vel > 0
+ ? QUICK_SETTINGS : (
+ mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK);
+
+ if (isFalseTouch(x, y, interactionType)) {
return true;
}
if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -511,12 +525,13 @@ public abstract class PanelViewController {
* @param y the final y-coordinate when the finger was lifted
* @return whether this motion should be regarded as a false touch
*/
- private boolean isFalseTouch(float x, float y) {
+ private boolean isFalseTouch(float x, float y,
+ @Classifier.InteractionType int interactionType) {
if (!mStatusBar.isFalsingThresholdNeeded()) {
return false;
}
if (mFalsingManager.isClassifierEnabled()) {
- return mFalsingManager.isFalseTouch();
+ return mFalsingManager.isFalseTouch(interactionType);
}
if (!mTouchAboveFalsingThreshold) {
return true;
From a13fc7f314ac56e75eca09c94db8090f928023ff Mon Sep 17 00:00:00 2001
From: Michael Groover
Date: Tue, 21 Jan 2020 20:00:41 -0800
Subject: [PATCH 039/288] Update the getIccId docs to reflect access
requirements
In Android 10 access to device identifiers was limited to apps with
the READ_PRIVILEGED_PHONE_STATE permission, carrier privileges, the
READ_DEVICE_IDENTIFIERS appop set to allow, or those that pass a
device / profile owner check. TelephonyManager#getSimSerialNumber
was guarded behind these new access requirements, but the same value
is still accessible via SubscriptionInfo#getIccId. This change
updates the documentation to reflect that the getIccId will no
longer be accessible if the caller does not meet the new access
requirements.
Bug: 131909991
Bug: 173421434
Test: Builds
Change-Id: I03072c65e1c7101538a7a20541d2ac59a2d213e3
Merged-In: I03072c65e1c7101538a7a20541d2ac59a2d213e3
---
.../android/telephony/SubscriptionInfo.java | 31 ++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 1e6cd474d13bf..d34b8c932f77d 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -38,6 +38,8 @@ import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
+import com.android.telephony.Rlog;
+
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -200,6 +202,19 @@ public class SubscriptionInfo implements Parcelable {
*/
private int mSubscriptionType;
+ /**
+ * Public copy constructor.
+ * @hide
+ */
+ public SubscriptionInfo(SubscriptionInfo info) {
+ this(info.mId, info.mIccId, info.mSimSlotIndex, info.mDisplayName, info.mCarrierName,
+ info.mNameSource, info.mIconTint, info.mNumber, info.mDataRoaming, info.mIconBitmap,
+ info.mMcc, info.mMnc, info.mCountryIso, info.mIsEmbedded, info.mAccessRules,
+ info.mCardString, info.mCardId, info.mIsOpportunistic,
+ info.mGroupUUID == null ? null : info.mGroupUUID.toString(), info.mIsGroupDisabled,
+ info.mCarrierId, info.mProfileClass, info.mSubscriptionType, info.mGroupOwner);
+ }
+
/**
* @hide
*/
@@ -271,12 +286,26 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * @return the ICC ID.
+ * Returns the ICC ID if the calling app has been granted the READ_PRIVILEGED_PHONE_STATE
+ * permission, has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or
+ * is a device owner or profile owner that has been granted the READ_PHONE_STATE permission.
+ * The profile owner is an app that owns a managed profile on the device; for more details see
+ * Work profiles. Profile
+ * owner access is deprecated and will be removed in a future release.
+ *
+ * @return the ICC ID, or an empty string if one of these requirements is not met
*/
public String getIccId() {
return this.mIccId;
}
+ /**
+ * @hide
+ */
+ public void clearIccId() {
+ this.mIccId = "";
+ }
+
/**
* @return the slot index of this Subscription's SIM card.
*/
From 1c1bc30ce345f770de44b4348420e860d0958534 Mon Sep 17 00:00:00 2001
From: Michael Groover
Date: Wed, 19 Feb 2020 23:37:42 -0800
Subject: [PATCH 040/288] Whitelist READ_PRIVILEGED_PHONE_STATE for the
contacts provider
In Android 10 access to device identifiers was moved from a runtime
permission to the privileged READ_PRIVILEGED_PHONE_STATE permission.
One of the non-resettable identifiers missed was SubscriptionInfo#
getIccId. The contacts provider currently uses this ID to upgrade
to version 1003 of the database; to ensure there are no issues with
this upgrade this permission is being whitelisted for the contacts
provider.
Bug: 131909991
Bug: 173421434
Test: atest ContactsProviderTests
Change-Id: I7574f787a7e55aa0337237b9fe916ee143a2f697
Merged-In: I7574f787a7e55aa0337237b9fe916ee143a2f697
---
data/etc/privapp-permissions-platform.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index ff4e1005c9d98..3648280233053 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -177,6 +177,7 @@ applications that come with the platform
+
From 5aba70130d3539fc77cfdfb4b550d8e86acd8a60 Mon Sep 17 00:00:00 2001
From: Michael Groover
Date: Fri, 20 Mar 2020 13:13:36 -0700
Subject: [PATCH 041/288] Add method to clear out
SubscriptionInfo#getCardString
In Androird 10 access to device identifiers was limited to apps with
the READ_PRIVILEGED_PHONE_STATE permission, carrier privileges, the
READ_DEVICE_IDENTIFIERS appop set to allow, or those that pass a
device / profile owner check. TelephonyManager#getSimSerialNumber
was guarded behind these new access requirements, but the same value
is still accessible as the cardString from the SubscriptionInfo.
While getCardString is a hidden API SubscriptionInfo#toString can
be used to obtain this value.
Bug: 152057778
Bug: 173421434
Test: atest SubscriptionControllerTest
Change-Id: I9b8786a321b1ba79d81b74c3041589f30df8e9c8
Merged-In: I9b8786a321b1ba79d81b74c3041589f30df8e9c8
---
.../android/telephony/SubscriptionInfo.java | 21 +++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index d34b8c932f77d..fcfc728b2c7be 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -38,8 +38,6 @@ import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
-import com.android.telephony.Rlog;
-
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -629,8 +627,16 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * @return the card string of the SIM card which contains the subscription. The card string is
- * the ICCID for UICCs or the EID for eUICCs.
+ * Returns the card string if the calling app has been granted the READ_PRIVILEGED_PHONE_STATE
+ * permission, has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or
+ * is a device owner or profile owner on an organization owned device that has been granted the
+ * READ_PHONE_STATE permission. The profile owner is an app that owns a managed profile on the
+ * device; for more details see
+ * Work profiles.
+ *
+ * @return the card string of the SIM card which contains the subscription or an empty string
+ * if these requirements are not met. The card string is the ICCID for UICCs or the EID for
+ * eUICCs.
* @hide
* //TODO rename usages in LPA: UiccSlotUtil.java, UiccSlotsManager.java, UiccSlotInfoTest.java
*/
@@ -638,6 +644,13 @@ public class SubscriptionInfo implements Parcelable {
return this.mCardString;
}
+ /**
+ * @hide
+ */
+ public void clearCardString() {
+ this.mCardString = "";
+ }
+
/**
* Returns the card ID of the SIM card which contains the subscription (see
* {@link UiccCardInfo#getCardId()}.
From 400e7d5dda91c58690f8febba4132856e560663d Mon Sep 17 00:00:00 2001
From: raylinhsu
Date: Wed, 6 Jan 2021 15:58:05 +0800
Subject: [PATCH 042/288] Fix the ambient lux which did not update for blocking
zone
When the display enters doze mode, it should unregister the light
sensor. It should register light sensor again when the display
state change from doze to on otherwise we will lost lux information
and then the refresh rate could not be locked.
Bug: 176018998
Test: atest DisplayModeDirectorTest
Test: no fps transition in the blocking zone when the display state change
Test: check if the lux will update when the display state change
Change-Id: Ibf2e7cb2e336c88a583f00f59abcbc9d06ec3d62
Merged-In: Ibf2e7cb2e336c88a583f00f59abcbc9d06ec3d62
---
.../server/display/DisplayModeDirector.java | 35 ++++++++++++----
.../display/DisplayModeDirectorTest.java | 42 ++++++++++++++++++-
2 files changed, 67 insertions(+), 10 deletions(-)
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 02ee059194ec3..29a77e2b6ef23 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -52,9 +52,12 @@ import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
/**
@@ -1160,7 +1163,8 @@ public class DisplayModeDirector {
// mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
// changeable and low power mode off. After initialization, these states will
// be updated from the same handler thread.
- private boolean mDefaultDisplayOn = false;
+ private int mDefaultDisplayState = Display.STATE_UNKNOWN;
+ private boolean mIsDeviceActive = false;
private boolean mRefreshRateChangeable = false;
private boolean mLowPowerModeEnabled = false;
@@ -1341,7 +1345,8 @@ public class DisplayModeDirector {
pw.println(" BrightnessObserver");
pw.println(" mAmbientLux: " + mAmbientLux);
pw.println(" mBrightness: " + mBrightness);
- pw.println(" mDefaultDisplayOn: " + mDefaultDisplayOn);
+ pw.println(" mDefaultDisplayState: " + mDefaultDisplayState);
+ pw.println(" mIsDeviceActive: " + mIsDeviceActive);
pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled);
pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable);
pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
@@ -1567,14 +1572,17 @@ public class DisplayModeDirector {
private void updateDefaultDisplayState() {
Display display = mContext.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY);
- boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF;
- setDefaultDisplayState(defaultDisplayOn);
+ if (display == null) {
+ return;
+ }
+
+ setDefaultDisplayState(display.getState());
}
@VisibleForTesting
- public void setDefaultDisplayState(boolean on) {
- if (mDefaultDisplayOn != on) {
- mDefaultDisplayOn = on;
+ public void setDefaultDisplayState(int state) {
+ if (mDefaultDisplayState != state) {
+ mDefaultDisplayState = state;
updateSensorStatus();
}
}
@@ -1595,15 +1603,19 @@ public class DisplayModeDirector {
}
private boolean isDeviceActive() {
- return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext);
+ mIsDeviceActive = mInjector.isDeviceInteractive(mContext);
+ return (mDefaultDisplayState == Display.STATE_ON)
+ && mIsDeviceActive;
}
private final class LightSensorEventListener implements SensorEventListener {
final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
private float mLastSensorData;
+ private long mTimestamp;
public void dumpLocked(PrintWriter pw) {
pw.println(" mLastSensorData: " + mLastSensorData);
+ pw.println(" mTimestamp: " + formatTimestamp(mTimestamp));
}
@Override
@@ -1627,6 +1639,7 @@ public class DisplayModeDirector {
}
long now = SystemClock.uptimeMillis();
+ mTimestamp = System.currentTimeMillis();
if (mAmbientFilter != null) {
mAmbientFilter.addValue(now, mLastSensorData);
}
@@ -1653,6 +1666,12 @@ public class DisplayModeDirector {
mHandler.removeCallbacks(mInjectSensorEventRunnable);
}
+ private String formatTimestamp(long time) {
+ SimpleDateFormat dateFormat =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
+ return dateFormat.format(new Date(time));
+ }
+
private void processSensorData(long now) {
if (mAmbientFilter != null) {
mAmbientLux = mAmbientFilter.getEstimate(now);
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index c467ee949aeba..e202145b7dbee 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.annotation.NonNull;
import android.content.ContentResolver;
@@ -410,7 +411,7 @@ public class DisplayModeDirectorTest {
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90);
director.getSettingsObserver().setDefaultRefreshRate(90);
- director.getBrightnessObserver().setDefaultDisplayState(true);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInLowZone(90);
@@ -453,7 +454,7 @@ public class DisplayModeDirectorTest {
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
- director.getBrightnessObserver().setDefaultDisplayState(true);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInHighZone(60);
@@ -490,6 +491,43 @@ public class DisplayModeDirectorTest {
assertVoteForRefreshRateLocked(vote, 60 /*fps*/);
}
+ @Test
+ public void testSensorRegistration() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90 /*fps*/);
+ director.getSettingsObserver().setDefaultRefreshRate(90);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+
+ director.start(sensorManager);
+ ArgumentCaptor listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+
+ // Dispaly state changed from On to Doze
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_DOZE);
+ Mockito.verify(sensorManager)
+ .unregisterListener(listenerCaptor.capture());
+
+ // Dispaly state changed from Doze to On
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+ Mockito.verify(sensorManager, times(2))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+
+ }
+
private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
final DisplayModeDirector.RefreshRateRange expectedRange =
From 2565d0b3a1d7e277d5bbafbd1caf92ad5924a21e Mon Sep 17 00:00:00 2001
From: Winson
Date: Wed, 9 Dec 2020 17:00:23 -0800
Subject: [PATCH 043/288] Only allow BROWSABLE && DEFAULT Intents to be always
opened
Auto verification of app links requires that an intent filter declare
action=VIEW, scheme=HTTP(S), category=BROWSABLE. However,
PackageManagerService was not taking that into account, missing the
category requirement.
But the app info Settings UI did take category into account, so it was
possible for a user to set an application to automatically open web URIs
without understanding that this also granted domains that were not
visible in the app info UI.
To resolve both this, this change makes it so that both auto
verification and the Settings state can only consider the app as
"always" open only if the Intent contains both BROWSABLE and DEFAULT.
Bug: 175139501
Bug: 175319005
Test: manual, see bug for reproduction steps
Merged-In: Ib957258735893bf2779bed19bd400c6726ee6478
Change-Id: Ib957258735893bf2779bed19bd400c6726ee6478
(cherry picked from commit 4266f938c6705466d3844385c2031d1e80ee7b2d)
---
.../com/android/server/pm/PackageManagerService.java | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 19b2c7c70cbf4..955cb7b74ce37 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -23,6 +23,8 @@ import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
+import static android.content.Intent.CATEGORY_BROWSABLE;
+import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
@@ -7881,6 +7883,13 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ " : linkgen=" + linkGeneration);
}
+
+ if (!intent.hasCategory(CATEGORY_BROWSABLE)
+ || !intent.hasCategory(CATEGORY_DEFAULT)) {
+ undefinedList.add(info);
+ continue;
+ }
+
// Use link-enabled generation as preferredOrder, i.e.
// prefer newly-enabled over earlier-enabled.
info.preferredOrder = linkGeneration;
From a154d7a648ebad658c107eb2f6cfd0bf0f8bb8b2 Mon Sep 17 00:00:00 2001
From: Winson
Date: Wed, 9 Dec 2020 17:00:23 -0800
Subject: [PATCH 044/288] Only allow BROWSABLE && DEFAULT Intents to be always
opened
Auto verification of app links requires that an intent filter declare
action=VIEW, scheme=HTTP(S), category=BROWSABLE. However,
PackageManagerService was not taking that into account, missing the
category requirement.
But the app info Settings UI did take category into account, so it was
possible for a user to set an application to automatically open web URIs
without understanding that this also granted domains that were not
visible in the app info UI.
To resolve both this, this change makes it so that both auto
verification and the Settings state can only consider the app as
"always" open only if the Intent contains both BROWSABLE and DEFAULT.
Bug: 175139501
Bug: 175319005
Test: manual, see bug for reproduction steps
Merged-In: Ib957258735893bf2779bed19bd400c6726ee6478
Change-Id: Ib957258735893bf2779bed19bd400c6726ee6478
(cherry picked from commit 4266f938c6705466d3844385c2031d1e80ee7b2d)
---
.../com/android/server/pm/PackageManagerService.java | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c55ab9a3a2e92..363969497ed5d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -26,6 +26,8 @@ import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
+import static android.content.Intent.CATEGORY_BROWSABLE;
+import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
@@ -7150,6 +7152,13 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ " : linkgen=" + linkGeneration);
}
+
+ if (!intent.hasCategory(CATEGORY_BROWSABLE)
+ || !intent.hasCategory(CATEGORY_DEFAULT)) {
+ undefinedList.add(info);
+ continue;
+ }
+
// Use link-enabled generation as preferredOrder, i.e.
// prefer newly-enabled over earlier-enabled.
info.preferredOrder = linkGeneration;
From 5a90c4c75b97ab1e2d0cb1c9abffb4e6969224af Mon Sep 17 00:00:00 2001
From: Winson
Date: Wed, 9 Dec 2020 17:00:23 -0800
Subject: [PATCH 045/288] Only allow BROWSABLE && DEFAULT Intents to be always
opened
Auto verification of app links requires that an intent filter declare
action=VIEW, scheme=HTTP(S), category=BROWSABLE. However,
PackageManagerService was not taking that into account, missing the
category requirement.
But the app info Settings UI did take category into account, so it was
possible for a user to set an application to automatically open web URIs
without understanding that this also granted domains that were not
visible in the app info UI.
To resolve both this, this change makes it so that both auto
verification and the Settings state can only consider the app as
"always" open only if the Intent contains both BROWSABLE and DEFAULT.
Bug: 175139501
Bug: 175319005
Test: manual, see bug for reproduction steps
Merged-In: Ib957258735893bf2779bed19bd400c6726ee6478
Change-Id: Ib957258735893bf2779bed19bd400c6726ee6478
(cherry picked from commit 4266f938c6705466d3844385c2031d1e80ee7b2d)
---
.../com/android/server/pm/PackageManagerService.java | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 50b5f79eab200..eaabf468fc46c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -29,6 +29,8 @@ import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
+import static android.content.Intent.CATEGORY_BROWSABLE;
+import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
@@ -7780,6 +7782,13 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ " : linkgen=" + linkGeneration);
}
+
+ if (!intent.hasCategory(CATEGORY_BROWSABLE)
+ || !intent.hasCategory(CATEGORY_DEFAULT)) {
+ undefinedList.add(info);
+ continue;
+ }
+
// Use link-enabled generation as preferredOrder, i.e.
// prefer newly-enabled over earlier-enabled.
info.preferredOrder = linkGeneration;
From eb8d0c749d3b8063672cee803006da2d8d550e97 Mon Sep 17 00:00:00 2001
From: Winson
Date: Wed, 9 Dec 2020 17:00:23 -0800
Subject: [PATCH 046/288] Only allow BROWSABLE && DEFAULT Intents to be always
opened
Auto verification of app links requires that an intent filter declare
action=VIEW, scheme=HTTP(S), category=BROWSABLE. However,
PackageManagerService was not taking that into account, missing the
category requirement.
But the app info Settings UI did take category into account, so it was
possible for a user to set an application to automatically open web URIs
without understanding that this also granted domains that were not
visible in the app info UI.
To resolve both this, this change makes it so that both auto
verification and the Settings state can only consider the app as
"always" open only if the Intent contains both BROWSABLE and DEFAULT.
Bug: 175139501
Bug: 175319005
Test: manual, see bug for reproduction steps
Merged-In: Ib957258735893bf2779bed19bd400c6726ee6478
Change-Id: Ib957258735893bf2779bed19bd400c6726ee6478
(cherry picked from commit 4266f938c6705466d3844385c2031d1e80ee7b2d)
---
.../com/android/server/pm/PackageManagerService.java | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e8b2f3c428bce..18f2655dc2d6a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -35,6 +35,8 @@ import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.EXTRA_VERSION_CODE;
import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
+import static android.content.Intent.CATEGORY_BROWSABLE;
+import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
@@ -7695,6 +7697,13 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ " : linkgen=" + linkGeneration);
}
+
+ if (!intent.hasCategory(CATEGORY_BROWSABLE)
+ || !intent.hasCategory(CATEGORY_DEFAULT)) {
+ undefinedList.add(info);
+ continue;
+ }
+
// Use link-enabled generation as preferredOrder, i.e.
// prefer newly-enabled over earlier-enabled.
info.preferredOrder = linkGeneration;
From 0f337fcea8de957ac2939f602461913283ecabce Mon Sep 17 00:00:00 2001
From: Winson
Date: Wed, 9 Dec 2020 17:00:23 -0800
Subject: [PATCH 047/288] Only allow BROWSABLE && DEFAULT Intents to be always
opened
Auto verification of app links requires that an intent filter declare
action=VIEW, scheme=HTTP(S), category=BROWSABLE. However,
PackageManagerService was not taking that into account, missing the
category requirement.
But the app info Settings UI did take category into account, so it was
possible for a user to set an application to automatically open web URIs
without understanding that this also granted domains that were not
visible in the app info UI.
To resolve both this, this change makes it so that both auto
verification and the Settings state can only consider the app as
"always" open only if the Intent contains both BROWSABLE and DEFAULT.
Bug: 175139501
Bug: 175319005
Test: manual, see bug for reproduction steps
Merged-In: Ib957258735893bf2779bed19bd400c6726ee6478
Change-Id: Ib957258735893bf2779bed19bd400c6726ee6478
(cherry picked from commit 4266f938c6705466d3844385c2031d1e80ee7b2d)
---
.../com/android/server/pm/PackageManagerService.java | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0af4ff4df93f7..40723e073ba10 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -35,6 +35,8 @@ import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.EXTRA_VERSION_CODE;
import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
+import static android.content.Intent.CATEGORY_BROWSABLE;
+import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
@@ -7704,6 +7706,13 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ " : linkgen=" + linkGeneration);
}
+
+ if (!intent.hasCategory(CATEGORY_BROWSABLE)
+ || !intent.hasCategory(CATEGORY_DEFAULT)) {
+ undefinedList.add(info);
+ continue;
+ }
+
// Use link-enabled generation as preferredOrder, i.e.
// prefer newly-enabled over earlier-enabled.
info.preferredOrder = linkGeneration;
From dca1c94faa2ffb9787cb2ab61c3cb2f9503422e4 Mon Sep 17 00:00:00 2001
From: Winson
Date: Wed, 9 Dec 2020 17:00:23 -0800
Subject: [PATCH 048/288] Only allow BROWSABLE && DEFAULT Intents to be always
opened
Auto verification of app links requires that an intent filter declare
action=VIEW, scheme=HTTP(S), category=BROWSABLE. However,
PackageManagerService was not taking that into account, missing the
category requirement.
But the app info Settings UI did take category into account, so it was
possible for a user to set an application to automatically open web URIs
without understanding that this also granted domains that were not
visible in the app info UI.
To resolve both this, this change makes it so that both auto
verification and the Settings state can only consider the app as
"always" open only if the Intent contains both BROWSABLE and DEFAULT.
Bug: 175139501
Bug: 175319005
Test: manual, see bug for reproduction steps
Merged-In: Ib957258735893bf2779bed19bd400c6726ee6478
Change-Id: Ib957258735893bf2779bed19bd400c6726ee6478
(cherry picked from commit 4266f938c6705466d3844385c2031d1e80ee7b2d)
---
.../com/android/server/pm/PackageManagerService.java | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index df05b56bd0e06..84213092081d9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -29,6 +29,8 @@ import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
+import static android.content.Intent.CATEGORY_BROWSABLE;
+import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
@@ -7738,6 +7740,13 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ " : linkgen=" + linkGeneration);
}
+
+ if (!intent.hasCategory(CATEGORY_BROWSABLE)
+ || !intent.hasCategory(CATEGORY_DEFAULT)) {
+ undefinedList.add(info);
+ continue;
+ }
+
// Use link-enabled generation as preferredOrder, i.e.
// prefer newly-enabled over earlier-enabled.
info.preferredOrder = linkGeneration;
From 2d067c33c0cda9f9467eb6a56bc4a27801e86ed1 Mon Sep 17 00:00:00 2001
From: Bill Yi
Date: Tue, 12 Jan 2021 17:12:06 -0800
Subject: [PATCH 049/288] Import translations. DO NOT MERGE ANYWHERE
Auto-generated-cl: translation import
Change-Id: I7d579a91209b6663d63f891cd6b6f4e6f24a89b4
---
packages/SettingsLib/res/values-or/strings.xml | 4 ++--
packages/SettingsLib/res/values-zh-rCN/strings.xml | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 173ae1ccceff9..aec99fd3ab842 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -114,7 +114,7 @@
"ଶ୍ରବଣ ଯନ୍ତ୍ର ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"
"ପେୟାର୍"
"ପେୟାର୍"
- "କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"
+ "ବାତିଲ୍ କରନ୍ତୁ"
"ପେୟାରିଂ ଫଳରେ ସଂଯୁକ୍ତ ଥିବା ବେଳେ ଆପଣଙ୍କ ସମ୍ପର୍କଗୁଡ଼ିକୁ ଏବଂ କଲ୍ର ଇତିବୃତିକୁ ଆକସେସ୍ ମଞ୍ଜୁର ହୁଏ।"
"%1$s ସହ ପେୟାର୍ କରିହେଲା ନାହିଁ।"
"ଏକ ଭୁଲ୍ PIN କିମ୍ବା ପାସକୀ କାରଣରୁ %1$s ସହ ପେୟାର୍ କରିପାରିଲା ନାହିଁ।"
@@ -493,7 +493,7 @@
"ଅଧିକ ସମୟ।"
"କମ୍ ସମୟ।"
- "କ୍ୟାନ୍ସଲ୍"
+ "ବାତିଲ୍"
"ଠିକ୍ ଅଛି"
"ଚାଲୁ କରନ୍ତୁ"
"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍ କରନ୍ତୁ"
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 5db88183c8e68..a3e1532ac8897 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -214,7 +214,7 @@
"使用二维码配对设备"
"使用二维码扫描器配对新设备"
"使用配对码配对设备"
- "使用六位数验证码配对新设备"
+ "使用六位数的配对码配对新设备"
"已配对的设备"
"当前已连接"
"设备详细信息"
From d348974ecd0570d5884ee033e5c180ed7447940e Mon Sep 17 00:00:00 2001
From: raylinhsu
Date: Wed, 13 Jan 2021 13:06:15 +0800
Subject: [PATCH 050/288] DO NOT MERGE Revert "Fix the ambient lux which did
not update for blocking zone"
This reverts commit 400e7d5dda91c58690f8febba4132856e560663d.
Bug: 176018998
Test: atest DisplayModeDirectorTest
Change-Id: I58c60a3a124db11e9ae091831a84a704b3c4e6bf
---
.../server/display/DisplayModeDirector.java | 35 ++++------------
.../display/DisplayModeDirectorTest.java | 42 +------------------
2 files changed, 10 insertions(+), 67 deletions(-)
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 29a77e2b6ef23..02ee059194ec3 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -52,12 +52,9 @@ import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Date;
import java.util.List;
-import java.util.Locale;
import java.util.Objects;
/**
@@ -1163,8 +1160,7 @@ public class DisplayModeDirector {
// mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
// changeable and low power mode off. After initialization, these states will
// be updated from the same handler thread.
- private int mDefaultDisplayState = Display.STATE_UNKNOWN;
- private boolean mIsDeviceActive = false;
+ private boolean mDefaultDisplayOn = false;
private boolean mRefreshRateChangeable = false;
private boolean mLowPowerModeEnabled = false;
@@ -1345,8 +1341,7 @@ public class DisplayModeDirector {
pw.println(" BrightnessObserver");
pw.println(" mAmbientLux: " + mAmbientLux);
pw.println(" mBrightness: " + mBrightness);
- pw.println(" mDefaultDisplayState: " + mDefaultDisplayState);
- pw.println(" mIsDeviceActive: " + mIsDeviceActive);
+ pw.println(" mDefaultDisplayOn: " + mDefaultDisplayOn);
pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled);
pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable);
pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
@@ -1572,17 +1567,14 @@ public class DisplayModeDirector {
private void updateDefaultDisplayState() {
Display display = mContext.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY);
- if (display == null) {
- return;
- }
-
- setDefaultDisplayState(display.getState());
+ boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF;
+ setDefaultDisplayState(defaultDisplayOn);
}
@VisibleForTesting
- public void setDefaultDisplayState(int state) {
- if (mDefaultDisplayState != state) {
- mDefaultDisplayState = state;
+ public void setDefaultDisplayState(boolean on) {
+ if (mDefaultDisplayOn != on) {
+ mDefaultDisplayOn = on;
updateSensorStatus();
}
}
@@ -1603,19 +1595,15 @@ public class DisplayModeDirector {
}
private boolean isDeviceActive() {
- mIsDeviceActive = mInjector.isDeviceInteractive(mContext);
- return (mDefaultDisplayState == Display.STATE_ON)
- && mIsDeviceActive;
+ return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext);
}
private final class LightSensorEventListener implements SensorEventListener {
final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
private float mLastSensorData;
- private long mTimestamp;
public void dumpLocked(PrintWriter pw) {
pw.println(" mLastSensorData: " + mLastSensorData);
- pw.println(" mTimestamp: " + formatTimestamp(mTimestamp));
}
@Override
@@ -1639,7 +1627,6 @@ public class DisplayModeDirector {
}
long now = SystemClock.uptimeMillis();
- mTimestamp = System.currentTimeMillis();
if (mAmbientFilter != null) {
mAmbientFilter.addValue(now, mLastSensorData);
}
@@ -1666,12 +1653,6 @@ public class DisplayModeDirector {
mHandler.removeCallbacks(mInjectSensorEventRunnable);
}
- private String formatTimestamp(long time) {
- SimpleDateFormat dateFormat =
- new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
- return dateFormat.format(new Date(time));
- }
-
private void processSensorData(long now) {
if (mAmbientFilter != null) {
mAmbientLux = mAmbientFilter.getEstimate(now);
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index e202145b7dbee..c467ee949aeba 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -34,7 +34,6 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.annotation.NonNull;
import android.content.ContentResolver;
@@ -411,7 +410,7 @@ public class DisplayModeDirectorTest {
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90);
director.getSettingsObserver().setDefaultRefreshRate(90);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+ director.getBrightnessObserver().setDefaultDisplayState(true);
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInLowZone(90);
@@ -454,7 +453,7 @@ public class DisplayModeDirectorTest {
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+ director.getBrightnessObserver().setDefaultDisplayState(true);
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInHighZone(60);
@@ -491,43 +490,6 @@ public class DisplayModeDirectorTest {
assertVoteForRefreshRateLocked(vote, 60 /*fps*/);
}
- @Test
- public void testSensorRegistration() {
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- setPeakRefreshRate(90 /*fps*/);
- director.getSettingsObserver().setDefaultRefreshRate(90);
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
-
- director.start(sensorManager);
- ArgumentCaptor listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
- .registerListener(
- listenerCaptor.capture(),
- eq(lightSensor),
- anyInt(),
- any(Handler.class));
-
- // Dispaly state changed from On to Doze
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_DOZE);
- Mockito.verify(sensorManager)
- .unregisterListener(listenerCaptor.capture());
-
- // Dispaly state changed from Doze to On
- director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
- Mockito.verify(sensorManager, times(2))
- .registerListener(
- listenerCaptor.capture(),
- eq(lightSensor),
- anyInt(),
- any(Handler.class));
-
- }
-
private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
final DisplayModeDirector.RefreshRateRange expectedRange =
From b14bed88c5aec64fef15b4e727fff4623bb8efe7 Mon Sep 17 00:00:00 2001
From: raylinhsu
Date: Wed, 13 Jan 2021 13:13:02 +0800
Subject: [PATCH 051/288] DO NOT MERGE Revert "Allow DeviceConfig to change
display settings for high zone"
This reverts commit cf55c21064cd6f906ca7e64f66f1cc8e1346fda6.
Bug: 176018998
Test: atest DisplayModeDirectorTest
Change-Id: Ib576e16257d7903b39efa8015c2af3be8c0d0c40
---
.../hardware/display/DisplayManager.java | 57 ++------
.../server/display/DisplayModeDirector.java | 132 ++++--------------
.../display/DisplayModeDirectorTest.java | 53 +------
3 files changed, 40 insertions(+), 202 deletions(-)
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 0fa4ca8a574b5..c1ba2094d3cf6 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -875,52 +875,12 @@ public final class DisplayManager {
public interface DeviceConfig {
/**
- * Key for refresh rate in the low zone defined by thresholds.
+ * Key for refresh rate in the zone defined by thresholds.
*
- * Note that the name and value don't match because they were added before we had a high
- * zone to consider.
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.integer#config_defaultZoneBehavior
*/
- String KEY_REFRESH_RATE_IN_LOW_ZONE = "refresh_rate_in_zone";
-
- /**
- * Key for accessing the low display brightness thresholds for the configured refresh
- * rate zone.
- * The value will be a pair of comma separated integers representing the minimum and maximum
- * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
- *
- * Note that the name and value don't match because they were added before we had a high
- * zone to consider.
- *
- * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
- * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
- * @hide
- */
- String KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS =
- "peak_refresh_rate_brightness_thresholds";
-
- /**
- * Key for accessing the low ambient brightness thresholds for the configured refresh
- * rate zone. The value will be a pair of comma separated integers representing the minimum
- * and maximum thresholds of the zone, respectively, in lux.
- *
- * Note that the name and value don't match because they were added before we had a high
- * zone to consider.
- *
- * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
- * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
- * @hide
- */
- String KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS =
- "peak_refresh_rate_ambient_thresholds";
- /**
- * Key for refresh rate in the high zone defined by thresholds.
- *
- * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
- * @see android.R.integer#config_fixedRefreshRateInHighZone
- */
- String KEY_REFRESH_RATE_IN_HIGH_ZONE = "refresh_rate_in_high_zone";
+ String KEY_REFRESH_RATE_IN_ZONE = "refresh_rate_in_zone";
/**
* Key for accessing the display brightness thresholds for the configured refresh rate zone.
@@ -928,11 +888,11 @@ public final class DisplayManager {
* thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
- * @see android.R.array#config_brightnessHighThresholdsOfFixedRefreshRate
+ * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
* @hide
*/
- String KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS =
- "fixed_refresh_rate_high_display_brightness_thresholds";
+ String KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS =
+ "peak_refresh_rate_brightness_thresholds";
/**
* Key for accessing the ambient brightness thresholds for the configured refresh rate zone.
@@ -940,11 +900,12 @@ public final class DisplayManager {
* thresholds of the zone, respectively, in lux.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
- * @see android.R.array#config_ambientHighThresholdsOfFixedRefreshRate
+ * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
* @hide
*/
- String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS =
- "fixed_refresh_rate_high_ambient_brightness_thresholds";
+ String KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS =
+ "peak_refresh_rate_ambient_thresholds";
+
/**
* Key for default peak refresh rate
*
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 02ee059194ec3..2a60839ab702a 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -69,8 +69,6 @@ public class DisplayModeDirector {
private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
- private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
- private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
// Special ID used to indicate that given vote is to be applied globally, rather than to a
// specific display.
@@ -517,6 +515,13 @@ public class DisplayModeDirector {
}
}
+ @VisibleForTesting
+ void updateSettingForHighZone(int refreshRate, int[] brightnessThresholds,
+ int[] ambientThresholds) {
+ mBrightnessObserver.updateThresholdsRefreshRateForHighZone(refreshRate,
+ brightnessThresholds, ambientThresholds);
+ }
+
/**
* Listens for changes refresh rate coordination.
*/
@@ -535,35 +540,11 @@ public class DisplayModeDirector {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: {
+ case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED:
Pair thresholds = (Pair) msg.obj;
mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
thresholds.first, thresholds.second);
break;
- }
-
- case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED: {
- int refreshRateInZone = msg.arg1;
- mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
- refreshRateInZone);
- break;
- }
-
- case MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED: {
- Pair thresholds = (Pair) msg.obj;
-
- mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged(
- thresholds.first, thresholds.second);
-
- break;
- }
-
- case MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED: {
- int refreshRateInZone = msg.arg1;
- mBrightnessObserver.onDeviceConfigRefreshRateInHighZoneChanged(
- refreshRateInZone);
- break;
- }
case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
Float defaultPeakRefreshRate = (Float) msg.obj;
@@ -571,6 +552,12 @@ public class DisplayModeDirector {
defaultPeakRefreshRate);
break;
+ case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED:
+ int refreshRateInZone = msg.arg1;
+ mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
+ refreshRateInZone);
+ break;
+
case MSG_REFRESH_RATE_RANGE_CHANGED:
DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener =
(DesiredDisplayModeSpecsListener) msg.obj;
@@ -1233,6 +1220,13 @@ public class DisplayModeDirector {
mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
}
+ public void updateThresholdsRefreshRateForHighZone(int refreshRate,
+ int[] brightnessThresholds, int[] ambientThresholds) {
+ mRefreshRateInHighZone = refreshRate;
+ mHighDisplayBrightnessThresholds = brightnessThresholds;
+ mHighAmbientBrightnessThresholds = ambientThresholds;
+ }
+
public void observe(SensorManager sensorManager) {
mSensorManager = sensorManager;
final ContentResolver cr = mContext.getContentResolver();
@@ -1252,22 +1246,7 @@ public class DisplayModeDirector {
mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
}
-
- int[] highDisplayBrightnessThresholds =
- mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
- int[] highAmbientBrightnessThresholds =
- mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
-
- if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
- && highDisplayBrightnessThresholds.length
- == highAmbientBrightnessThresholds.length) {
- mHighDisplayBrightnessThresholds = highDisplayBrightnessThresholds;
- mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
- }
-
mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
- mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone();
-
restartObserver();
mDeviceConfigDisplaySettings.startListening();
}
@@ -1314,29 +1293,6 @@ public class DisplayModeDirector {
}
}
- public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
- int[] ambientThresholds) {
- if (displayThresholds != null && ambientThresholds != null
- && displayThresholds.length == ambientThresholds.length) {
- mHighDisplayBrightnessThresholds = displayThresholds;
- mHighAmbientBrightnessThresholds = ambientThresholds;
- } else {
- // Invalid or empty. Use device default.
- mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray(
- R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
- mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray(
- R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
- }
- restartObserver();
- }
-
- public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) {
- if (refreshRate != mRefreshRateInHighZone) {
- mRefreshRateInHighZone = refreshRate;
- restartObserver();
- }
- }
-
public void dumpLocked(PrintWriter pw) {
pw.println(" BrightnessObserver");
pw.println(" mAmbientLux: " + mAmbientLux);
@@ -1712,7 +1668,7 @@ public class DisplayModeDirector {
public int[] getLowDisplayBrightnessThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig.
- KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
}
/*
@@ -1721,7 +1677,7 @@ public class DisplayModeDirector {
public int[] getLowAmbientBrightnessThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig.
- KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
}
public int getRefreshRateInLowZone() {
@@ -1730,37 +1686,7 @@ public class DisplayModeDirector {
int refreshRate = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
- defaultRefreshRateInZone);
-
- return refreshRate;
- }
-
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getHighDisplayBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
- }
-
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getHighAmbientBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
- }
-
- public int getRefreshRateInHighZone() {
- int defaultRefreshRateInZone = mContext.getResources().getInteger(
- R.integer.config_fixedRefreshRateInHighZone);
-
- int refreshRate = mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
defaultRefreshRateInZone);
return refreshRate;
@@ -1795,16 +1721,6 @@ public class DisplayModeDirector {
.sendToTarget();
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
.sendToTarget();
-
- int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
- int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
- int refreshRateInHighZone = getRefreshRateInHighZone();
-
- mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
- new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
- .sendToTarget();
- mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
- .sendToTarget();
}
private int[] getIntArrayProperty(String prop) {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index c467ee949aeba..4ee6a553b9d15 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -16,12 +16,9 @@
package com.android.server.display;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE;
import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER;
@@ -454,11 +451,7 @@ public class DisplayModeDirectorTest {
setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(true);
-
- final FakeDeviceConfig config = mInjector.getDeviceConfig();
- config.setRefreshRateInHighZone(60);
- config.setHighDisplayBrightnessThresholds(new int[] { 255 });
- config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+ director.updateSettingForHighZone(60, new int[] {255}, new int[] {8000});
Sensor lightSensor = createLightSensor();
SensorManager sensorManager = createMockSensorManager(lightSensor);
@@ -515,7 +508,7 @@ public class DisplayModeDirectorTest {
void setRefreshRateInLowZone(int fps) {
putPropertyAndNotify(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_LOW_ZONE,
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_ZONE,
String.valueOf(fps));
}
@@ -528,7 +521,7 @@ public class DisplayModeDirectorTest {
putPropertyAndNotify(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS,
+ KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS,
thresholds);
}
@@ -541,39 +534,7 @@ public class DisplayModeDirectorTest {
putPropertyAndNotify(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS,
- thresholds);
- }
-
- void setRefreshRateInHighZone(int fps) {
- putPropertyAndNotify(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HIGH_ZONE,
- String.valueOf(fps));
- }
-
- void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) {
- String thresholds = toPropertyValue(brightnessThresholds);
-
- if (DEBUG) {
- Slog.e(TAG, "Brightness Thresholds = " + thresholds);
- }
-
- putPropertyAndNotify(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS,
- thresholds);
- }
-
- void setHighAmbientBrightnessThresholds(int[] ambientThresholds) {
- String thresholds = toPropertyValue(ambientThresholds);
-
- if (DEBUG) {
- Slog.e(TAG, "Ambient Thresholds = " + thresholds);
- }
-
- putPropertyAndNotify(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS,
+ KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS,
thresholds);
}
From b4bf83a7733d5334a2df829874684f896df4d3eb Mon Sep 17 00:00:00 2001
From: raylinhsu
Date: Wed, 13 Jan 2021 13:27:25 +0800
Subject: [PATCH 052/288] DO NOT MERGE Revert "Set fixed fps when ambient or
display brightness is high"
This reverts commit 4a0a896accfc86dff48d867ee07239fb8b53c626.
Bug: 176018998
Test: atest DisplayModeDirectorTest
Change-Id: Ic9329c59c358742696adbbca9cc2b33f25787eb1
---
core/res/res/values/config.xml | 29 -
core/res/res/values/symbols.xml | 5 -
services/core/Android.bp | 7 -
.../server/display/DisplayModeDirector.java | 573 +++++-------------
.../server/wm/HighRefreshRateBlacklist.java | 2 +-
.../server/wm/WindowManagerConstants.java | 2 +-
.../server/wm/WindowManagerService.java | 2 +-
.../{ => wm}/utils/DeviceConfigInterface.java | 13 +-
services/tests/servicestests/Android.bp | 3 +-
.../display/DisplayModeDirectorTest.java | 378 +-----------
.../wm/HighRefreshRateBlacklistTest.java | 2 +-
.../server/wm/WindowManagerConstantsTest.java | 2 +-
.../wm/utils}/FakeDeviceConfigInterface.java | 16 +-
13 files changed, 169 insertions(+), 865 deletions(-)
rename services/core/java/com/android/server/{ => wm}/utils/DeviceConfigInterface.java (90%)
rename services/tests/{servicestests/utils/com/android/server/testutils => wmtests/src/com/android/server/wm/utils}/FakeDeviceConfigInterface.java (92%)
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5f02eb6a6a37e..550601af0faa1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4138,35 +4138,6 @@
If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
0
-
-
-
-
-
-
-
-
-
-
- 0
-
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b80e8e1cd9805..06f357e79a621 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3783,11 +3783,6 @@
-
-
-
-
-
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 7585d6ba9c60f..4bba0d892f3b2 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -1,10 +1,3 @@
-filegroup {
- name: "services.core-sources-deviceconfig-interface",
- srcs: [
- "java/com/android/server/utils/DeviceConfigInterface.java"
- ],
-}
-
filegroup {
name: "services.core-sources",
srcs: ["java/**/*.java"],
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 2a60839ab702a..3c050804f01d9 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -46,10 +46,8 @@ import android.view.DisplayInfo;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
-import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -66,9 +64,9 @@ public class DisplayModeDirector {
private static final boolean DEBUG = false;
private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
- private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
+ private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
- private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
+ private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4;
// Special ID used to indicate that given vote is to be applied globally, rather than to a
// specific display.
@@ -81,13 +79,6 @@ public class DisplayModeDirector {
private final Context mContext;
private final DisplayModeDirectorHandler mHandler;
- private final Injector mInjector;
-
- private final AppRequestObserver mAppRequestObserver;
- private final SettingsObserver mSettingsObserver;
- private final DisplayObserver mDisplayObserver;
- private final DeviceConfigInterface mDeviceConfig;
- private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
// A map from the display ID to the collection of votes and their priority. The latter takes
// the form of another map from the priority to the vote itself so that each priority is
@@ -98,19 +89,17 @@ public class DisplayModeDirector {
// A map from the display ID to the default mode of that display.
private SparseArray mDefaultModeByDisplay;
+ private final AppRequestObserver mAppRequestObserver;
+ private final SettingsObserver mSettingsObserver;
+ private final DisplayObserver mDisplayObserver;
private BrightnessObserver mBrightnessObserver;
+ private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
- this(context, handler, new RealInjector());
- }
-
- public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
- @NonNull Injector injector) {
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
- mInjector = injector;
mVotesByDisplay = new SparseArray<>();
mSupportedModesByDisplay = new SparseArray<>();
mDefaultModeByDisplay = new SparseArray<>();
@@ -119,7 +108,6 @@ public class DisplayModeDirector {
mDisplayObserver = new DisplayObserver(context, handler);
mBrightnessObserver = new BrightnessObserver(context, handler);
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
- mDeviceConfig = injector.getDeviceConfig();
}
/**
@@ -360,23 +348,6 @@ public class DisplayModeDirector {
}
}
- /**
- * Retrieve the Vote for the given display and priority. Intended only for testing purposes.
- *
- * @param displayId the display to query for
- * @param priority the priority of the vote to return
- * @return the vote corresponding to the given {@code displayId} and {@code priority},
- * or {@code null} if there isn't one
- */
- @VisibleForTesting
- @Nullable
- Vote getVote(int displayId, int priority) {
- synchronized (mLock) {
- SparseArray votes = getVotesLocked(displayId);
- return votes.get(priority);
- }
- }
-
/**
* Print the object's state and debug information into the given stream.
*
@@ -494,17 +465,6 @@ public class DisplayModeDirector {
mBrightnessObserver = brightnessObserver;
}
- @VisibleForTesting
- BrightnessObserver getBrightnessObserver() {
- return mBrightnessObserver;
- }
-
- @VisibleForTesting
- SettingsObserver getSettingsObserver() {
- return mSettingsObserver;
- }
-
-
@VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
@@ -515,13 +475,6 @@ public class DisplayModeDirector {
}
}
- @VisibleForTesting
- void updateSettingForHighZone(int refreshRate, int[] brightnessThresholds,
- int[] ambientThresholds) {
- mBrightnessObserver.updateThresholdsRefreshRateForHighZone(refreshRate,
- brightnessThresholds, ambientThresholds);
- }
-
/**
* Listens for changes refresh rate coordination.
*/
@@ -540,10 +493,15 @@ public class DisplayModeDirector {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED:
+ case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
Pair thresholds = (Pair) msg.obj;
- mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
- thresholds.first, thresholds.second);
+
+ if (thresholds != null) {
+ mBrightnessObserver.onDeviceConfigThresholdsChanged(
+ thresholds.first, thresholds.second);
+ } else {
+ mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null);
+ }
break;
case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
@@ -552,9 +510,9 @@ public class DisplayModeDirector {
defaultPeakRefreshRate);
break;
- case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED:
+ case MSG_REFRESH_RATE_IN_ZONE_CHANGED:
int refreshRateInZone = msg.arg1;
- mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
+ mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged(
refreshRateInZone);
break;
@@ -727,11 +685,10 @@ public class DisplayModeDirector {
// by all other considerations. It acts to set a default frame rate for a device.
public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
- // FLICKER votes for a single refresh rate like [60,60], [90,90] or null.
+ // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
// If the higher voters result is a range, it will fix the rate to a single choice.
- // It's used to avoid refresh rate switches in certain conditions which may result in the
- // user seeing the display flickering when the switches occur.
- public static final int PRIORITY_FLICKER = 1;
+ // It's used to avoid rate switch in certain conditions.
+ public static final int PRIORITY_LOW_BRIGHTNESS = 1;
// SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
// It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
@@ -804,8 +761,8 @@ public class DisplayModeDirector {
switch (priority) {
case PRIORITY_DEFAULT_REFRESH_RATE:
return "PRIORITY_DEFAULT_REFRESH_RATE";
- case PRIORITY_FLICKER:
- return "PRIORITY_FLICKER";
+ case PRIORITY_LOW_BRIGHTNESS:
+ return "PRIORITY_LOW_BRIGHTNESS";
case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
case PRIORITY_APP_REQUEST_REFRESH_RATE:
@@ -830,8 +787,7 @@ public class DisplayModeDirector {
}
}
- @VisibleForTesting
- final class SettingsObserver extends ContentObserver {
+ private final class SettingsObserver extends ContentObserver {
private final Uri mPeakRefreshRateSetting =
Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
private final Uri mMinRefreshRateSetting =
@@ -854,7 +810,8 @@ public class DisplayModeDirector {
public void observe() {
final ContentResolver cr = mContext.getContentResolver();
- mInjector.registerPeakRefreshRateObserver(cr, this);
+ cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this,
+ UserHandle.USER_SYSTEM);
cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
UserHandle.USER_SYSTEM);
cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
@@ -872,13 +829,6 @@ public class DisplayModeDirector {
}
}
- public void setDefaultRefreshRate(float refreshRate) {
- synchronized (mLock) {
- mDefaultRefreshRate = refreshRate;
- updateRefreshRateSettingLocked();
- }
- }
-
public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
if (defaultPeakRefreshRate == null) {
defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
@@ -1083,7 +1033,6 @@ public class DisplayModeDirector {
@Override
public void onDisplayChanged(int displayId) {
updateDisplayModes(displayId);
- // TODO: Break the coupling between DisplayObserver and BrightnessObserver.
mBrightnessObserver.onDisplayChanged(displayId);
}
@@ -1122,16 +1071,15 @@ public class DisplayModeDirector {
*/
@VisibleForTesting
public class BrightnessObserver extends ContentObserver {
+ // TODO: brightnessfloat: change this to the float setting
+ private final Uri mDisplayBrightnessSetting =
+ Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
private final static int LIGHT_SENSOR_RATE_MS = 250;
- private int[] mLowDisplayBrightnessThresholds;
- private int[] mLowAmbientBrightnessThresholds;
- private int[] mHighDisplayBrightnessThresholds;
- private int[] mHighAmbientBrightnessThresholds;
+ private int[] mDisplayBrightnessThresholds;
+ private int[] mAmbientBrightnessThresholds;
// valid threshold if any item from the array >= 0
- private boolean mShouldObserveDisplayLowChange;
- private boolean mShouldObserveAmbientLowChange;
- private boolean mShouldObserveDisplayHighChange;
- private boolean mShouldObserveAmbientHighChange;
+ private boolean mShouldObserveDisplayChange;
+ private boolean mShouldObserveAmbientChange;
private SensorManager mSensorManager;
private Sensor mLightSensor;
@@ -1139,114 +1087,46 @@ public class DisplayModeDirector {
// Take it as low brightness before valid sensor data comes
private float mAmbientLux = -1.0f;
private AmbientFilter mAmbientFilter;
- private int mBrightness = -1;
private final Context mContext;
- // Enable light sensor only when mShouldObserveAmbientLowChange is true or
- // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
- // changeable and low power mode off. After initialization, these states will
+ // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
+ // refresh rate changeable and low power mode off. After initialization, these states will
// be updated from the same handler thread.
- private boolean mDefaultDisplayOn = false;
+ private boolean mScreenOn = false;
private boolean mRefreshRateChangeable = false;
private boolean mLowPowerModeEnabled = false;
- private int mRefreshRateInLowZone;
- private int mRefreshRateInHighZone;
+ private int mRefreshRateInZone;
BrightnessObserver(Context context, Handler handler) {
super(handler);
mContext = context;
- mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
+ mDisplayBrightnessThresholds = context.getResources().getIntArray(
R.array.config_brightnessThresholdsOfPeakRefreshRate);
- mLowAmbientBrightnessThresholds = context.getResources().getIntArray(
+ mAmbientBrightnessThresholds = context.getResources().getIntArray(
R.array.config_ambientThresholdsOfPeakRefreshRate);
- if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
- throw new RuntimeException("display low brightness threshold array and ambient "
- + "brightness threshold array have different length: "
- + "displayBrightnessThresholds="
- + Arrays.toString(mLowDisplayBrightnessThresholds)
- + ", ambientBrightnessThresholds="
- + Arrays.toString(mLowAmbientBrightnessThresholds));
+ if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) {
+ throw new RuntimeException("display brightness threshold array and ambient "
+ + "brightness threshold array have different length");
}
-
- mHighDisplayBrightnessThresholds = context.getResources().getIntArray(
- R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
- mHighAmbientBrightnessThresholds = context.getResources().getIntArray(
- R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
- if (mHighDisplayBrightnessThresholds.length
- != mHighAmbientBrightnessThresholds.length) {
- throw new RuntimeException("display high brightness threshold array and ambient "
- + "brightness threshold array have different length: "
- + "displayBrightnessThresholds="
- + Arrays.toString(mHighDisplayBrightnessThresholds)
- + ", ambientBrightnessThresholds="
- + Arrays.toString(mHighAmbientBrightnessThresholds));
- }
- mRefreshRateInHighZone = context.getResources().getInteger(
- R.integer.config_fixedRefreshRateInHighZone);
- }
-
- /**
- * @return the refresh to lock to when in a low brightness zone
- */
- @VisibleForTesting
- int getRefreshRateInLowZone() {
- return mRefreshRateInLowZone;
- }
-
- /**
- * @return the display brightness thresholds for the low brightness zones
- */
- @VisibleForTesting
- int[] getLowDisplayBrightnessThresholds() {
- return mLowDisplayBrightnessThresholds;
- }
-
- /**
- * @return the ambient brightness thresholds for the low brightness zones
- */
- @VisibleForTesting
- int[] getLowAmbientBrightnessThresholds() {
- return mLowAmbientBrightnessThresholds;
- }
-
- public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) {
- mSensorManager = sensorManager;
- mLightSensor = lightSensor;
-
- mSensorManager.registerListener(mLightSensorListener,
- mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
- }
-
- public void updateThresholdsRefreshRateForHighZone(int refreshRate,
- int[] brightnessThresholds, int[] ambientThresholds) {
- mRefreshRateInHighZone = refreshRate;
- mHighDisplayBrightnessThresholds = brightnessThresholds;
- mHighAmbientBrightnessThresholds = ambientThresholds;
}
public void observe(SensorManager sensorManager) {
mSensorManager = sensorManager;
- final ContentResolver cr = mContext.getContentResolver();
- mBrightness = Settings.System.getIntForUser(cr,
- Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
// DeviceConfig is accessible after system ready.
- int[] lowDisplayBrightnessThresholds =
- mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
- int[] lowAmbientBrightnessThresholds =
- mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
+ int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
+ int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
- if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
- && lowDisplayBrightnessThresholds.length
- == lowAmbientBrightnessThresholds.length) {
- mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds;
- mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
+ if (brightnessThresholds != null && ambientThresholds != null
+ && brightnessThresholds.length == ambientThresholds.length) {
+ mDisplayBrightnessThresholds = brightnessThresholds;
+ mAmbientBrightnessThresholds = ambientThresholds;
}
- mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
+ mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone();
restartObserver();
mDeviceConfigDisplaySettings.startListening();
}
@@ -1258,7 +1138,7 @@ public class DisplayModeDirector {
updateSensorStatus();
if (!changeable) {
// Revoke previous vote from BrightnessObserver
- updateVoteLocked(Vote.PRIORITY_FLICKER, null);
+ updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null);
}
}
}
@@ -1270,25 +1150,25 @@ public class DisplayModeDirector {
}
}
- public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
+ public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds,
int[] ambientThresholds) {
- if (displayThresholds != null && ambientThresholds != null
- && displayThresholds.length == ambientThresholds.length) {
- mLowDisplayBrightnessThresholds = displayThresholds;
- mLowAmbientBrightnessThresholds = ambientThresholds;
+ if (brightnessThresholds != null && ambientThresholds != null
+ && brightnessThresholds.length == ambientThresholds.length) {
+ mDisplayBrightnessThresholds = brightnessThresholds;
+ mAmbientBrightnessThresholds = ambientThresholds;
} else {
// Invalid or empty. Use device default.
- mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+ mDisplayBrightnessThresholds = mContext.getResources().getIntArray(
R.array.config_brightnessThresholdsOfPeakRefreshRate);
- mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+ mAmbientBrightnessThresholds = mContext.getResources().getIntArray(
R.array.config_ambientThresholdsOfPeakRefreshRate);
}
restartObserver();
}
- public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) {
- if (refreshRate != mRefreshRateInLowZone) {
- mRefreshRateInLowZone = refreshRate;
+ public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) {
+ if (refreshRate != mRefreshRateInZone) {
+ mRefreshRateInZone = refreshRate;
restartObserver();
}
}
@@ -1296,95 +1176,48 @@ public class DisplayModeDirector {
public void dumpLocked(PrintWriter pw) {
pw.println(" BrightnessObserver");
pw.println(" mAmbientLux: " + mAmbientLux);
- pw.println(" mBrightness: " + mBrightness);
- pw.println(" mDefaultDisplayOn: " + mDefaultDisplayOn);
- pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled);
- pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable);
- pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
- pw.println(" mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange);
- pw.println(" mRefreshRateInLowZone: " + mRefreshRateInLowZone);
+ pw.println(" mRefreshRateInZone: " + mRefreshRateInZone);
- for (int d : mLowDisplayBrightnessThresholds) {
- pw.println(" mDisplayLowBrightnessThreshold: " + d);
+ for (int d: mDisplayBrightnessThresholds) {
+ pw.println(" mDisplayBrightnessThreshold: " + d);
}
- for (int d : mLowAmbientBrightnessThresholds) {
- pw.println(" mAmbientLowBrightnessThreshold: " + d);
- }
-
- pw.println(" mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange);
- pw.println(" mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange);
- pw.println(" mRefreshRateInHighZone: " + mRefreshRateInHighZone);
-
- for (int d : mHighDisplayBrightnessThresholds) {
- pw.println(" mDisplayHighBrightnessThresholds: " + d);
- }
-
- for (int d : mHighAmbientBrightnessThresholds) {
- pw.println(" mAmbientHighBrightnessThresholds: " + d);
+ for (int d: mAmbientBrightnessThresholds) {
+ pw.println(" mAmbientBrightnessThreshold: " + d);
}
mLightSensorListener.dumpLocked(pw);
-
- if (mAmbientFilter != null) {
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.setIndent(" ");
- mAmbientFilter.dump(ipw);
- }
}
public void onDisplayChanged(int displayId) {
if (displayId == Display.DEFAULT_DISPLAY) {
- updateDefaultDisplayState();
+ onScreenOn(isDefaultDisplayOn());
}
}
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
synchronized (mLock) {
- final ContentResolver cr = mContext.getContentResolver();
- int brightness = Settings.System.getIntForUser(cr,
- Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
- if (brightness != mBrightness) {
- mBrightness = brightness;
- onBrightnessChangedLocked();
- }
+ onBrightnessChangedLocked();
}
}
private void restartObserver() {
+ mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
+ mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
+
final ContentResolver cr = mContext.getContentResolver();
-
- if (mRefreshRateInLowZone > 0) {
- mShouldObserveDisplayLowChange = hasValidThreshold(
- mLowDisplayBrightnessThresholds);
- mShouldObserveAmbientLowChange = hasValidThreshold(
- mLowAmbientBrightnessThresholds);
- } else {
- mShouldObserveDisplayLowChange = false;
- mShouldObserveAmbientLowChange = false;
- }
-
- if (mRefreshRateInHighZone > 0) {
- mShouldObserveDisplayHighChange = hasValidThreshold(
- mHighDisplayBrightnessThresholds);
- mShouldObserveAmbientHighChange = hasValidThreshold(
- mHighAmbientBrightnessThresholds);
- } else {
- mShouldObserveDisplayHighChange = false;
- mShouldObserveAmbientHighChange = false;
- }
-
- if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) {
+ if (mShouldObserveDisplayChange) {
// Content Service does not check if an listener has already been registered.
// To ensure only one listener is registered, force an unregistration first.
- mInjector.unregisterBrightnessObserver(cr, this);
- mInjector.registerBrightnessObserver(cr, this);
+ cr.unregisterContentObserver(this);
+ cr.registerContentObserver(mDisplayBrightnessSetting,
+ false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
} else {
- mInjector.unregisterBrightnessObserver(cr, this);
+ cr.unregisterContentObserver(this);
}
- if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
+ if (mShouldObserveAmbientChange) {
Resources resources = mContext.getResources();
String lightSensorType = resources.getString(
com.android.internal.R.string.config_displayLightSensorType);
@@ -1410,6 +1243,8 @@ public class DisplayModeDirector {
mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
mLightSensor = lightSensor;
+
+ onScreenOn(isDefaultDisplayOn());
}
} else {
mAmbientFilter = null;
@@ -1428,7 +1263,11 @@ public class DisplayModeDirector {
* Checks to see if at least one value is positive, in which case it is necessary to listen
* to value changes.
*/
- private boolean hasValidThreshold(int[] a) {
+ private boolean checkShouldObserve(int[] a) {
+ if (mRefreshRateInZone <= 0) {
+ return false;
+ }
+
for (int d: a) {
if (d >= 0) {
return true;
@@ -1438,13 +1277,13 @@ public class DisplayModeDirector {
return false;
}
- private boolean isInsideLowZone(int brightness, float lux) {
- for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) {
- int disp = mLowDisplayBrightnessThresholds[i];
- int ambi = mLowAmbientBrightnessThresholds[i];
+ private boolean isInsideZone(int brightness, float lux) {
+ for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
+ int disp = mDisplayBrightnessThresholds[i];
+ int ambi = mAmbientBrightnessThresholds[i];
if (disp >= 0 && ambi >= 0) {
- if (brightness <= disp && lux <= ambi) {
+ if (brightness <= disp && mAmbientLux <= ambi) {
return true;
}
} else if (disp >= 0) {
@@ -1452,30 +1291,7 @@ public class DisplayModeDirector {
return true;
}
} else if (ambi >= 0) {
- if (lux <= ambi) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- private boolean isInsideHighZone(int brightness, float lux) {
- for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) {
- int disp = mHighDisplayBrightnessThresholds[i];
- int ambi = mHighAmbientBrightnessThresholds[i];
-
- if (disp >= 0 && ambi >= 0) {
- if (brightness >= disp && lux >= ambi) {
- return true;
- }
- } else if (disp >= 0) {
- if (brightness >= disp) {
- return true;
- }
- } else if (ambi >= 0) {
- if (lux >= ambi) {
+ if (mAmbientLux <= ambi) {
return true;
}
}
@@ -1483,54 +1299,27 @@ public class DisplayModeDirector {
return false;
}
+ // TODO: brightnessfloat: make it use float not int
private void onBrightnessChangedLocked() {
+ int brightness = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, -1);
+
Vote vote = null;
-
- if (mBrightness < 0) {
- // Either the setting isn't available or we shouldn't be observing yet anyways.
- // Either way, just bail out since there's nothing we can do here.
- return;
- }
-
- boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
- if (insideLowZone) {
- vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
- }
-
- boolean insideHighZone = hasValidHighZone()
- && isInsideHighZone(mBrightness, mAmbientLux);
- if (insideHighZone) {
- vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
+ boolean insideZone = isInsideZone(brightness, mAmbientLux);
+ if (insideZone) {
+ vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone);
}
if (DEBUG) {
- Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux
- + ", Vote " + vote);
+ Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux +
+ ", Vote " + vote);
}
- updateVoteLocked(Vote.PRIORITY_FLICKER, vote);
+ updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
}
- private boolean hasValidLowZone() {
- return mRefreshRateInLowZone > 0
- && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange);
- }
-
- private boolean hasValidHighZone() {
- return mRefreshRateInHighZone > 0
- && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange);
- }
-
- private void updateDefaultDisplayState() {
- Display display = mContext.getSystemService(DisplayManager.class)
- .getDisplay(Display.DEFAULT_DISPLAY);
- boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF;
- setDefaultDisplayState(defaultDisplayOn);
- }
-
- @VisibleForTesting
- public void setDefaultDisplayState(boolean on) {
- if (mDefaultDisplayOn != on) {
- mDefaultDisplayOn = on;
+ private void onScreenOn(boolean on) {
+ if (mScreenOn != on) {
+ mScreenOn = on;
updateSensorStatus();
}
}
@@ -1540,8 +1329,8 @@ public class DisplayModeDirector {
return;
}
- if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
- && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
+ if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
+ && mRefreshRateChangeable) {
mSensorManager.registerListener(mLightSensorListener,
mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
} else {
@@ -1550,8 +1339,11 @@ public class DisplayModeDirector {
}
}
- private boolean isDeviceActive() {
- return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext);
+ private boolean isDefaultDisplayOn() {
+ final Display display = mContext.getSystemService(DisplayManager.class)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+ return display.getState() != Display.STATE_OFF
+ && mContext.getSystemService(PowerManager.class).isInteractive();
}
private final class LightSensorEventListener implements SensorEventListener {
@@ -1569,33 +1361,23 @@ public class DisplayModeDirector {
Slog.d(TAG, "On sensor changed: " + mLastSensorData);
}
- boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
- mLowAmbientBrightnessThresholds);
- boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
- mHighAmbientBrightnessThresholds);
- if ((lowZoneChanged && mLastSensorData < mAmbientLux)
- || (highZoneChanged && mLastSensorData > mAmbientLux)) {
- // Easier to see flicker at lower brightness environment or high brightness
- // environment. Forget the history to get immediate response.
- if (mAmbientFilter != null) {
- mAmbientFilter.clear();
- }
+ boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux);
+ if (zoneChanged && mLastSensorData < mAmbientLux) {
+ // Easier to see flicker at lower brightness environment. Forget the history to
+ // get immediate response.
+ mAmbientFilter.clear();
}
long now = SystemClock.uptimeMillis();
- if (mAmbientFilter != null) {
- mAmbientFilter.addValue(now, mLastSensorData);
- }
+ mAmbientFilter.addValue(now, mLastSensorData);
mHandler.removeCallbacks(mInjectSensorEventRunnable);
processSensorData(now);
- if ((lowZoneChanged && mLastSensorData > mAmbientLux)
- || (highZoneChanged && mLastSensorData < mAmbientLux)) {
+ if (zoneChanged && mLastSensorData > mAmbientLux) {
// Sensor may not report new event if there is no brightness change.
// Need to keep querying the temporal filter for the latest estimation,
- // until sensor readout and filter estimation are in the same zone or
- // is interrupted by a new sensor event.
+ // until enter in higher lux zone or is interrupted by a new sensor event.
mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
}
}
@@ -1610,19 +1392,17 @@ public class DisplayModeDirector {
}
private void processSensorData(long now) {
- if (mAmbientFilter != null) {
- mAmbientLux = mAmbientFilter.getEstimate(now);
- } else {
- mAmbientLux = mLastSensorData;
- }
+ mAmbientLux = mAmbientFilter.getEstimate(now);
synchronized (mLock) {
onBrightnessChangedLocked();
}
}
- private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) {
- for (final float boundary : luxThresholds) {
+ private boolean isDifferentZone(float lux1, float lux2) {
+ for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) {
+ final float boundary = mAmbientBrightnessThresholds[z];
+
// Test each boundary. See if the current value and the new value are at
// different sides.
if ((lux1 <= boundary && lux2 > boundary)
@@ -1642,10 +1422,7 @@ public class DisplayModeDirector {
processSensorData(now);
// Inject next event if there is a possible zone change.
- if (isDifferentZone(mLastSensorData, mAmbientLux,
- mLowAmbientBrightnessThresholds)
- || isDifferentZone(mLastSensorData, mAmbientLux,
- mHighAmbientBrightnessThresholds)) {
+ if (isDifferentZone(mLastSensorData, mAmbientLux)) {
mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
}
}
@@ -1658,14 +1435,14 @@ public class DisplayModeDirector {
}
public void startListening() {
- mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
BackgroundThread.getExecutor(), this);
}
/*
* Return null if no such property or wrong format (not comma separated integers).
*/
- public int[] getLowDisplayBrightnessThresholds() {
+ public int[] getBrightnessThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig.
KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
@@ -1674,29 +1451,17 @@ public class DisplayModeDirector {
/*
* Return null if no such property or wrong format (not comma separated integers).
*/
- public int[] getLowAmbientBrightnessThresholds() {
+ public int[] getAmbientThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig.
KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
}
- public int getRefreshRateInLowZone() {
- int defaultRefreshRateInZone = mContext.getResources().getInteger(
- R.integer.config_defaultRefreshRateInZone);
-
- int refreshRate = mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
- defaultRefreshRateInZone);
-
- return refreshRate;
- }
-
/*
* Return null if no such property
*/
public Float getDefaultPeakRefreshRate() {
- float defaultPeakRefreshRate = mDeviceConfig.getFloat(
+ float defaultPeakRefreshRate = DeviceConfig.getFloat(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
@@ -1706,25 +1471,36 @@ public class DisplayModeDirector {
return defaultPeakRefreshRate;
}
+ public int getRefreshRateInZone() {
+ int defaultRefreshRateInZone = mContext.getResources().getInteger(
+ R.integer.config_defaultRefreshRateInZone);
+
+ int refreshRate = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
+ defaultRefreshRateInZone);
+
+ return refreshRate;
+ }
+
@Override
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
+ int[] brightnessThresholds = getBrightnessThresholds();
+ int[] ambientThresholds = getAmbientThresholds();
Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
+ int refreshRateInZone = getRefreshRateInZone();
+
+ mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
+ new Pair(brightnessThresholds, ambientThresholds))
+ .sendToTarget();
mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
defaultPeakRefreshRate).sendToTarget();
-
- int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
- int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
- int refreshRateInLowZone = getRefreshRateInLowZone();
-
- mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
- new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
- .sendToTarget();
- mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
- .sendToTarget();
+ mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone,
+ 0).sendToTarget();
}
private int[] getIntArrayProperty(String prop) {
- String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
+ String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
null);
if (strArray != null) {
@@ -1751,59 +1527,4 @@ public class DisplayModeDirector {
}
}
- interface Injector {
- // TODO: brightnessfloat: change this to the float setting
- Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
- Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
-
- @NonNull
- DeviceConfigInterface getDeviceConfig();
-
- void registerBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer);
-
- void unregisterBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer);
-
- void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer);
-
- boolean isDeviceInteractive(@NonNull Context context);
- }
-
- @VisibleForTesting
- static class RealInjector implements Injector {
-
- @Override
- @NonNull
- public DeviceConfigInterface getDeviceConfig() {
- return DeviceConfigInterface.REAL;
- }
-
- @Override
- public void registerBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/,
- observer, UserHandle.USER_SYSTEM);
- }
-
- @Override
- public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- cr.unregisterContentObserver(observer);
- }
-
- @Override
- public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
- observer, UserHandle.USER_SYSTEM);
- }
-
- @Override
- public boolean isDeviceInteractive(@NonNull Context ctx) {
- return ctx.getSystemService(PowerManager.class).isInteractive();
- }
- }
-
}
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
index 09ab004b4b3bc..d9cf637ffaf8f 100644
--- a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
+++ b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
@@ -27,7 +27,7 @@ import android.util.ArraySet;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
-import com.android.server.utils.DeviceConfigInterface;
+import com.android.server.wm.utils.DeviceConfigInterface;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index a5ebf9ac74b9b..b0c5dbc6cca38 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -23,7 +23,7 @@ import android.provider.AndroidDeviceConfig;
import android.provider.DeviceConfig;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.utils.DeviceConfigInterface;
+import com.android.server.wm.utils.DeviceConfigInterface;
import java.io.PrintWriter;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d9594a40bde31..b7a2eb3c705d3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -282,8 +282,8 @@ import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
import com.android.server.protolog.ProtoLogImpl;
import com.android.server.protolog.common.ProtoLog;
-import com.android.server.utils.DeviceConfigInterface;
import com.android.server.utils.PriorityDump;
+import com.android.server.wm.utils.DeviceConfigInterface;
import java.io.BufferedWriter;
import java.io.DataInputStream;
diff --git a/services/core/java/com/android/server/utils/DeviceConfigInterface.java b/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java
similarity index 90%
rename from services/core/java/com/android/server/utils/DeviceConfigInterface.java
rename to services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java
index ff609031b57c5..ab7e7f63cafd4 100644
--- a/services/core/java/com/android/server/utils/DeviceConfigInterface.java
+++ b/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.utils;
+package com.android.server.wm.utils;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -53,11 +53,6 @@ public interface DeviceConfigInterface {
*/
boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue);
- /**
- * @see DeviceConfig#getFloat
- */
- float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue);
-
/**
* @see DeviceConfig#addOnPropertiesChangedListener
*/
@@ -100,12 +95,6 @@ public interface DeviceConfigInterface {
return DeviceConfig.getBoolean(namespace, name, defaultValue);
}
- @Override
- public float getFloat(@NonNull String namespace, @NonNull String name,
- float defaultValue) {
- return DeviceConfig.getFloat(namespace, name, defaultValue);
- }
-
@Override
public void addOnPropertiesChangedListener(String namespace, Executor executor,
DeviceConfig.OnPropertiesChangedListener listener) {
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index e57097e488811..979f4e179e95d 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -48,6 +48,7 @@ android_test {
// TODO: remove once Android migrates to JUnit 4.12,
// which provides assertThrows
"testng",
+
],
aidl: {
@@ -109,7 +110,6 @@ java_library {
"utils/**/*.java",
"utils/**/*.kt",
"utils-mockito/**/*.kt",
- ":services.core-sources-deviceconfig-interface",
],
static_libs: [
"junit",
@@ -126,7 +126,6 @@ java_library {
"utils/**/*.java",
"utils/**/*.kt",
"utils-mockito/**/*.kt",
- ":services.core-sources-deviceconfig-interface",
],
static_libs: [
"junit",
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 4ee6a553b9d15..43a396d8e5d7d 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -16,96 +16,49 @@
package com.android.server.display;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE;
-
-import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER;
-
-import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import android.annotation.NonNull;
-import android.content.ContentResolver;
import android.content.Context;
-import android.content.ContextWrapper;
-import android.database.ContentObserver;
-import android.hardware.Sensor;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Looper;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.test.mock.MockContentResolver;
-import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
-import androidx.test.core.app.ApplicationProvider;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.display.DisplayModeDirector.BrightnessObserver;
import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
import com.android.server.display.DisplayModeDirector.Vote;
-import com.android.server.testutils.FakeDeviceConfigInterface;
import com.google.common.truth.Truth;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DisplayModeDirectorTest {
// The tolerance within which we consider something approximately equals.
- private static final String TAG = "DisplayModeDirectorTest";
- private static final boolean DEBUG = false;
private static final float FLOAT_TOLERANCE = 0.01f;
private Context mContext;
- private FakesInjector mInjector;
- private Handler mHandler;
- @Rule
- public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
- final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
- when(mContext.getContentResolver()).thenReturn(resolver);
- mInjector = new FakesInjector();
- mHandler = new Handler(Looper.getMainLooper());
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
}
private DisplayModeDirector createDirectorFromRefreshRateArray(
float[] refreshRates, int baseModeId) {
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector);
+ new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper()));
int displayId = 0;
Display.Mode[] modes = new Display.Mode[refreshRates.length];
for (int i = 0; i < refreshRates.length; i++) {
@@ -206,9 +159,9 @@ public class DisplayModeDirectorTest {
}
@Test
- public void testFlickerHasLowerPriorityThanUser() {
- assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
- assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_SIZE);
+ public void testBrightnessHasLowerPriorityThanUser() {
+ assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
+ assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE);
int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
@@ -216,7 +169,7 @@ public class DisplayModeDirectorTest {
SparseArray> votesByDisplay = new SparseArray<>();
votesByDisplay.put(displayId, votes);
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
- votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -224,7 +177,7 @@ public class DisplayModeDirectorTest {
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
- votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -232,7 +185,7 @@ public class DisplayModeDirectorTest {
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
- votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -240,7 +193,7 @@ public class DisplayModeDirectorTest {
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
- votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -249,10 +202,10 @@ public class DisplayModeDirectorTest {
@Test
public void testAppRequestRefreshRateRange() {
- // Confirm that the app request range doesn't include flicker or min refresh rate settings,
- // but does include everything else.
+ // Confirm that the app request range doesn't include low brightness or min refresh rate
+ // settings, but does include everything else.
assertTrue(
- PRIORITY_FLICKER < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
+ Vote.PRIORITY_LOW_BRIGHTNESS < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE
< Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE
@@ -263,7 +216,7 @@ public class DisplayModeDirectorTest {
SparseArray votes = new SparseArray<>();
SparseArray> votesByDisplay = new SparseArray<>();
votesByDisplay.put(displayId, votes);
- votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -349,307 +302,4 @@ public class DisplayModeDirectorTest {
verifyBrightnessObserverCall(director, 90, 90, 0, 90, 90);
verifyBrightnessObserverCall(director, 120, 90, 0, 120, 90);
}
-
- @Test
- public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() {
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
- SensorManager sensorManager = createMockSensorManager(createLightSensor());
-
- final int initialRefreshRate = 60;
- mInjector.getDeviceConfig().setRefreshRateInLowZone(initialRefreshRate);
- director.start(sensorManager);
- assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
- .isEqualTo(initialRefreshRate);
-
- final int updatedRefreshRate = 90;
- mInjector.getDeviceConfig().setRefreshRateInLowZone(updatedRefreshRate);
- // Need to wait for the property change to propagate to the main thread.
- waitForIdleSync();
- assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
- .isEqualTo(updatedRefreshRate);
- }
-
- @Test
- public void testBrightnessObserverThresholdsInZone() {
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
- SensorManager sensorManager = createMockSensorManager(createLightSensor());
-
- final int[] initialDisplayThresholds = { 10 };
- final int[] initialAmbientThresholds = { 20 };
-
- final FakeDeviceConfig config = mInjector.getDeviceConfig();
- config.setLowDisplayBrightnessThresholds(initialDisplayThresholds);
- config.setLowAmbientBrightnessThresholds(initialAmbientThresholds);
- director.start(sensorManager);
-
- assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
- .isEqualTo(initialDisplayThresholds);
- assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
- .isEqualTo(initialAmbientThresholds);
-
- final int[] updatedDisplayThresholds = { 9, 14 };
- final int[] updatedAmbientThresholds = { -1, 19 };
- config.setLowDisplayBrightnessThresholds(updatedDisplayThresholds);
- config.setLowAmbientBrightnessThresholds(updatedAmbientThresholds);
- // Need to wait for the property change to propagate to the main thread.
- waitForIdleSync();
- assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
- .isEqualTo(updatedDisplayThresholds);
- assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
- .isEqualTo(updatedAmbientThresholds);
- }
-
- @Test
- public void testLockFpsForLowZone() throws Exception {
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- setPeakRefreshRate(90);
- director.getSettingsObserver().setDefaultRefreshRate(90);
- director.getBrightnessObserver().setDefaultDisplayState(true);
-
- final FakeDeviceConfig config = mInjector.getDeviceConfig();
- config.setRefreshRateInLowZone(90);
- config.setLowDisplayBrightnessThresholds(new int[] { 10 });
- config.setLowAmbientBrightnessThresholds(new int[] { 20 });
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
-
- director.start(sensorManager);
-
- ArgumentCaptor listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
- .registerListener(
- listenerCaptor.capture(),
- eq(lightSensor),
- anyInt(),
- any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
- setBrightness(10);
- // Sensor reads 20 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
-
- Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
- assertVoteForRefreshRateLocked(vote, 90 /*fps*/);
-
- setBrightness(125);
- // Sensor reads 1000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
-
- vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
- assertThat(vote).isNull();
- }
-
- @Test
- public void testLockFpsForHighZone() throws Exception {
- DisplayModeDirector director =
- createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- setPeakRefreshRate(90 /*fps*/);
- director.getSettingsObserver().setDefaultRefreshRate(90);
- director.getBrightnessObserver().setDefaultDisplayState(true);
- director.updateSettingForHighZone(60, new int[] {255}, new int[] {8000});
-
- Sensor lightSensor = createLightSensor();
- SensorManager sensorManager = createMockSensorManager(lightSensor);
-
- director.start(sensorManager);
-
- ArgumentCaptor listenerCaptor =
- ArgumentCaptor.forClass(SensorEventListener.class);
- Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
- .registerListener(
- listenerCaptor.capture(),
- eq(lightSensor),
- anyInt(),
- any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
-
- setBrightness(100);
- // Sensor reads 2000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
-
- Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
- assertThat(vote).isNull();
-
- setBrightness(255);
- // Sensor reads 9000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
-
- vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
- assertVoteForRefreshRateLocked(vote, 60 /*fps*/);
- }
-
- private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) {
- assertThat(vote).isNotNull();
- final DisplayModeDirector.RefreshRateRange expectedRange =
- new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate);
- assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
- }
-
- private static class FakeDeviceConfig extends FakeDeviceConfigInterface {
- @Override
- public String getProperty(String namespace, String name) {
- Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
- return super.getProperty(namespace, name);
- }
-
- @Override
- public void addOnPropertiesChangedListener(
- String namespace,
- Executor executor,
- DeviceConfig.OnPropertiesChangedListener listener) {
- Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
- super.addOnPropertiesChangedListener(namespace, executor, listener);
- }
-
- void setRefreshRateInLowZone(int fps) {
- putPropertyAndNotify(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_ZONE,
- String.valueOf(fps));
- }
-
- void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) {
- String thresholds = toPropertyValue(brightnessThresholds);
-
- if (DEBUG) {
- Slog.e(TAG, "Brightness Thresholds = " + thresholds);
- }
-
- putPropertyAndNotify(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS,
- thresholds);
- }
-
- void setLowAmbientBrightnessThresholds(int[] ambientThresholds) {
- String thresholds = toPropertyValue(ambientThresholds);
-
- if (DEBUG) {
- Slog.e(TAG, "Ambient Thresholds = " + thresholds);
- }
-
- putPropertyAndNotify(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS,
- thresholds);
- }
-
- @NonNull
- private static String toPropertyValue(@NonNull int[] intArray) {
- return Arrays.stream(intArray)
- .mapToObj(Integer::toString)
- .collect(Collectors.joining(","));
- }
- }
-
- private void setBrightness(int brightness) {
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS,
- brightness);
- mInjector.notifyBrightnessChanged();
- waitForIdleSync();
- }
-
- private void setPeakRefreshRate(float fps) {
- Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
- fps);
- mInjector.notifyPeakRefreshRateChanged();
- waitForIdleSync();
- }
-
- private static SensorManager createMockSensorManager(Sensor... sensors) {
- SensorManager sensorManager = Mockito.mock(SensorManager.class);
- when(sensorManager.getSensorList(anyInt())).then((invocation) -> {
- List requestedSensors = new ArrayList<>();
- int type = invocation.getArgument(0);
- for (Sensor sensor : sensors) {
- if (sensor.getType() == type || type == Sensor.TYPE_ALL) {
- requestedSensors.add(sensor);
- }
- }
- return requestedSensors;
- });
-
- when(sensorManager.getDefaultSensor(anyInt())).then((invocation) -> {
- int type = invocation.getArgument(0);
- for (Sensor sensor : sensors) {
- if (sensor.getType() == type) {
- return sensor;
- }
- }
- return null;
- });
- return sensorManager;
- }
-
- private static Sensor createLightSensor() {
- try {
- return TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
- } catch (Exception e) {
- // There's nothing we can do if this fails, just throw a RuntimeException so that we
- // don't have to mark every function that might call this as throwing Exception
- throw new RuntimeException("Failed to create a light sensor", e);
- }
- }
-
- private void waitForIdleSync() {
- mHandler.runWithScissors(() -> { }, 500 /*timeout*/);
- }
-
- static class FakesInjector implements DisplayModeDirector.Injector {
- private final FakeDeviceConfig mDeviceConfig;
- private ContentObserver mBrightnessObserver;
- private ContentObserver mPeakRefreshRateObserver;
-
- FakesInjector() {
- mDeviceConfig = new FakeDeviceConfig();
- }
-
- @NonNull
- public FakeDeviceConfig getDeviceConfig() {
- return mDeviceConfig;
- }
-
- @Override
- public void registerBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- if (mBrightnessObserver != null) {
- throw new IllegalStateException("Tried to register a second brightness observer");
- }
- mBrightnessObserver = observer;
- }
-
- @Override
- public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- mBrightnessObserver = null;
- }
-
- void notifyBrightnessChanged() {
- if (mBrightnessObserver != null) {
- mBrightnessObserver.dispatchChange(false /*selfChange*/, DISPLAY_BRIGHTNESS_URI);
- }
- }
-
- @Override
- public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- mPeakRefreshRateObserver = observer;
- }
-
- void notifyPeakRefreshRateChanged() {
- if (mPeakRefreshRateObserver != null) {
- mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
- PEAK_REFRESH_RATE_URI);
- }
- }
-
- @Override
- public boolean isDeviceInteractive(@NonNull Context context) {
- return true;
- }
- }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
index a85e1db32ce51..56cb447e65b0b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
@@ -31,7 +31,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.util.Preconditions;
-import com.android.server.testutils.FakeDeviceConfigInterface;
+import com.android.server.wm.utils.FakeDeviceConfigInterface;
import org.junit.After;
import org.junit.Test;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
index 7a0ef0d7d7a98..52100116df531 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
@@ -32,7 +32,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
-import com.android.server.testutils.FakeDeviceConfigInterface;
+import com.android.server.wm.utils.FakeDeviceConfigInterface;
import org.junit.After;
import org.junit.Before;
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java b/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java
similarity index 92%
rename from services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
rename to services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java
index a67f64596ef51..2904a5b736463 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.testutils;
+package com.android.server.wm.utils;
import android.annotation.NonNull;
import android.provider.DeviceConfig;
@@ -22,7 +22,6 @@ import android.util.ArrayMap;
import android.util.Pair;
import com.android.internal.util.Preconditions;
-import com.android.server.utils.DeviceConfigInterface;
import java.lang.reflect.Constructor;
import java.util.HashMap;
@@ -122,19 +121,6 @@ public class FakeDeviceConfigInterface implements DeviceConfigInterface {
}
}
- @Override
- public float getFloat(String namespace, String name, float defaultValue) {
- String value = getProperty(namespace, name);
- if (value == null) {
- return defaultValue;
- }
- try {
- return Float.parseFloat(value);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
-
@Override
public boolean getBoolean(String namespace, String name, boolean defaultValue) {
String value = getProperty(namespace, name);
From 7c963eaa6e45802bf87e345e64f757403645d588 Mon Sep 17 00:00:00 2001
From: Heemin Seog
Date: Tue, 12 Jan 2021 15:35:13 -0800
Subject: [PATCH 053/288] DO NOT MERGE Remove floating cards launcher
Bug: 177364704
Test: none
Change-Id: I8f19d7d58ee78b64d93de265739a7a880b6a52df
---
data/etc/car/Android.bp | 7 ------
.../com.android.car.floatingcardslauncher.xml | 25 -------------------
2 files changed, 32 deletions(-)
delete mode 100644 data/etc/car/com.android.car.floatingcardslauncher.xml
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 5010c537a66e6..70c3be61009c6 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -150,13 +150,6 @@ prebuilt_etc {
system_ext_specific: true,
}
-prebuilt_etc {
- name: "privapp_whitelist_com.android.car.floatingcardslauncher",
- sub_dir: "permissions",
- src: "com.android.car.floatingcardslauncher.xml",
- filename_from_src: true,
-}
-
prebuilt_etc {
name: "privapp_allowlist_com.google.android.car.networking.preferenceupdater",
sub_dir: "permissions",
diff --git a/data/etc/car/com.android.car.floatingcardslauncher.xml b/data/etc/car/com.android.car.floatingcardslauncher.xml
deleted file mode 100644
index 2755fee4eb557..0000000000000
--- a/data/etc/car/com.android.car.floatingcardslauncher.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
From d26253e6f5df806c7cf328d294f86617eb35cd1f Mon Sep 17 00:00:00 2001
From: Bill Yi
Date: Thu, 14 Jan 2021 15:48:56 -0800
Subject: [PATCH 054/288] Import translations. DO NOT MERGE ANYWHERE
Auto-generated-cl: translation import
Change-Id: I26837872bece6c11b9f8b302919edbffa3d4441e
---
packages/SettingsLib/res/values-kk/strings.xml | 2 +-
packages/SettingsLib/res/values-mr/strings.xml | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 32d8091dd01c8..991d936ac8012 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -359,7 +359,7 @@
"GPU жөндеу қабаттарын қосу"
"GPU жөндеу қабаттарының жүктелуіне рұқсат ету"
"Жеткізушілерді журналға тіркеу"
- "Қате туралы есепте қызмет көрсетушінің құрылғыға қатысты қосымша ақпаратын қамту. Мұнда жеке ақпарат көрсетілуі, батарея шығыны артуы және/немесе қосымша жад пайдаланылуы мүмкін."
+ "Қате туралы есепте жеткізушінің құрылғыға қатысты қосымша ақпараты қамтылады. Мұнда жеке ақпарат көрсетілуі, батарея шығыны артуы және/немесе қосымша жад пайдаланылуы мүмкін."
"Терезе анимациясының өлшемі"
"Ауысу анимациясының өлшемі"
"Аниматор ұзақтығы"
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index c473a60adde29..4a8a6b241831c 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -168,7 +168,7 @@
"उच्चार संश्लेषणाचे एक छोटेसे प्रात्यक्षिक प्ले करा"
"व्हॉइस डेटा इंस्टॉल करा"
"उच्चार संश्लेषणासाठी आवश्यक आवाज डेटा इंस्टॉल करा"
- "हे उच्चार संश्लेषण इंजिन पासवर्ड आणि क्रेडिट कार्ड नंबर यासारख्या वैयक्तिक मजकुरासह, बोलला जाणारा सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. हे %s इंजिनवरून येते. या उच्चार संश्लेषण इंजिनचा वापर सक्षम करायचा?"
+ "हे उच्चार संश्लेषण इंजीन पासवर्ड आणि क्रेडिट कार्ड नंबर यासारख्या वैयक्तिक मजकुरासह, बोलला जाणारा सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. हे %s इंजीनवरून येते. या उच्चार संश्लेषण इंजीनचा वापर सक्षम करायचा?"
"या भाषेस टेक्स्ट टू स्पीचसाठी एका नेटवर्क कनेक्शनची आवश्यकता आहे."
"हे उच्चार संश्लेषणाचे एक उदाहरण आहे"
"डीफॉल्ट भाषा स्थिती"
@@ -177,8 +177,8 @@
"%1$s समर्थित नाही"
"तपासत आहे..."
"%s साठी सेटिंग्ज"
- "इंजिन सेटिंग्ज लाँच करा"
- "प्राधान्य इंजिन"
+ "इंजीन सेटिंग्ज लाँच करा"
+ "प्राधान्य इंजीन"
"सामान्य"
"उच्चार पिच रीसेट करा"
"डीफॉल्टवर मजकूर ज्या पिचवर बोलला जातो तो रीसेट करा."
From d96bcfcdf2f8e9dffaec3a7f12dcee8ea831a99d Mon Sep 17 00:00:00 2001
From: Joanne Chung
Date: Wed, 30 Dec 2020 19:25:39 +0800
Subject: [PATCH 055/288] Get ApplicationInfo using usr id
Use ApplicationInfoAsUser() to get the application information by
user id to make sure we have correct permission, othewise we will
get the SecurityException due to lack of INTERACT_ACROSS_USERS.
Bug: 176313819
Test: atest CtsVoiceRecognitionTestCases
Test: manual.
1.create profile user
2.Install sample recognizer app
3.Function works fine and no security exception occurred after
apply the change
Change-Id: I6958dbf661373606fb9dba55e386aaea4399a146
Merged-In: Iaf485537b8082d2109d2134ff987dc7244e31218
---
core/java/android/app/AppOpsManager.java | 12 ++++++------
.../java/com/android/server/appop/AppOpsService.java | 4 ++--
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c494781d1eeb8..7bfc4603f060b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -7623,8 +7623,8 @@ public class AppOpsManager {
} else if (collectionMode == COLLECT_SYNC
// Only collect app-ops when the proxy is trusted
&& (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
- myUid) == PackageManager.PERMISSION_GRANTED
- || isTrustedVoiceServiceProxy(mContext, mContext.getOpPackageName(), op))) {
+ myUid) == PackageManager.PERMISSION_GRANTED || isTrustedVoiceServiceProxy(
+ mContext, mContext.getOpPackageName(), op, mContext.getUserId()))) {
collectNotedOpSync(op, proxiedAttributionTag);
}
}
@@ -7642,7 +7642,7 @@ public class AppOpsManager {
* @hide
*/
public static boolean isTrustedVoiceServiceProxy(Context context, String packageName,
- int code) {
+ int code, int userId) {
// This is a workaround for R QPR, new API change is not allowed. We only allow the current
// voice recognizer is also the voice interactor to noteproxy op.
if (code != OP_RECORD_AUDIO) {
@@ -7654,7 +7654,7 @@ public class AppOpsManager {
final String voiceRecognitionServicePackageName =
getComponentPackageNameFromString(voiceRecognitionComponent);
return (Objects.equals(packageName, voiceRecognitionServicePackageName))
- && isPackagePreInstalled(context, packageName);
+ && isPackagePreInstalled(context, packageName, userId);
}
private static String getComponentPackageNameFromString(String from) {
@@ -7662,10 +7662,10 @@ public class AppOpsManager {
return componentName != null ? componentName.getPackageName() : "";
}
- private static boolean isPackagePreInstalled(Context context, String packageName) {
+ private static boolean isPackagePreInstalled(Context context, String packageName, int userId) {
try {
final PackageManager pm = context.getPackageManager();
- final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+ final ApplicationInfo info = pm.getApplicationInfoAsUser(packageName, 0, userId);
return ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
} catch (PackageManager.NameNotFoundException e) {
return false;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 4e194e2e495aa..902b2e2228f88 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3014,8 +3014,8 @@ public class AppOpsService extends IAppOpsService.Stub {
// This is a workaround for R QPR, new API change is not allowed. We only allow the current
// voice recognizer is also the voice interactor to noteproxy op.
- final boolean isTrustVoiceServiceProxy =
- AppOpsManager.isTrustedVoiceServiceProxy(mContext, proxyPackageName, code);
+ final boolean isTrustVoiceServiceProxy = AppOpsManager.isTrustedVoiceServiceProxy(mContext,
+ proxyPackageName, code, UserHandle.getUserId(proxyUid));
final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
final boolean isProxyTrusted = mContext.checkPermission(
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
From c45278479f5c84b84606a6040d2a8ad03f57e0f7 Mon Sep 17 00:00:00 2001
From: Heemin Seog
Date: Tue, 12 Jan 2021 15:35:13 -0800
Subject: [PATCH 056/288] DO NOT MERGE Remove floating cards launcher
Bug: 177364704
Test: none
Change-Id: I8f19d7d58ee78b64d93de265739a7a880b6a52df
Merged-In: I8f19d7d58ee78b64d93de265739a7a880b6a52df
---
data/etc/car/Android.bp | 7 ------
.../com.android.car.floatingcardslauncher.xml | 25 -------------------
2 files changed, 32 deletions(-)
delete mode 100644 data/etc/car/com.android.car.floatingcardslauncher.xml
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 3c990abaec871..d6df64f3295db 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -150,13 +150,6 @@ prebuilt_etc {
system_ext_specific: true,
}
-prebuilt_etc {
- name: "privapp_whitelist_com.android.car.floatingcardslauncher",
- sub_dir: "permissions",
- src: "com.android.car.floatingcardslauncher.xml",
- filename_from_src: true,
-}
-
prebuilt_etc {
name: "privapp_whitelist_com.android.car.ui.paintbooth",
sub_dir: "permissions",
diff --git a/data/etc/car/com.android.car.floatingcardslauncher.xml b/data/etc/car/com.android.car.floatingcardslauncher.xml
deleted file mode 100644
index 2755fee4eb557..0000000000000
--- a/data/etc/car/com.android.car.floatingcardslauncher.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
From c0f0e1b235828c88ab945ea159d32352ff613c58 Mon Sep 17 00:00:00 2001
From: Riddle Hsu
Date: Thu, 14 Jan 2021 18:09:32 +0800
Subject: [PATCH 057/288] Do not abort launch logger by invisible drawn
activity
For a special complex case: A no-display trampoline activity
launches main activity in the same task (no starting window),
and then the main activity launches trampoline and main again
consecutively. If the drawn event of main is reported before
the 2nd launch of main, the launch event log will be canceled
by seeing the drawn state before transition.
Because there will be a transition to commit window visibility,
the case can be addressed by allowing invisible drawn activity
as a valid launch event. Then the launch time can be reported
when notifying transition starting.
Bug: 176687862
Test: ActivityMetricsLaunchObserverTests#testActivityDrawnBeforeTransition
Change-Id: I81ec1695038216490537bdac6defcd9e6d75e1f8
Merged-In: I81ec1695038216490537bdac6defcd9e6d75e1f8
(cherry picked from commit dc21a33a80fedffe332e1347bde8c1933dd4f4c2)
---
.../android/server/wm/ActivityMetricsLogger.java | 4 ++--
.../wm/ActivityMetricsLaunchObserverTests.java | 16 ++++++++++++++++
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 8f59eef495167..eec2e41eb5db5 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -264,7 +264,7 @@ class ActivityMetricsLogger {
return;
}
mLastLaunchedActivity = r;
- if (!r.noDisplay) {
+ if (!r.noDisplay && !r.mDrawn) {
if (DEBUG_METRICS) Slog.i(TAG, "Add pending draw " + r);
mPendingDrawActivities.add(r);
}
@@ -546,7 +546,7 @@ class ActivityMetricsLogger {
+ " processSwitch=" + processSwitch + " info=" + info);
}
- if (launchedActivity.mDrawn) {
+ if (launchedActivity.mDrawn && launchedActivity.isVisible()) {
// Launched activity is already visible. We cannot measure windows drawn delay.
abort(info, "launched activity already visible");
return;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 5b516a9de350d..53a6d3ff599fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -311,6 +311,22 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
verifyNoMoreInteractions(mLaunchObserver);
}
+ @Test
+ public void testActivityDrawnBeforeTransition() {
+ mTopActivity.setVisible(false);
+ notifyActivityLaunching(mTopActivity.intent);
+ // Assume the activity is launched the second time consecutively. The drawn event is from
+ // the first time (omitted in test) launch that is earlier than transition.
+ mTopActivity.mDrawn = true;
+ notifyWindowsDrawn(mTopActivity);
+ notifyActivityLaunched(START_SUCCESS, mTopActivity);
+ // If the launching activity was drawn when starting transition, the launch event should
+ // be reported successfully.
+ notifyTransitionStarting(mTopActivity);
+
+ verifyOnActivityLaunchFinished(mTopActivity);
+ }
+
@Test
public void testActivityRecordProtoIsNotTooBig() {
// The ActivityRecordProto must not be too big, otherwise converting it at runtime
From 2268cafb1c6dc6c3a3478366a68d9f6f7d3e830d Mon Sep 17 00:00:00 2001
From: Jordan Liu
Date: Mon, 11 Jan 2021 11:54:18 -0800
Subject: [PATCH 058/288] Allow CBR when device owners is set up
Bug: 169758206
Test: manual
Change-Id: I98ac9eecd8a497541d9ce0761920db1252eb72e0
Merged-In: I98ac9eecd8a497541d9ce0761920db1252eb72e0
---
core/res/res/values/required_apps_managed_device.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/core/res/res/values/required_apps_managed_device.xml b/core/res/res/values/required_apps_managed_device.xml
index 40db9dfbcd2c6..4c5cd6861601e 100644
--- a/core/res/res/values/required_apps_managed_device.xml
+++ b/core/res/res/values/required_apps_managed_device.xml
@@ -27,5 +27,6 @@
- com.android.providers.downloads
- com.android.providers.downloads.ui
- com.android.documentsui
+ - com.android.cellbroadcastreceiver
From 5d0e3a340448c350f6919378787f912adf039673 Mon Sep 17 00:00:00 2001
From: Jack Yu
Date: Fri, 15 Jan 2021 10:19:38 -0800
Subject: [PATCH 059/288] Temporary fix to prevent TEMPORARILY_NOT_METERED leak
Currently we are seeing TEMPORARILY_NOT_METERED occurs on non-5G
devices. This fix adds more checks to prevent incorrect
TEMPORARILY_NOT_METERED being set. Before we root cause the issue,
we only allow this bit when all of the following conditions met.
1. Devices should support 5G.
2. Carriers should support 5G unmetered data.
3. Device should camp on 5G network.
This temporary fix should be removed after we root cause the issue.
Bug: 176119724
Test: Manual
Merged-In: I2b987d24851cdb16d8457b0ec5c546ace5485ed0
Change-Id: I2b987d24851cdb16d8457b0ec5c546ace5485ed0
---
.../java/android/telephony/CarrierConfigManager.java | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 757939df0134b..6f10af1940a2c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3928,6 +3928,13 @@ public class CarrierConfigManager {
public static final String KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED =
"use_lower_mtu_value_if_both_received";
+ /**
+ * Indicates temporarily unmetered mobile data is supported by the carrier.
+ * @hide
+ */
+ public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL =
+ "network_temp_not_metered_supported_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -4466,6 +4473,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
+ sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, false);
}
/**
From 53251a491faab1fb88e28b7cafe5913842b8d71d Mon Sep 17 00:00:00 2001
From: Muhammad Qureshi
Date: Fri, 15 Jan 2021 12:58:50 -0800
Subject: [PATCH 060/288] [RESTRICT AUTOMERGE] Fix potential out of bounds
writes in LogEvent.
mUidFieldIndex, mExclusiveStateFieldIndex,
mAttributionChain{Start|End}Index are stored using int8_t.
These values can overflow when mValues.size() exceeds INT8_MAX.
Add proper bounds checking to ensure these values don't exceed
INT8_MAX.
Bug: 174488848
Bug: 174485572
Test: m
Test: statsd_test
Test: TreeHugger
Change-Id: I416e2de06ca2935017e15d4e91000652831e6b6c
---
cmds/statsd/src/logd/LogEvent.cpp | 27 ++++++-
cmds/statsd/tests/LogEvent_test.cpp | 110 ++++++++++++++++++++++++++++
2 files changed, 133 insertions(+), 4 deletions(-)
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index f56fa6221bc99..4f031724763f9 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
#include "annotations.h"
@@ -216,13 +217,18 @@ void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
last[2] = true;
parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
}
- // Check if at least one node was successfully parsed.
- if (mValues.size() - 1 > firstUidInChainIndex) {
+
+ if (mValues.size() - 1 > INT8_MAX) {
+ mValid = false;
+ } else if (mValues.size() - 1 > firstUidInChainIndex) {
+ // At least one node was successfully parsed.
mAttributionChainStartIndex = static_cast(firstUidInChainIndex);
mAttributionChainEndIndex = static_cast(mValues.size() - 1);
}
- parseAnnotations(numAnnotations, firstUidInChainIndex);
+ if (mValid) {
+ parseAnnotations(numAnnotations, firstUidInChainIndex);
+ }
pos[1] = pos[2] = 1;
last[1] = last[2] = false;
@@ -234,7 +240,8 @@ bool LogEvent::checkPreviousValueType(Type expected) {
}
void LogEvent::parseIsUidAnnotation(uint8_t annotationType) {
- if (mValues.empty() || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) {
+ if (mValues.empty() || mValues.size() - 1 > INT8_MAX || !checkPreviousValueType(INT)
+ || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
@@ -270,6 +277,12 @@ void LogEvent::parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType,
return;
}
+ if (static_cast(mValues.size() - 1) < firstUidInChainIndex) { // AttributionChain is empty.
+ mValid = false;
+ android_errorWriteLog(0x534e4554, "174485572");
+ return;
+ }
+
const bool primaryField = readNextValue();
mValues[firstUidInChainIndex].mAnnotations.setPrimaryField(primaryField);
}
@@ -280,6 +293,12 @@ void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) {
return;
}
+ if (mValues.size() - 1 > INT8_MAX) {
+ android_errorWriteLog(0x534e4554, "174488848");
+ mValid = false;
+ return;
+ }
+
const bool exclusiveState = readNextValue();
mExclusiveStateFieldIndex = static_cast(mValues.size() - 1);
mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState);
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 5c170c07eb7d9..aed25475da113 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -363,6 +363,116 @@ TEST(LogEventTest, TestResetStateAnnotation) {
EXPECT_EQ(event.getResetState(), resetState);
}
+TEST(LogEventTest, TestExclusiveStateAnnotationAfterTooManyFields) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+
+ const unsigned int numAttributionNodes = 64;
+
+ uint32_t uids[numAttributionNodes];
+ const char* tags[numAttributionNodes];
+
+ for (unsigned int i = 1; i <= numAttributionNodes; i++) {
+ uids[i-1] = i;
+ tags[i-1] = std::to_string(i).c_str();
+ }
+
+ AStatsEvent_writeAttributionChain(event, uids, tags, numAttributionNodes);
+ AStatsEvent_writeInt32(event, 1);
+ AStatsEvent_addBoolAnnotation(event, ANNOTATION_ID_EXCLUSIVE_STATE, true);
+
+ AStatsEvent_build(event);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_FALSE(logEvent.parseBuffer(buf, size));
+ EXPECT_EQ(-1, logEvent.getExclusiveStateFieldIndex());
+
+ AStatsEvent_release(event);
+}
+
+TEST(LogEventTest, TestUidAnnotationAfterTooManyFields) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+
+ const unsigned int numAttributionNodes = 64;
+
+ uint32_t uids[numAttributionNodes];
+ const char* tags[numAttributionNodes];
+
+ for (unsigned int i = 1; i <= numAttributionNodes; i++) {
+ uids[i-1] = i;
+ tags[i-1] = std::to_string(i).c_str();
+ }
+
+ AStatsEvent_writeAttributionChain(event, uids, tags, numAttributionNodes);
+ AStatsEvent_writeInt32(event, 1);
+ AStatsEvent_addBoolAnnotation(event, ANNOTATION_ID_IS_UID, true);
+
+ AStatsEvent_build(event);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_FALSE(logEvent.parseBuffer(buf, size));
+ EXPECT_EQ(-1, logEvent.getUidFieldIndex());
+
+ AStatsEvent_release(event);
+}
+
+TEST(LogEventTest, TestAttributionChainEndIndexAfterTooManyFields) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+
+ const unsigned int numAttributionNodes = 65;
+
+ uint32_t uids[numAttributionNodes];
+ const char* tags[numAttributionNodes];
+
+ for (unsigned int i = 1; i <= numAttributionNodes; i++) {
+ uids[i-1] = i;
+ tags[i-1] = std::to_string(i).c_str();
+ }
+
+ AStatsEvent_writeAttributionChain(event, uids, tags, numAttributionNodes);
+
+ AStatsEvent_build(event);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_FALSE(logEvent.parseBuffer(buf, size));
+ EXPECT_FALSE(logEvent.hasAttributionChain());
+
+ AStatsEvent_release(event);
+}
+
+TEST(LogEventTest, TestEmptyAttributionChainWithPrimaryFieldFirstUidAnnotation) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+
+ uint32_t uids[] = {};
+ const char* tags[] = {};
+
+ AStatsEvent_writeInt32(event, 10);
+ AStatsEvent_writeAttributionChain(event, uids, tags, 0);
+ AStatsEvent_addBoolAnnotation(event, ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
+
+ AStatsEvent_build(event);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_FALSE(logEvent.parseBuffer(buf, size));
+
+ AStatsEvent_release(event);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
From e8212659b95b8a68ce32a82d8548d88aaeb4608a Mon Sep 17 00:00:00 2001
From: Yu-Han Yang
Date: Fri, 15 Jan 2021 14:24:09 -0800
Subject: [PATCH 061/288] DO NOT MERGE: Do not inject mock location to chipset
Bug: 177561690
Test: on device
Change-Id: Icafbdf54fe807f8779377b13cb4e4eb265db692e
---
.../android/server/location/gnss/GnssLocationProvider.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index a6a607e4ce66f..8d1d3afab5c5f 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -841,6 +841,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
private void injectBestLocation(Location location) {
+ if (location.isFromMockProvider()) {
+ return;
+ }
if (DEBUG) {
Log.d(TAG, "injectBestLocation: " + location);
}
@@ -942,6 +945,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
private void injectLocation(Location location) {
+ if (location.isFromMockProvider()) {
+ return;
+ }
if (location.hasAccuracy()) {
if (DEBUG) {
Log.d(TAG, "injectLocation: " + location);
From c59cbd13f62586bf1eaeb75767859ed08f3adeb1 Mon Sep 17 00:00:00 2001
From: Yu-Han Yang
Date: Fri, 15 Jan 2021 15:23:52 -0800
Subject: [PATCH 062/288] DO NOT MERGE: Do not inject mock location to chipset
Bug: 177561690
Test: on device
Change-Id: Icafbdf54fe807f8779377b13cb4e4eb265db692e
---
.../com/android/server/location/GnssLocationProvider.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index c312b76cc4634..184e53deb69bc 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -772,6 +772,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
private void injectBestLocation(Location location) {
+ if (location.isFromMockProvider()) {
+ return;
+ }
if (DEBUG) {
Log.d(TAG, "injectBestLocation: " + location);
}
@@ -878,6 +881,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
private void handleUpdateLocation(Location location) {
+ if (location.isFromMockProvider()) {
+ return;
+ }
if (location.hasAccuracy()) {
if (DEBUG) {
Log.d(TAG, "injectLocation: " + location);
From 20a39b541c6045837662a569e075eb0176bc16c2 Mon Sep 17 00:00:00 2001
From: jimsun
Date: Fri, 15 Jan 2021 15:10:59 +0800
Subject: [PATCH 063/288] Add a new reason code for IMS call failed due to
network congestion
Bug: 175370724
Test: Simulate call end reason from vendor code and check IMS call
end reason is correctly mapped.
Change-Id: Iea0a5115548530350c5a2645cdf5487870943f72
---
telephony/java/android/telephony/ims/ImsReasonInfo.java | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 0d6b31d349e11..4cace2fecdf70 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -889,6 +889,13 @@ public final class ImsReasonInfo implements Parcelable {
*/
public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
+ /**
+ * Call failed because of network congestion, resource is not available,
+ * or no circuit or channel available, etc.
+ * @hide
+ */
+ public static final int CODE_NETWORK_CONGESTION = 1624;
+
/**
* The dialed RTT call should be retried without RTT
* @hide
@@ -1075,6 +1082,7 @@ public final class ImsReasonInfo implements Parcelable {
CODE_REJECT_VT_AVPF_NOT_ALLOWED,
CODE_REJECT_ONGOING_ENCRYPTED_CALL,
CODE_REJECT_ONGOING_CS_CALL,
+ CODE_NETWORK_CONGESTION,
CODE_RETRY_ON_IMS_WITHOUT_RTT,
CODE_OEM_CAUSE_1,
CODE_OEM_CAUSE_2,
From 0ecd3e13e7aa3dfbcd96564b9c3444152e3a1532 Mon Sep 17 00:00:00 2001
From: raylinhsu
Date: Fri, 15 Jan 2021 02:43:59 +0800
Subject: [PATCH 064/288] DO NOT MERGE "Set fixed fps when ambient or display
brightness is high"
Force the display to stay at fixed fps when ambient and
display brightness are high.
Bug: 177636374
Test: atest DisplayModeDirectorTest
Test: no fps transition in the blocking zone when the display state change
Test: check if the lux will update when the display state change
Change-Id: I7d8ed657544bd1423ca8b9afefc87e58530c44dc
---
core/res/res/values/config.xml | 29 +
core/res/res/values/symbols.xml | 5 +
services/core/Android.bp | 7 +
.../server/display/DisplayModeDirector.java | 577 +++++++++++++-----
.../{wm => }/utils/DeviceConfigInterface.java | 13 +-
.../server/wm/HighRefreshRateBlacklist.java | 2 +-
.../server/wm/WindowManagerConstants.java | 2 +-
.../server/wm/WindowManagerService.java | 2 +-
services/tests/servicestests/Android.bp | 3 +-
.../display/DisplayModeDirectorTest.java | 382 +++++++++++-
.../testutils}/FakeDeviceConfigInterface.java | 16 +-
.../wm/HighRefreshRateBlacklistTest.java | 2 +-
.../server/wm/WindowManagerConstantsTest.java | 2 +-
13 files changed, 869 insertions(+), 173 deletions(-)
rename services/core/java/com/android/server/{wm => }/utils/DeviceConfigInterface.java (90%)
rename services/tests/{wmtests/src/com/android/server/wm/utils => servicestests/utils/com/android/server/testutils}/FakeDeviceConfigInterface.java (92%)
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 550601af0faa1..5f02eb6a6a37e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4138,6 +4138,35 @@
If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
0
+
+
+
+
+
+
+
+
+
+
+ 0
+
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 06f357e79a621..b80e8e1cd9805 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3783,6 +3783,11 @@
+
+
+
+
+
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4bba0d892f3b2..7585d6ba9c60f 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -1,3 +1,10 @@
+filegroup {
+ name: "services.core-sources-deviceconfig-interface",
+ srcs: [
+ "java/com/android/server/utils/DeviceConfigInterface.java"
+ ],
+}
+
filegroup {
name: "services.core-sources",
srcs: ["java/**/*.java"],
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 3c050804f01d9..2a60839ab702a 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -46,8 +46,10 @@ import android.view.DisplayInfo;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
+import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,9 +66,9 @@ public class DisplayModeDirector {
private static final boolean DEBUG = false;
private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
- private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
+ private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
- private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4;
+ private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
// Special ID used to indicate that given vote is to be applied globally, rather than to a
// specific display.
@@ -79,6 +81,13 @@ public class DisplayModeDirector {
private final Context mContext;
private final DisplayModeDirectorHandler mHandler;
+ private final Injector mInjector;
+
+ private final AppRequestObserver mAppRequestObserver;
+ private final SettingsObserver mSettingsObserver;
+ private final DisplayObserver mDisplayObserver;
+ private final DeviceConfigInterface mDeviceConfig;
+ private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
// A map from the display ID to the collection of votes and their priority. The latter takes
// the form of another map from the priority to the vote itself so that each priority is
@@ -89,17 +98,19 @@ public class DisplayModeDirector {
// A map from the display ID to the default mode of that display.
private SparseArray mDefaultModeByDisplay;
- private final AppRequestObserver mAppRequestObserver;
- private final SettingsObserver mSettingsObserver;
- private final DisplayObserver mDisplayObserver;
private BrightnessObserver mBrightnessObserver;
- private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
+ this(context, handler, new RealInjector());
+ }
+
+ public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
+ @NonNull Injector injector) {
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
+ mInjector = injector;
mVotesByDisplay = new SparseArray<>();
mSupportedModesByDisplay = new SparseArray<>();
mDefaultModeByDisplay = new SparseArray<>();
@@ -108,6 +119,7 @@ public class DisplayModeDirector {
mDisplayObserver = new DisplayObserver(context, handler);
mBrightnessObserver = new BrightnessObserver(context, handler);
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
+ mDeviceConfig = injector.getDeviceConfig();
}
/**
@@ -348,6 +360,23 @@ public class DisplayModeDirector {
}
}
+ /**
+ * Retrieve the Vote for the given display and priority. Intended only for testing purposes.
+ *
+ * @param displayId the display to query for
+ * @param priority the priority of the vote to return
+ * @return the vote corresponding to the given {@code displayId} and {@code priority},
+ * or {@code null} if there isn't one
+ */
+ @VisibleForTesting
+ @Nullable
+ Vote getVote(int displayId, int priority) {
+ synchronized (mLock) {
+ SparseArray votes = getVotesLocked(displayId);
+ return votes.get(priority);
+ }
+ }
+
/**
* Print the object's state and debug information into the given stream.
*
@@ -465,6 +494,17 @@ public class DisplayModeDirector {
mBrightnessObserver = brightnessObserver;
}
+ @VisibleForTesting
+ BrightnessObserver getBrightnessObserver() {
+ return mBrightnessObserver;
+ }
+
+ @VisibleForTesting
+ SettingsObserver getSettingsObserver() {
+ return mSettingsObserver;
+ }
+
+
@VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
@@ -475,6 +515,13 @@ public class DisplayModeDirector {
}
}
+ @VisibleForTesting
+ void updateSettingForHighZone(int refreshRate, int[] brightnessThresholds,
+ int[] ambientThresholds) {
+ mBrightnessObserver.updateThresholdsRefreshRateForHighZone(refreshRate,
+ brightnessThresholds, ambientThresholds);
+ }
+
/**
* Listens for changes refresh rate coordination.
*/
@@ -493,15 +540,10 @@ public class DisplayModeDirector {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
+ case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED:
Pair thresholds = (Pair) msg.obj;
-
- if (thresholds != null) {
- mBrightnessObserver.onDeviceConfigThresholdsChanged(
- thresholds.first, thresholds.second);
- } else {
- mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null);
- }
+ mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
+ thresholds.first, thresholds.second);
break;
case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
@@ -510,9 +552,9 @@ public class DisplayModeDirector {
defaultPeakRefreshRate);
break;
- case MSG_REFRESH_RATE_IN_ZONE_CHANGED:
+ case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED:
int refreshRateInZone = msg.arg1;
- mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged(
+ mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
refreshRateInZone);
break;
@@ -685,10 +727,11 @@ public class DisplayModeDirector {
// by all other considerations. It acts to set a default frame rate for a device.
public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
- // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
+ // FLICKER votes for a single refresh rate like [60,60], [90,90] or null.
// If the higher voters result is a range, it will fix the rate to a single choice.
- // It's used to avoid rate switch in certain conditions.
- public static final int PRIORITY_LOW_BRIGHTNESS = 1;
+ // It's used to avoid refresh rate switches in certain conditions which may result in the
+ // user seeing the display flickering when the switches occur.
+ public static final int PRIORITY_FLICKER = 1;
// SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
// It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
@@ -761,8 +804,8 @@ public class DisplayModeDirector {
switch (priority) {
case PRIORITY_DEFAULT_REFRESH_RATE:
return "PRIORITY_DEFAULT_REFRESH_RATE";
- case PRIORITY_LOW_BRIGHTNESS:
- return "PRIORITY_LOW_BRIGHTNESS";
+ case PRIORITY_FLICKER:
+ return "PRIORITY_FLICKER";
case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
case PRIORITY_APP_REQUEST_REFRESH_RATE:
@@ -787,7 +830,8 @@ public class DisplayModeDirector {
}
}
- private final class SettingsObserver extends ContentObserver {
+ @VisibleForTesting
+ final class SettingsObserver extends ContentObserver {
private final Uri mPeakRefreshRateSetting =
Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
private final Uri mMinRefreshRateSetting =
@@ -810,8 +854,7 @@ public class DisplayModeDirector {
public void observe() {
final ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this,
- UserHandle.USER_SYSTEM);
+ mInjector.registerPeakRefreshRateObserver(cr, this);
cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
UserHandle.USER_SYSTEM);
cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
@@ -829,6 +872,13 @@ public class DisplayModeDirector {
}
}
+ public void setDefaultRefreshRate(float refreshRate) {
+ synchronized (mLock) {
+ mDefaultRefreshRate = refreshRate;
+ updateRefreshRateSettingLocked();
+ }
+ }
+
public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
if (defaultPeakRefreshRate == null) {
defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
@@ -1033,6 +1083,7 @@ public class DisplayModeDirector {
@Override
public void onDisplayChanged(int displayId) {
updateDisplayModes(displayId);
+ // TODO: Break the coupling between DisplayObserver and BrightnessObserver.
mBrightnessObserver.onDisplayChanged(displayId);
}
@@ -1071,15 +1122,16 @@ public class DisplayModeDirector {
*/
@VisibleForTesting
public class BrightnessObserver extends ContentObserver {
- // TODO: brightnessfloat: change this to the float setting
- private final Uri mDisplayBrightnessSetting =
- Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
private final static int LIGHT_SENSOR_RATE_MS = 250;
- private int[] mDisplayBrightnessThresholds;
- private int[] mAmbientBrightnessThresholds;
+ private int[] mLowDisplayBrightnessThresholds;
+ private int[] mLowAmbientBrightnessThresholds;
+ private int[] mHighDisplayBrightnessThresholds;
+ private int[] mHighAmbientBrightnessThresholds;
// valid threshold if any item from the array >= 0
- private boolean mShouldObserveDisplayChange;
- private boolean mShouldObserveAmbientChange;
+ private boolean mShouldObserveDisplayLowChange;
+ private boolean mShouldObserveAmbientLowChange;
+ private boolean mShouldObserveDisplayHighChange;
+ private boolean mShouldObserveAmbientHighChange;
private SensorManager mSensorManager;
private Sensor mLightSensor;
@@ -1087,46 +1139,114 @@ public class DisplayModeDirector {
// Take it as low brightness before valid sensor data comes
private float mAmbientLux = -1.0f;
private AmbientFilter mAmbientFilter;
+ private int mBrightness = -1;
private final Context mContext;
- // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
- // refresh rate changeable and low power mode off. After initialization, these states will
+ // Enable light sensor only when mShouldObserveAmbientLowChange is true or
+ // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
+ // changeable and low power mode off. After initialization, these states will
// be updated from the same handler thread.
- private boolean mScreenOn = false;
+ private boolean mDefaultDisplayOn = false;
private boolean mRefreshRateChangeable = false;
private boolean mLowPowerModeEnabled = false;
- private int mRefreshRateInZone;
+ private int mRefreshRateInLowZone;
+ private int mRefreshRateInHighZone;
BrightnessObserver(Context context, Handler handler) {
super(handler);
mContext = context;
- mDisplayBrightnessThresholds = context.getResources().getIntArray(
+ mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
R.array.config_brightnessThresholdsOfPeakRefreshRate);
- mAmbientBrightnessThresholds = context.getResources().getIntArray(
+ mLowAmbientBrightnessThresholds = context.getResources().getIntArray(
R.array.config_ambientThresholdsOfPeakRefreshRate);
- if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) {
- throw new RuntimeException("display brightness threshold array and ambient "
- + "brightness threshold array have different length");
+ if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
+ throw new RuntimeException("display low brightness threshold array and ambient "
+ + "brightness threshold array have different length: "
+ + "displayBrightnessThresholds="
+ + Arrays.toString(mLowDisplayBrightnessThresholds)
+ + ", ambientBrightnessThresholds="
+ + Arrays.toString(mLowAmbientBrightnessThresholds));
}
+
+ mHighDisplayBrightnessThresholds = context.getResources().getIntArray(
+ R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
+ mHighAmbientBrightnessThresholds = context.getResources().getIntArray(
+ R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
+ if (mHighDisplayBrightnessThresholds.length
+ != mHighAmbientBrightnessThresholds.length) {
+ throw new RuntimeException("display high brightness threshold array and ambient "
+ + "brightness threshold array have different length: "
+ + "displayBrightnessThresholds="
+ + Arrays.toString(mHighDisplayBrightnessThresholds)
+ + ", ambientBrightnessThresholds="
+ + Arrays.toString(mHighAmbientBrightnessThresholds));
+ }
+ mRefreshRateInHighZone = context.getResources().getInteger(
+ R.integer.config_fixedRefreshRateInHighZone);
+ }
+
+ /**
+ * @return the refresh to lock to when in a low brightness zone
+ */
+ @VisibleForTesting
+ int getRefreshRateInLowZone() {
+ return mRefreshRateInLowZone;
+ }
+
+ /**
+ * @return the display brightness thresholds for the low brightness zones
+ */
+ @VisibleForTesting
+ int[] getLowDisplayBrightnessThresholds() {
+ return mLowDisplayBrightnessThresholds;
+ }
+
+ /**
+ * @return the ambient brightness thresholds for the low brightness zones
+ */
+ @VisibleForTesting
+ int[] getLowAmbientBrightnessThresholds() {
+ return mLowAmbientBrightnessThresholds;
+ }
+
+ public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) {
+ mSensorManager = sensorManager;
+ mLightSensor = lightSensor;
+
+ mSensorManager.registerListener(mLightSensorListener,
+ mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
+ }
+
+ public void updateThresholdsRefreshRateForHighZone(int refreshRate,
+ int[] brightnessThresholds, int[] ambientThresholds) {
+ mRefreshRateInHighZone = refreshRate;
+ mHighDisplayBrightnessThresholds = brightnessThresholds;
+ mHighAmbientBrightnessThresholds = ambientThresholds;
}
public void observe(SensorManager sensorManager) {
mSensorManager = sensorManager;
+ final ContentResolver cr = mContext.getContentResolver();
+ mBrightness = Settings.System.getIntForUser(cr,
+ Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
// DeviceConfig is accessible after system ready.
- int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
- int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
+ int[] lowDisplayBrightnessThresholds =
+ mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
+ int[] lowAmbientBrightnessThresholds =
+ mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
- if (brightnessThresholds != null && ambientThresholds != null
- && brightnessThresholds.length == ambientThresholds.length) {
- mDisplayBrightnessThresholds = brightnessThresholds;
- mAmbientBrightnessThresholds = ambientThresholds;
+ if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
+ && lowDisplayBrightnessThresholds.length
+ == lowAmbientBrightnessThresholds.length) {
+ mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds;
+ mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
}
- mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone();
+ mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
restartObserver();
mDeviceConfigDisplaySettings.startListening();
}
@@ -1138,7 +1258,7 @@ public class DisplayModeDirector {
updateSensorStatus();
if (!changeable) {
// Revoke previous vote from BrightnessObserver
- updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null);
+ updateVoteLocked(Vote.PRIORITY_FLICKER, null);
}
}
}
@@ -1150,25 +1270,25 @@ public class DisplayModeDirector {
}
}
- public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds,
+ public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
int[] ambientThresholds) {
- if (brightnessThresholds != null && ambientThresholds != null
- && brightnessThresholds.length == ambientThresholds.length) {
- mDisplayBrightnessThresholds = brightnessThresholds;
- mAmbientBrightnessThresholds = ambientThresholds;
+ if (displayThresholds != null && ambientThresholds != null
+ && displayThresholds.length == ambientThresholds.length) {
+ mLowDisplayBrightnessThresholds = displayThresholds;
+ mLowAmbientBrightnessThresholds = ambientThresholds;
} else {
// Invalid or empty. Use device default.
- mDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+ mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray(
R.array.config_brightnessThresholdsOfPeakRefreshRate);
- mAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+ mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray(
R.array.config_ambientThresholdsOfPeakRefreshRate);
}
restartObserver();
}
- public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) {
- if (refreshRate != mRefreshRateInZone) {
- mRefreshRateInZone = refreshRate;
+ public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) {
+ if (refreshRate != mRefreshRateInLowZone) {
+ mRefreshRateInLowZone = refreshRate;
restartObserver();
}
}
@@ -1176,48 +1296,95 @@ public class DisplayModeDirector {
public void dumpLocked(PrintWriter pw) {
pw.println(" BrightnessObserver");
pw.println(" mAmbientLux: " + mAmbientLux);
- pw.println(" mRefreshRateInZone: " + mRefreshRateInZone);
+ pw.println(" mBrightness: " + mBrightness);
+ pw.println(" mDefaultDisplayOn: " + mDefaultDisplayOn);
+ pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled);
+ pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable);
+ pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
+ pw.println(" mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange);
+ pw.println(" mRefreshRateInLowZone: " + mRefreshRateInLowZone);
- for (int d: mDisplayBrightnessThresholds) {
- pw.println(" mDisplayBrightnessThreshold: " + d);
+ for (int d : mLowDisplayBrightnessThresholds) {
+ pw.println(" mDisplayLowBrightnessThreshold: " + d);
}
- for (int d: mAmbientBrightnessThresholds) {
- pw.println(" mAmbientBrightnessThreshold: " + d);
+ for (int d : mLowAmbientBrightnessThresholds) {
+ pw.println(" mAmbientLowBrightnessThreshold: " + d);
+ }
+
+ pw.println(" mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange);
+ pw.println(" mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange);
+ pw.println(" mRefreshRateInHighZone: " + mRefreshRateInHighZone);
+
+ for (int d : mHighDisplayBrightnessThresholds) {
+ pw.println(" mDisplayHighBrightnessThresholds: " + d);
+ }
+
+ for (int d : mHighAmbientBrightnessThresholds) {
+ pw.println(" mAmbientHighBrightnessThresholds: " + d);
}
mLightSensorListener.dumpLocked(pw);
+
+ if (mAmbientFilter != null) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.setIndent(" ");
+ mAmbientFilter.dump(ipw);
+ }
}
public void onDisplayChanged(int displayId) {
if (displayId == Display.DEFAULT_DISPLAY) {
- onScreenOn(isDefaultDisplayOn());
+ updateDefaultDisplayState();
}
}
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
synchronized (mLock) {
- onBrightnessChangedLocked();
+ final ContentResolver cr = mContext.getContentResolver();
+ int brightness = Settings.System.getIntForUser(cr,
+ Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
+ if (brightness != mBrightness) {
+ mBrightness = brightness;
+ onBrightnessChangedLocked();
+ }
}
}
private void restartObserver() {
- mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
- mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
-
final ContentResolver cr = mContext.getContentResolver();
- if (mShouldObserveDisplayChange) {
- // Content Service does not check if an listener has already been registered.
- // To ensure only one listener is registered, force an unregistration first.
- cr.unregisterContentObserver(this);
- cr.registerContentObserver(mDisplayBrightnessSetting,
- false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
+
+ if (mRefreshRateInLowZone > 0) {
+ mShouldObserveDisplayLowChange = hasValidThreshold(
+ mLowDisplayBrightnessThresholds);
+ mShouldObserveAmbientLowChange = hasValidThreshold(
+ mLowAmbientBrightnessThresholds);
} else {
- cr.unregisterContentObserver(this);
+ mShouldObserveDisplayLowChange = false;
+ mShouldObserveAmbientLowChange = false;
}
- if (mShouldObserveAmbientChange) {
+ if (mRefreshRateInHighZone > 0) {
+ mShouldObserveDisplayHighChange = hasValidThreshold(
+ mHighDisplayBrightnessThresholds);
+ mShouldObserveAmbientHighChange = hasValidThreshold(
+ mHighAmbientBrightnessThresholds);
+ } else {
+ mShouldObserveDisplayHighChange = false;
+ mShouldObserveAmbientHighChange = false;
+ }
+
+ if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) {
+ // Content Service does not check if an listener has already been registered.
+ // To ensure only one listener is registered, force an unregistration first.
+ mInjector.unregisterBrightnessObserver(cr, this);
+ mInjector.registerBrightnessObserver(cr, this);
+ } else {
+ mInjector.unregisterBrightnessObserver(cr, this);
+ }
+
+ if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
Resources resources = mContext.getResources();
String lightSensorType = resources.getString(
com.android.internal.R.string.config_displayLightSensorType);
@@ -1243,8 +1410,6 @@ public class DisplayModeDirector {
mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
mLightSensor = lightSensor;
-
- onScreenOn(isDefaultDisplayOn());
}
} else {
mAmbientFilter = null;
@@ -1263,11 +1428,7 @@ public class DisplayModeDirector {
* Checks to see if at least one value is positive, in which case it is necessary to listen
* to value changes.
*/
- private boolean checkShouldObserve(int[] a) {
- if (mRefreshRateInZone <= 0) {
- return false;
- }
-
+ private boolean hasValidThreshold(int[] a) {
for (int d: a) {
if (d >= 0) {
return true;
@@ -1277,13 +1438,13 @@ public class DisplayModeDirector {
return false;
}
- private boolean isInsideZone(int brightness, float lux) {
- for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
- int disp = mDisplayBrightnessThresholds[i];
- int ambi = mAmbientBrightnessThresholds[i];
+ private boolean isInsideLowZone(int brightness, float lux) {
+ for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) {
+ int disp = mLowDisplayBrightnessThresholds[i];
+ int ambi = mLowAmbientBrightnessThresholds[i];
if (disp >= 0 && ambi >= 0) {
- if (brightness <= disp && mAmbientLux <= ambi) {
+ if (brightness <= disp && lux <= ambi) {
return true;
}
} else if (disp >= 0) {
@@ -1291,7 +1452,7 @@ public class DisplayModeDirector {
return true;
}
} else if (ambi >= 0) {
- if (mAmbientLux <= ambi) {
+ if (lux <= ambi) {
return true;
}
}
@@ -1299,27 +1460,77 @@ public class DisplayModeDirector {
return false;
}
- // TODO: brightnessfloat: make it use float not int
- private void onBrightnessChangedLocked() {
- int brightness = Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS, -1);
+ private boolean isInsideHighZone(int brightness, float lux) {
+ for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) {
+ int disp = mHighDisplayBrightnessThresholds[i];
+ int ambi = mHighAmbientBrightnessThresholds[i];
+
+ if (disp >= 0 && ambi >= 0) {
+ if (brightness >= disp && lux >= ambi) {
+ return true;
+ }
+ } else if (disp >= 0) {
+ if (brightness >= disp) {
+ return true;
+ }
+ } else if (ambi >= 0) {
+ if (lux >= ambi) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ private void onBrightnessChangedLocked() {
Vote vote = null;
- boolean insideZone = isInsideZone(brightness, mAmbientLux);
- if (insideZone) {
- vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone);
+
+ if (mBrightness < 0) {
+ // Either the setting isn't available or we shouldn't be observing yet anyways.
+ // Either way, just bail out since there's nothing we can do here.
+ return;
+ }
+
+ boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
+ if (insideLowZone) {
+ vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
+ }
+
+ boolean insideHighZone = hasValidHighZone()
+ && isInsideHighZone(mBrightness, mAmbientLux);
+ if (insideHighZone) {
+ vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
}
if (DEBUG) {
- Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux +
- ", Vote " + vote);
+ Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux
+ + ", Vote " + vote);
}
- updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
+ updateVoteLocked(Vote.PRIORITY_FLICKER, vote);
}
- private void onScreenOn(boolean on) {
- if (mScreenOn != on) {
- mScreenOn = on;
+ private boolean hasValidLowZone() {
+ return mRefreshRateInLowZone > 0
+ && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange);
+ }
+
+ private boolean hasValidHighZone() {
+ return mRefreshRateInHighZone > 0
+ && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange);
+ }
+
+ private void updateDefaultDisplayState() {
+ Display display = mContext.getSystemService(DisplayManager.class)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+ boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF;
+ setDefaultDisplayState(defaultDisplayOn);
+ }
+
+ @VisibleForTesting
+ public void setDefaultDisplayState(boolean on) {
+ if (mDefaultDisplayOn != on) {
+ mDefaultDisplayOn = on;
updateSensorStatus();
}
}
@@ -1329,8 +1540,8 @@ public class DisplayModeDirector {
return;
}
- if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
- && mRefreshRateChangeable) {
+ if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
+ && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
mSensorManager.registerListener(mLightSensorListener,
mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
} else {
@@ -1339,11 +1550,8 @@ public class DisplayModeDirector {
}
}
- private boolean isDefaultDisplayOn() {
- final Display display = mContext.getSystemService(DisplayManager.class)
- .getDisplay(Display.DEFAULT_DISPLAY);
- return display.getState() != Display.STATE_OFF
- && mContext.getSystemService(PowerManager.class).isInteractive();
+ private boolean isDeviceActive() {
+ return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext);
}
private final class LightSensorEventListener implements SensorEventListener {
@@ -1361,23 +1569,33 @@ public class DisplayModeDirector {
Slog.d(TAG, "On sensor changed: " + mLastSensorData);
}
- boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux);
- if (zoneChanged && mLastSensorData < mAmbientLux) {
- // Easier to see flicker at lower brightness environment. Forget the history to
- // get immediate response.
- mAmbientFilter.clear();
+ boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
+ mLowAmbientBrightnessThresholds);
+ boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
+ mHighAmbientBrightnessThresholds);
+ if ((lowZoneChanged && mLastSensorData < mAmbientLux)
+ || (highZoneChanged && mLastSensorData > mAmbientLux)) {
+ // Easier to see flicker at lower brightness environment or high brightness
+ // environment. Forget the history to get immediate response.
+ if (mAmbientFilter != null) {
+ mAmbientFilter.clear();
+ }
}
long now = SystemClock.uptimeMillis();
- mAmbientFilter.addValue(now, mLastSensorData);
+ if (mAmbientFilter != null) {
+ mAmbientFilter.addValue(now, mLastSensorData);
+ }
mHandler.removeCallbacks(mInjectSensorEventRunnable);
processSensorData(now);
- if (zoneChanged && mLastSensorData > mAmbientLux) {
+ if ((lowZoneChanged && mLastSensorData > mAmbientLux)
+ || (highZoneChanged && mLastSensorData < mAmbientLux)) {
// Sensor may not report new event if there is no brightness change.
// Need to keep querying the temporal filter for the latest estimation,
- // until enter in higher lux zone or is interrupted by a new sensor event.
+ // until sensor readout and filter estimation are in the same zone or
+ // is interrupted by a new sensor event.
mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
}
}
@@ -1392,17 +1610,19 @@ public class DisplayModeDirector {
}
private void processSensorData(long now) {
- mAmbientLux = mAmbientFilter.getEstimate(now);
+ if (mAmbientFilter != null) {
+ mAmbientLux = mAmbientFilter.getEstimate(now);
+ } else {
+ mAmbientLux = mLastSensorData;
+ }
synchronized (mLock) {
onBrightnessChangedLocked();
}
}
- private boolean isDifferentZone(float lux1, float lux2) {
- for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) {
- final float boundary = mAmbientBrightnessThresholds[z];
-
+ private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) {
+ for (final float boundary : luxThresholds) {
// Test each boundary. See if the current value and the new value are at
// different sides.
if ((lux1 <= boundary && lux2 > boundary)
@@ -1422,7 +1642,10 @@ public class DisplayModeDirector {
processSensorData(now);
// Inject next event if there is a possible zone change.
- if (isDifferentZone(mLastSensorData, mAmbientLux)) {
+ if (isDifferentZone(mLastSensorData, mAmbientLux,
+ mLowAmbientBrightnessThresholds)
+ || isDifferentZone(mLastSensorData, mAmbientLux,
+ mHighAmbientBrightnessThresholds)) {
mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
}
}
@@ -1435,14 +1658,14 @@ public class DisplayModeDirector {
}
public void startListening() {
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
BackgroundThread.getExecutor(), this);
}
/*
* Return null if no such property or wrong format (not comma separated integers).
*/
- public int[] getBrightnessThresholds() {
+ public int[] getLowDisplayBrightnessThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig.
KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
@@ -1451,17 +1674,29 @@ public class DisplayModeDirector {
/*
* Return null if no such property or wrong format (not comma separated integers).
*/
- public int[] getAmbientThresholds() {
+ public int[] getLowAmbientBrightnessThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig.
KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
}
+ public int getRefreshRateInLowZone() {
+ int defaultRefreshRateInZone = mContext.getResources().getInteger(
+ R.integer.config_defaultRefreshRateInZone);
+
+ int refreshRate = mDeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
+ defaultRefreshRateInZone);
+
+ return refreshRate;
+ }
+
/*
* Return null if no such property
*/
public Float getDefaultPeakRefreshRate() {
- float defaultPeakRefreshRate = DeviceConfig.getFloat(
+ float defaultPeakRefreshRate = mDeviceConfig.getFloat(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
@@ -1471,36 +1706,25 @@ public class DisplayModeDirector {
return defaultPeakRefreshRate;
}
- public int getRefreshRateInZone() {
- int defaultRefreshRateInZone = mContext.getResources().getInteger(
- R.integer.config_defaultRefreshRateInZone);
-
- int refreshRate = DeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
- defaultRefreshRateInZone);
-
- return refreshRate;
- }
-
@Override
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
- int[] brightnessThresholds = getBrightnessThresholds();
- int[] ambientThresholds = getAmbientThresholds();
Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
- int refreshRateInZone = getRefreshRateInZone();
-
- mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
- new Pair(brightnessThresholds, ambientThresholds))
- .sendToTarget();
mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
defaultPeakRefreshRate).sendToTarget();
- mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone,
- 0).sendToTarget();
+
+ int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
+ int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
+ int refreshRateInLowZone = getRefreshRateInLowZone();
+
+ mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
+ new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
+ .sendToTarget();
+ mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
+ .sendToTarget();
}
private int[] getIntArrayProperty(String prop) {
- String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
+ String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
null);
if (strArray != null) {
@@ -1527,4 +1751,59 @@ public class DisplayModeDirector {
}
}
+ interface Injector {
+ // TODO: brightnessfloat: change this to the float setting
+ Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+ Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
+
+ @NonNull
+ DeviceConfigInterface getDeviceConfig();
+
+ void registerBrightnessObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer);
+
+ void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer);
+
+ void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer);
+
+ boolean isDeviceInteractive(@NonNull Context context);
+ }
+
+ @VisibleForTesting
+ static class RealInjector implements Injector {
+
+ @Override
+ @NonNull
+ public DeviceConfigInterface getDeviceConfig() {
+ return DeviceConfigInterface.REAL;
+ }
+
+ @Override
+ public void registerBrightnessObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/,
+ observer, UserHandle.USER_SYSTEM);
+ }
+
+ @Override
+ public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ cr.unregisterContentObserver(observer);
+ }
+
+ @Override
+ public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
+ observer, UserHandle.USER_SYSTEM);
+ }
+
+ @Override
+ public boolean isDeviceInteractive(@NonNull Context ctx) {
+ return ctx.getSystemService(PowerManager.class).isInteractive();
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java b/services/core/java/com/android/server/utils/DeviceConfigInterface.java
similarity index 90%
rename from services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java
rename to services/core/java/com/android/server/utils/DeviceConfigInterface.java
index ab7e7f63cafd4..ff609031b57c5 100644
--- a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java
+++ b/services/core/java/com/android/server/utils/DeviceConfigInterface.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.utils;
+package com.android.server.utils;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -53,6 +53,11 @@ public interface DeviceConfigInterface {
*/
boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue);
+ /**
+ * @see DeviceConfig#getFloat
+ */
+ float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue);
+
/**
* @see DeviceConfig#addOnPropertiesChangedListener
*/
@@ -95,6 +100,12 @@ public interface DeviceConfigInterface {
return DeviceConfig.getBoolean(namespace, name, defaultValue);
}
+ @Override
+ public float getFloat(@NonNull String namespace, @NonNull String name,
+ float defaultValue) {
+ return DeviceConfig.getFloat(namespace, name, defaultValue);
+ }
+
@Override
public void addOnPropertiesChangedListener(String namespace, Executor executor,
DeviceConfig.OnPropertiesChangedListener listener) {
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
index d9cf637ffaf8f..09ab004b4b3bc 100644
--- a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
+++ b/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
@@ -27,7 +27,7 @@ import android.util.ArraySet;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
-import com.android.server.wm.utils.DeviceConfigInterface;
+import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index b0c5dbc6cca38..a5ebf9ac74b9b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -23,7 +23,7 @@ import android.provider.AndroidDeviceConfig;
import android.provider.DeviceConfig;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.utils.DeviceConfigInterface;
+import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b7a2eb3c705d3..d9594a40bde31 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -282,8 +282,8 @@ import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
import com.android.server.protolog.ProtoLogImpl;
import com.android.server.protolog.common.ProtoLog;
+import com.android.server.utils.DeviceConfigInterface;
import com.android.server.utils.PriorityDump;
-import com.android.server.wm.utils.DeviceConfigInterface;
import java.io.BufferedWriter;
import java.io.DataInputStream;
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 979f4e179e95d..e57097e488811 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -48,7 +48,6 @@ android_test {
// TODO: remove once Android migrates to JUnit 4.12,
// which provides assertThrows
"testng",
-
],
aidl: {
@@ -110,6 +109,7 @@ java_library {
"utils/**/*.java",
"utils/**/*.kt",
"utils-mockito/**/*.kt",
+ ":services.core-sources-deviceconfig-interface",
],
static_libs: [
"junit",
@@ -126,6 +126,7 @@ java_library {
"utils/**/*.java",
"utils/**/*.kt",
"utils-mockito/**/*.kt",
+ ":services.core-sources-deviceconfig-interface",
],
static_libs: [
"junit",
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 43a396d8e5d7d..4ee6a553b9d15 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -16,49 +16,96 @@
package com.android.server.display;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.verify;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE;
+import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.ContextWrapper;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Looper;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.display.DisplayModeDirector.BrightnessObserver;
import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
import com.android.server.display.DisplayModeDirector.Vote;
+import com.android.server.testutils.FakeDeviceConfigInterface;
import com.google.common.truth.Truth;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DisplayModeDirectorTest {
// The tolerance within which we consider something approximately equals.
+ private static final String TAG = "DisplayModeDirectorTest";
+ private static final boolean DEBUG = false;
private static final float FLOAT_TOLERANCE = 0.01f;
private Context mContext;
+ private FakesInjector mInjector;
+ private Handler mHandler;
+ @Rule
+ public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
+ when(mContext.getContentResolver()).thenReturn(resolver);
+ mInjector = new FakesInjector();
+ mHandler = new Handler(Looper.getMainLooper());
}
private DisplayModeDirector createDirectorFromRefreshRateArray(
float[] refreshRates, int baseModeId) {
DisplayModeDirector director =
- new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper()));
+ new DisplayModeDirector(mContext, mHandler, mInjector);
int displayId = 0;
Display.Mode[] modes = new Display.Mode[refreshRates.length];
for (int i = 0; i < refreshRates.length; i++) {
@@ -159,9 +206,9 @@ public class DisplayModeDirectorTest {
}
@Test
- public void testBrightnessHasLowerPriorityThanUser() {
- assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
- assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE);
+ public void testFlickerHasLowerPriorityThanUser() {
+ assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
+ assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_SIZE);
int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
@@ -169,7 +216,7 @@ public class DisplayModeDirectorTest {
SparseArray> votesByDisplay = new SparseArray<>();
votesByDisplay.put(displayId, votes);
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
- votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+ votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -177,7 +224,7 @@ public class DisplayModeDirectorTest {
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
- votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+ votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -185,7 +232,7 @@ public class DisplayModeDirectorTest {
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
- votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+ votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -193,7 +240,7 @@ public class DisplayModeDirectorTest {
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
- votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+ votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -202,10 +249,10 @@ public class DisplayModeDirectorTest {
@Test
public void testAppRequestRefreshRateRange() {
- // Confirm that the app request range doesn't include low brightness or min refresh rate
- // settings, but does include everything else.
+ // Confirm that the app request range doesn't include flicker or min refresh rate settings,
+ // but does include everything else.
assertTrue(
- Vote.PRIORITY_LOW_BRIGHTNESS < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
+ PRIORITY_FLICKER < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE
< Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE
@@ -216,7 +263,7 @@ public class DisplayModeDirectorTest {
SparseArray votes = new SparseArray<>();
SparseArray> votesByDisplay = new SparseArray<>();
votesByDisplay.put(displayId, votes);
- votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+ votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -302,4 +349,307 @@ public class DisplayModeDirectorTest {
verifyBrightnessObserverCall(director, 90, 90, 0, 90, 90);
verifyBrightnessObserverCall(director, 120, 90, 0, 120, 90);
}
+
+ @Test
+ public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+ SensorManager sensorManager = createMockSensorManager(createLightSensor());
+
+ final int initialRefreshRate = 60;
+ mInjector.getDeviceConfig().setRefreshRateInLowZone(initialRefreshRate);
+ director.start(sensorManager);
+ assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
+ .isEqualTo(initialRefreshRate);
+
+ final int updatedRefreshRate = 90;
+ mInjector.getDeviceConfig().setRefreshRateInLowZone(updatedRefreshRate);
+ // Need to wait for the property change to propagate to the main thread.
+ waitForIdleSync();
+ assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
+ .isEqualTo(updatedRefreshRate);
+ }
+
+ @Test
+ public void testBrightnessObserverThresholdsInZone() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+ SensorManager sensorManager = createMockSensorManager(createLightSensor());
+
+ final int[] initialDisplayThresholds = { 10 };
+ final int[] initialAmbientThresholds = { 20 };
+
+ final FakeDeviceConfig config = mInjector.getDeviceConfig();
+ config.setLowDisplayBrightnessThresholds(initialDisplayThresholds);
+ config.setLowAmbientBrightnessThresholds(initialAmbientThresholds);
+ director.start(sensorManager);
+
+ assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
+ .isEqualTo(initialDisplayThresholds);
+ assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
+ .isEqualTo(initialAmbientThresholds);
+
+ final int[] updatedDisplayThresholds = { 9, 14 };
+ final int[] updatedAmbientThresholds = { -1, 19 };
+ config.setLowDisplayBrightnessThresholds(updatedDisplayThresholds);
+ config.setLowAmbientBrightnessThresholds(updatedAmbientThresholds);
+ // Need to wait for the property change to propagate to the main thread.
+ waitForIdleSync();
+ assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
+ .isEqualTo(updatedDisplayThresholds);
+ assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
+ .isEqualTo(updatedAmbientThresholds);
+ }
+
+ @Test
+ public void testLockFpsForLowZone() throws Exception {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90);
+ director.getSettingsObserver().setDefaultRefreshRate(90);
+ director.getBrightnessObserver().setDefaultDisplayState(true);
+
+ final FakeDeviceConfig config = mInjector.getDeviceConfig();
+ config.setRefreshRateInLowZone(90);
+ config.setLowDisplayBrightnessThresholds(new int[] { 10 });
+ config.setLowAmbientBrightnessThresholds(new int[] { 20 });
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+
+ director.start(sensorManager);
+
+ ArgumentCaptor listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ setBrightness(10);
+ // Sensor reads 20 lux,
+ listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+ assertVoteForRefreshRateLocked(vote, 90 /*fps*/);
+
+ setBrightness(125);
+ // Sensor reads 1000 lux,
+ listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+ assertThat(vote).isNull();
+ }
+
+ @Test
+ public void testLockFpsForHighZone() throws Exception {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90 /*fps*/);
+ director.getSettingsObserver().setDefaultRefreshRate(90);
+ director.getBrightnessObserver().setDefaultDisplayState(true);
+ director.updateSettingForHighZone(60, new int[] {255}, new int[] {8000});
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+
+ director.start(sensorManager);
+
+ ArgumentCaptor listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ setBrightness(100);
+ // Sensor reads 2000 lux,
+ listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+ assertThat(vote).isNull();
+
+ setBrightness(255);
+ // Sensor reads 9000 lux,
+ listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+ assertVoteForRefreshRateLocked(vote, 60 /*fps*/);
+ }
+
+ private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) {
+ assertThat(vote).isNotNull();
+ final DisplayModeDirector.RefreshRateRange expectedRange =
+ new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate);
+ assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
+ }
+
+ private static class FakeDeviceConfig extends FakeDeviceConfigInterface {
+ @Override
+ public String getProperty(String namespace, String name) {
+ Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
+ return super.getProperty(namespace, name);
+ }
+
+ @Override
+ public void addOnPropertiesChangedListener(
+ String namespace,
+ Executor executor,
+ DeviceConfig.OnPropertiesChangedListener listener) {
+ Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
+ super.addOnPropertiesChangedListener(namespace, executor, listener);
+ }
+
+ void setRefreshRateInLowZone(int fps) {
+ putPropertyAndNotify(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_ZONE,
+ String.valueOf(fps));
+ }
+
+ void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) {
+ String thresholds = toPropertyValue(brightnessThresholds);
+
+ if (DEBUG) {
+ Slog.e(TAG, "Brightness Thresholds = " + thresholds);
+ }
+
+ putPropertyAndNotify(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS,
+ thresholds);
+ }
+
+ void setLowAmbientBrightnessThresholds(int[] ambientThresholds) {
+ String thresholds = toPropertyValue(ambientThresholds);
+
+ if (DEBUG) {
+ Slog.e(TAG, "Ambient Thresholds = " + thresholds);
+ }
+
+ putPropertyAndNotify(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS,
+ thresholds);
+ }
+
+ @NonNull
+ private static String toPropertyValue(@NonNull int[] intArray) {
+ return Arrays.stream(intArray)
+ .mapToObj(Integer::toString)
+ .collect(Collectors.joining(","));
+ }
+ }
+
+ private void setBrightness(int brightness) {
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS,
+ brightness);
+ mInjector.notifyBrightnessChanged();
+ waitForIdleSync();
+ }
+
+ private void setPeakRefreshRate(float fps) {
+ Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
+ fps);
+ mInjector.notifyPeakRefreshRateChanged();
+ waitForIdleSync();
+ }
+
+ private static SensorManager createMockSensorManager(Sensor... sensors) {
+ SensorManager sensorManager = Mockito.mock(SensorManager.class);
+ when(sensorManager.getSensorList(anyInt())).then((invocation) -> {
+ List requestedSensors = new ArrayList<>();
+ int type = invocation.getArgument(0);
+ for (Sensor sensor : sensors) {
+ if (sensor.getType() == type || type == Sensor.TYPE_ALL) {
+ requestedSensors.add(sensor);
+ }
+ }
+ return requestedSensors;
+ });
+
+ when(sensorManager.getDefaultSensor(anyInt())).then((invocation) -> {
+ int type = invocation.getArgument(0);
+ for (Sensor sensor : sensors) {
+ if (sensor.getType() == type) {
+ return sensor;
+ }
+ }
+ return null;
+ });
+ return sensorManager;
+ }
+
+ private static Sensor createLightSensor() {
+ try {
+ return TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
+ } catch (Exception e) {
+ // There's nothing we can do if this fails, just throw a RuntimeException so that we
+ // don't have to mark every function that might call this as throwing Exception
+ throw new RuntimeException("Failed to create a light sensor", e);
+ }
+ }
+
+ private void waitForIdleSync() {
+ mHandler.runWithScissors(() -> { }, 500 /*timeout*/);
+ }
+
+ static class FakesInjector implements DisplayModeDirector.Injector {
+ private final FakeDeviceConfig mDeviceConfig;
+ private ContentObserver mBrightnessObserver;
+ private ContentObserver mPeakRefreshRateObserver;
+
+ FakesInjector() {
+ mDeviceConfig = new FakeDeviceConfig();
+ }
+
+ @NonNull
+ public FakeDeviceConfig getDeviceConfig() {
+ return mDeviceConfig;
+ }
+
+ @Override
+ public void registerBrightnessObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ if (mBrightnessObserver != null) {
+ throw new IllegalStateException("Tried to register a second brightness observer");
+ }
+ mBrightnessObserver = observer;
+ }
+
+ @Override
+ public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ mBrightnessObserver = null;
+ }
+
+ void notifyBrightnessChanged() {
+ if (mBrightnessObserver != null) {
+ mBrightnessObserver.dispatchChange(false /*selfChange*/, DISPLAY_BRIGHTNESS_URI);
+ }
+ }
+
+ @Override
+ public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ mPeakRefreshRateObserver = observer;
+ }
+
+ void notifyPeakRefreshRateChanged() {
+ if (mPeakRefreshRateObserver != null) {
+ mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
+ PEAK_REFRESH_RATE_URI);
+ }
+ }
+
+ @Override
+ public boolean isDeviceInteractive(@NonNull Context context) {
+ return true;
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
similarity index 92%
rename from services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java
rename to services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
index 2904a5b736463..a67f64596ef51 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.utils;
+package com.android.server.testutils;
import android.annotation.NonNull;
import android.provider.DeviceConfig;
@@ -22,6 +22,7 @@ import android.util.ArrayMap;
import android.util.Pair;
import com.android.internal.util.Preconditions;
+import com.android.server.utils.DeviceConfigInterface;
import java.lang.reflect.Constructor;
import java.util.HashMap;
@@ -121,6 +122,19 @@ public class FakeDeviceConfigInterface implements DeviceConfigInterface {
}
}
+ @Override
+ public float getFloat(String namespace, String name, float defaultValue) {
+ String value = getProperty(namespace, name);
+ if (value == null) {
+ return defaultValue;
+ }
+ try {
+ return Float.parseFloat(value);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
@Override
public boolean getBoolean(String namespace, String name, boolean defaultValue) {
String value = getProperty(namespace, name);
diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
index 56cb447e65b0b..a85e1db32ce51 100644
--- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
@@ -31,7 +31,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.util.Preconditions;
-import com.android.server.wm.utils.FakeDeviceConfigInterface;
+import com.android.server.testutils.FakeDeviceConfigInterface;
import org.junit.After;
import org.junit.Test;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
index 52100116df531..7a0ef0d7d7a98 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
@@ -32,7 +32,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
-import com.android.server.wm.utils.FakeDeviceConfigInterface;
+import com.android.server.testutils.FakeDeviceConfigInterface;
import org.junit.After;
import org.junit.Before;
From 58515946ac0f3700c10353a2b4d9e87f0048efd5 Mon Sep 17 00:00:00 2001
From: Michael Wright
Date: Fri, 15 Jan 2021 02:47:05 +0800
Subject: [PATCH 065/288] DO NOT MERGE "Allow DeviceConfig to change display
settings for high zone"
Allow DeviceConfig to change the high zone behavior including
refresh rate and brightness thresholds.
Bug: 177636374
Test: atest DisplayModeDirectorTest
Test: adb shell device_config put display_manager refresh_rate_in_high_zone "90"
Test: adb shell device_config put display_manager fixed_refresh_rate_high_display_brightness_thresholds "200"
Test: adb shell device_config put display_manager fixed_refresh_rate_high_ambient_brightness_thresholds "8000"
Change-Id: I1463d6c5daaee46a87a193e98e7f63cad9274b49
---
.../hardware/display/DisplayManager.java | 57 ++++++--
.../server/display/DisplayModeDirector.java | 132 ++++++++++++++----
.../display/DisplayModeDirectorTest.java | 53 ++++++-
3 files changed, 202 insertions(+), 40 deletions(-)
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index c1ba2094d3cf6..0fa4ca8a574b5 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -875,12 +875,52 @@ public final class DisplayManager {
public interface DeviceConfig {
/**
- * Key for refresh rate in the zone defined by thresholds.
+ * Key for refresh rate in the low zone defined by thresholds.
*
+ * Note that the name and value don't match because they were added before we had a high
+ * zone to consider.
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
* @see android.R.integer#config_defaultZoneBehavior
*/
- String KEY_REFRESH_RATE_IN_ZONE = "refresh_rate_in_zone";
+ String KEY_REFRESH_RATE_IN_LOW_ZONE = "refresh_rate_in_zone";
+
+ /**
+ * Key for accessing the low display brightness thresholds for the configured refresh
+ * rate zone.
+ * The value will be a pair of comma separated integers representing the minimum and maximum
+ * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
+ *
+ * Note that the name and value don't match because they were added before we had a high
+ * zone to consider.
+ *
+ * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+ * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
+ * @hide
+ */
+ String KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS =
+ "peak_refresh_rate_brightness_thresholds";
+
+ /**
+ * Key for accessing the low ambient brightness thresholds for the configured refresh
+ * rate zone. The value will be a pair of comma separated integers representing the minimum
+ * and maximum thresholds of the zone, respectively, in lux.
+ *
+ * Note that the name and value don't match because they were added before we had a high
+ * zone to consider.
+ *
+ * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+ * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
+ * @hide
+ */
+ String KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS =
+ "peak_refresh_rate_ambient_thresholds";
+ /**
+ * Key for refresh rate in the high zone defined by thresholds.
+ *
+ * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+ * @see android.R.integer#config_fixedRefreshRateInHighZone
+ */
+ String KEY_REFRESH_RATE_IN_HIGH_ZONE = "refresh_rate_in_high_zone";
/**
* Key for accessing the display brightness thresholds for the configured refresh rate zone.
@@ -888,11 +928,11 @@ public final class DisplayManager {
* thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
- * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
+ * @see android.R.array#config_brightnessHighThresholdsOfFixedRefreshRate
* @hide
*/
- String KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS =
- "peak_refresh_rate_brightness_thresholds";
+ String KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS =
+ "fixed_refresh_rate_high_display_brightness_thresholds";
/**
* Key for accessing the ambient brightness thresholds for the configured refresh rate zone.
@@ -900,12 +940,11 @@ public final class DisplayManager {
* thresholds of the zone, respectively, in lux.
*
* @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
- * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
+ * @see android.R.array#config_ambientHighThresholdsOfFixedRefreshRate
* @hide
*/
- String KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS =
- "peak_refresh_rate_ambient_thresholds";
-
+ String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS =
+ "fixed_refresh_rate_high_ambient_brightness_thresholds";
/**
* Key for default peak refresh rate
*
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 2a60839ab702a..02ee059194ec3 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -69,6 +69,8 @@ public class DisplayModeDirector {
private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
+ private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
+ private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
// Special ID used to indicate that given vote is to be applied globally, rather than to a
// specific display.
@@ -515,13 +517,6 @@ public class DisplayModeDirector {
}
}
- @VisibleForTesting
- void updateSettingForHighZone(int refreshRate, int[] brightnessThresholds,
- int[] ambientThresholds) {
- mBrightnessObserver.updateThresholdsRefreshRateForHighZone(refreshRate,
- brightnessThresholds, ambientThresholds);
- }
-
/**
* Listens for changes refresh rate coordination.
*/
@@ -540,11 +535,35 @@ public class DisplayModeDirector {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED:
+ case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: {
Pair thresholds = (Pair) msg.obj;
mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
thresholds.first, thresholds.second);
break;
+ }
+
+ case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED: {
+ int refreshRateInZone = msg.arg1;
+ mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
+ refreshRateInZone);
+ break;
+ }
+
+ case MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED: {
+ Pair thresholds = (Pair) msg.obj;
+
+ mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged(
+ thresholds.first, thresholds.second);
+
+ break;
+ }
+
+ case MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED: {
+ int refreshRateInZone = msg.arg1;
+ mBrightnessObserver.onDeviceConfigRefreshRateInHighZoneChanged(
+ refreshRateInZone);
+ break;
+ }
case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
Float defaultPeakRefreshRate = (Float) msg.obj;
@@ -552,12 +571,6 @@ public class DisplayModeDirector {
defaultPeakRefreshRate);
break;
- case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED:
- int refreshRateInZone = msg.arg1;
- mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
- refreshRateInZone);
- break;
-
case MSG_REFRESH_RATE_RANGE_CHANGED:
DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener =
(DesiredDisplayModeSpecsListener) msg.obj;
@@ -1220,13 +1233,6 @@ public class DisplayModeDirector {
mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
}
- public void updateThresholdsRefreshRateForHighZone(int refreshRate,
- int[] brightnessThresholds, int[] ambientThresholds) {
- mRefreshRateInHighZone = refreshRate;
- mHighDisplayBrightnessThresholds = brightnessThresholds;
- mHighAmbientBrightnessThresholds = ambientThresholds;
- }
-
public void observe(SensorManager sensorManager) {
mSensorManager = sensorManager;
final ContentResolver cr = mContext.getContentResolver();
@@ -1246,7 +1252,22 @@ public class DisplayModeDirector {
mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
}
+
+ int[] highDisplayBrightnessThresholds =
+ mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
+ int[] highAmbientBrightnessThresholds =
+ mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
+
+ if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
+ && highDisplayBrightnessThresholds.length
+ == highAmbientBrightnessThresholds.length) {
+ mHighDisplayBrightnessThresholds = highDisplayBrightnessThresholds;
+ mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
+ }
+
mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
+ mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone();
+
restartObserver();
mDeviceConfigDisplaySettings.startListening();
}
@@ -1293,6 +1314,29 @@ public class DisplayModeDirector {
}
}
+ public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
+ int[] ambientThresholds) {
+ if (displayThresholds != null && ambientThresholds != null
+ && displayThresholds.length == ambientThresholds.length) {
+ mHighDisplayBrightnessThresholds = displayThresholds;
+ mHighAmbientBrightnessThresholds = ambientThresholds;
+ } else {
+ // Invalid or empty. Use device default.
+ mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+ R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
+ mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+ R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
+ }
+ restartObserver();
+ }
+
+ public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) {
+ if (refreshRate != mRefreshRateInHighZone) {
+ mRefreshRateInHighZone = refreshRate;
+ restartObserver();
+ }
+ }
+
public void dumpLocked(PrintWriter pw) {
pw.println(" BrightnessObserver");
pw.println(" mAmbientLux: " + mAmbientLux);
@@ -1668,7 +1712,7 @@ public class DisplayModeDirector {
public int[] getLowDisplayBrightnessThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig.
- KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
}
/*
@@ -1677,7 +1721,7 @@ public class DisplayModeDirector {
public int[] getLowAmbientBrightnessThresholds() {
return getIntArrayProperty(
DisplayManager.DeviceConfig.
- KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
}
public int getRefreshRateInLowZone() {
@@ -1686,7 +1730,37 @@ public class DisplayModeDirector {
int refreshRate = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
+ defaultRefreshRateInZone);
+
+ return refreshRate;
+ }
+
+ /*
+ * Return null if no such property or wrong format (not comma separated integers).
+ */
+ public int[] getHighDisplayBrightnessThresholds() {
+ return getIntArrayProperty(
+ DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /*
+ * Return null if no such property or wrong format (not comma separated integers).
+ */
+ public int[] getHighAmbientBrightnessThresholds() {
+ return getIntArrayProperty(
+ DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ }
+
+ public int getRefreshRateInHighZone() {
+ int defaultRefreshRateInZone = mContext.getResources().getInteger(
+ R.integer.config_fixedRefreshRateInHighZone);
+
+ int refreshRate = mDeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
defaultRefreshRateInZone);
return refreshRate;
@@ -1721,6 +1795,16 @@ public class DisplayModeDirector {
.sendToTarget();
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
.sendToTarget();
+
+ int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
+ int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
+ int refreshRateInHighZone = getRefreshRateInHighZone();
+
+ mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
+ new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
+ .sendToTarget();
+ mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
+ .sendToTarget();
}
private int[] getIntArrayProperty(String prop) {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 4ee6a553b9d15..c467ee949aeba 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -16,9 +16,12 @@
package com.android.server.display;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS;
-import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER;
@@ -451,7 +454,11 @@ public class DisplayModeDirectorTest {
setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(true);
- director.updateSettingForHighZone(60, new int[] {255}, new int[] {8000});
+
+ final FakeDeviceConfig config = mInjector.getDeviceConfig();
+ config.setRefreshRateInHighZone(60);
+ config.setHighDisplayBrightnessThresholds(new int[] { 255 });
+ config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
Sensor lightSensor = createLightSensor();
SensorManager sensorManager = createMockSensorManager(lightSensor);
@@ -508,7 +515,7 @@ public class DisplayModeDirectorTest {
void setRefreshRateInLowZone(int fps) {
putPropertyAndNotify(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_ZONE,
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_LOW_ZONE,
String.valueOf(fps));
}
@@ -521,7 +528,7 @@ public class DisplayModeDirectorTest {
putPropertyAndNotify(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS,
+ KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS,
thresholds);
}
@@ -534,7 +541,39 @@ public class DisplayModeDirectorTest {
putPropertyAndNotify(
DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS,
+ KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS,
+ thresholds);
+ }
+
+ void setRefreshRateInHighZone(int fps) {
+ putPropertyAndNotify(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HIGH_ZONE,
+ String.valueOf(fps));
+ }
+
+ void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) {
+ String thresholds = toPropertyValue(brightnessThresholds);
+
+ if (DEBUG) {
+ Slog.e(TAG, "Brightness Thresholds = " + thresholds);
+ }
+
+ putPropertyAndNotify(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS,
+ thresholds);
+ }
+
+ void setHighAmbientBrightnessThresholds(int[] ambientThresholds) {
+ String thresholds = toPropertyValue(ambientThresholds);
+
+ if (DEBUG) {
+ Slog.e(TAG, "Ambient Thresholds = " + thresholds);
+ }
+
+ putPropertyAndNotify(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS,
thresholds);
}
From 73ba11e97b3b31b0423fa37223d1bd13e9ecb4f2 Mon Sep 17 00:00:00 2001
From: raylinhsu
Date: Fri, 15 Jan 2021 02:48:14 +0800
Subject: [PATCH 066/288] DO NOT MERGE "Fix the ambient lux which did not
update for blocking zone"
When the display enters doze mode, it should unregister the light
sensor. It should register light sensor again when the display
state change from doze to on otherwise we will lost lux information
and then the refresh rate could not be locked.
Bug: 177636374
Test: atest DisplayModeDirectorTest
Test: no fps transition in the blocking zone when the display state change
Test: check if the lux will update when the display state change
Change-Id: Ib69314aaf716be38af605f8abcca139c4bdd33f1
---
.../server/display/DisplayModeDirector.java | 35 ++++++++++++----
.../display/DisplayModeDirectorTest.java | 42 ++++++++++++++++++-
2 files changed, 67 insertions(+), 10 deletions(-)
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 02ee059194ec3..29a77e2b6ef23 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -52,9 +52,12 @@ import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
/**
@@ -1160,7 +1163,8 @@ public class DisplayModeDirector {
// mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
// changeable and low power mode off. After initialization, these states will
// be updated from the same handler thread.
- private boolean mDefaultDisplayOn = false;
+ private int mDefaultDisplayState = Display.STATE_UNKNOWN;
+ private boolean mIsDeviceActive = false;
private boolean mRefreshRateChangeable = false;
private boolean mLowPowerModeEnabled = false;
@@ -1341,7 +1345,8 @@ public class DisplayModeDirector {
pw.println(" BrightnessObserver");
pw.println(" mAmbientLux: " + mAmbientLux);
pw.println(" mBrightness: " + mBrightness);
- pw.println(" mDefaultDisplayOn: " + mDefaultDisplayOn);
+ pw.println(" mDefaultDisplayState: " + mDefaultDisplayState);
+ pw.println(" mIsDeviceActive: " + mIsDeviceActive);
pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled);
pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable);
pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
@@ -1567,14 +1572,17 @@ public class DisplayModeDirector {
private void updateDefaultDisplayState() {
Display display = mContext.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY);
- boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF;
- setDefaultDisplayState(defaultDisplayOn);
+ if (display == null) {
+ return;
+ }
+
+ setDefaultDisplayState(display.getState());
}
@VisibleForTesting
- public void setDefaultDisplayState(boolean on) {
- if (mDefaultDisplayOn != on) {
- mDefaultDisplayOn = on;
+ public void setDefaultDisplayState(int state) {
+ if (mDefaultDisplayState != state) {
+ mDefaultDisplayState = state;
updateSensorStatus();
}
}
@@ -1595,15 +1603,19 @@ public class DisplayModeDirector {
}
private boolean isDeviceActive() {
- return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext);
+ mIsDeviceActive = mInjector.isDeviceInteractive(mContext);
+ return (mDefaultDisplayState == Display.STATE_ON)
+ && mIsDeviceActive;
}
private final class LightSensorEventListener implements SensorEventListener {
final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
private float mLastSensorData;
+ private long mTimestamp;
public void dumpLocked(PrintWriter pw) {
pw.println(" mLastSensorData: " + mLastSensorData);
+ pw.println(" mTimestamp: " + formatTimestamp(mTimestamp));
}
@Override
@@ -1627,6 +1639,7 @@ public class DisplayModeDirector {
}
long now = SystemClock.uptimeMillis();
+ mTimestamp = System.currentTimeMillis();
if (mAmbientFilter != null) {
mAmbientFilter.addValue(now, mLastSensorData);
}
@@ -1653,6 +1666,12 @@ public class DisplayModeDirector {
mHandler.removeCallbacks(mInjectSensorEventRunnable);
}
+ private String formatTimestamp(long time) {
+ SimpleDateFormat dateFormat =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
+ return dateFormat.format(new Date(time));
+ }
+
private void processSensorData(long now) {
if (mAmbientFilter != null) {
mAmbientLux = mAmbientFilter.getEstimate(now);
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index c467ee949aeba..e202145b7dbee 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.annotation.NonNull;
import android.content.ContentResolver;
@@ -410,7 +411,7 @@ public class DisplayModeDirectorTest {
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90);
director.getSettingsObserver().setDefaultRefreshRate(90);
- director.getBrightnessObserver().setDefaultDisplayState(true);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInLowZone(90);
@@ -453,7 +454,7 @@ public class DisplayModeDirectorTest {
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
- director.getBrightnessObserver().setDefaultDisplayState(true);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInHighZone(60);
@@ -490,6 +491,43 @@ public class DisplayModeDirectorTest {
assertVoteForRefreshRateLocked(vote, 60 /*fps*/);
}
+ @Test
+ public void testSensorRegistration() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90 /*fps*/);
+ director.getSettingsObserver().setDefaultRefreshRate(90);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+
+ director.start(sensorManager);
+ ArgumentCaptor listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+
+ // Dispaly state changed from On to Doze
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_DOZE);
+ Mockito.verify(sensorManager)
+ .unregisterListener(listenerCaptor.capture());
+
+ // Dispaly state changed from Doze to On
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+ Mockito.verify(sensorManager, times(2))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+
+ }
+
private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
final DisplayModeDirector.RefreshRateRange expectedRange =
From 9c4853d363603f44adbc7a257f5f5fbc7a2de880 Mon Sep 17 00:00:00 2001
From: Sungsoo Lim
Date: Wed, 6 Jan 2021 02:59:37 +0000
Subject: [PATCH 067/288] Prevent multiple calls of ActivityThread.systemMain()
Bug: 170591440
Test: manually
Change-Id: Iba6d10e5203fa1c1f96c85e39a57483ee26e8111
(cherry picked from commit 8f2c59c0cba7a40adc6a8dac613566b1c43f9f6c)
---
.../core/java/com/android/server/media/MediaShellCommand.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java
index 20df271a1de2a..b199325798f4b 100644
--- a/services/core/java/com/android/server/media/MediaShellCommand.java
+++ b/services/core/java/com/android/server/media/MediaShellCommand.java
@@ -64,7 +64,7 @@ public class MediaShellCommand extends ShellCommand {
}
if (sThread == null) {
Looper.prepare();
- sThread = ActivityThread.systemMain();
+ sThread = ActivityThread.currentActivityThread();
Context context = sThread.getSystemContext();
sMediaSessionManager =
(MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
From 1f610b73f3114e8c675aea310b3121faee6acdaa Mon Sep 17 00:00:00 2001
From: Jack Yu
Date: Fri, 15 Jan 2021 10:19:38 -0800
Subject: [PATCH 068/288] Temporary fix to prevent TEMPORARILY_NOT_METERED leak
Currently we are seeing TEMPORARILY_NOT_METERED occurs on non-5G
devices. This fix adds more checks to prevent incorrect
TEMPORARILY_NOT_METERED being set. Before we root cause the issue,
we only allow this bit when all of the following conditions met.
1. Devices should support 5G.
2. Carriers should support 5G unmetered data.
3. Device should camp on 5G network.
This temporary fix should be removed after we root cause the issue.
Bug: 176119724
Test: Manual
Merged-In: I2b987d24851cdb16d8457b0ec5c546ace5485ed0
Change-Id: I2b987d24851cdb16d8457b0ec5c546ace5485ed0
---
.../java/android/telephony/CarrierConfigManager.java | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 74b2aad5293e2..111ffe084ef1d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4082,6 +4082,13 @@ public class CarrierConfigManager {
*/
public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
+ /**
+ * Indicates temporarily unmetered mobile data is supported by the carrier.
+ * @hide
+ */
+ public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL =
+ "network_temp_not_metered_supported_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -4628,6 +4635,7 @@ public class CarrierConfigManager {
sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, "");
sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
+ sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, false);
}
/**
From 44f2b9a27b7522bfe73df816604691a8246caaca Mon Sep 17 00:00:00 2001
From: Yu-Han Yang
Date: Fri, 15 Jan 2021 15:23:52 -0800
Subject: [PATCH 069/288] DO NOT MERGE: Do not inject mock location to chipset
Bug: 177561690
Test: on device
Change-Id: Icafbdf54fe807f8779377b13cb4e4eb265db692e
---
.../com/android/server/location/GnssLocationProvider.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 342e9b8c3006b..491bba2272adc 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -805,6 +805,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
private void injectBestLocation(Location location) {
+ if (location.isFromMockProvider()) {
+ return;
+ }
if (DEBUG) {
Log.d(TAG, "injectBestLocation: " + location);
}
@@ -911,6 +914,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
private void handleUpdateLocation(Location location) {
+ if (location.isFromMockProvider()) {
+ return;
+ }
if (location.hasAccuracy()) {
if (DEBUG) {
Log.d(TAG, "injectLocation: " + location);
From abccd8f7bc76e19e92d2dfebe2247e97619f7234 Mon Sep 17 00:00:00 2001
From: Yu-Han Yang
Date: Fri, 15 Jan 2021 14:24:09 -0800
Subject: [PATCH 070/288] DO NOT MERGE: Do not inject mock location to chipset
Bug: 177561690
Test: on device
Change-Id: Icafbdf54fe807f8779377b13cb4e4eb265db692e
---
.../android/server/location/gnss/GnssLocationProvider.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index a6a607e4ce66f..8d1d3afab5c5f 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -841,6 +841,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
private void injectBestLocation(Location location) {
+ if (location.isFromMockProvider()) {
+ return;
+ }
if (DEBUG) {
Log.d(TAG, "injectBestLocation: " + location);
}
@@ -942,6 +945,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
private void injectLocation(Location location) {
+ if (location.isFromMockProvider()) {
+ return;
+ }
if (location.hasAccuracy()) {
if (DEBUG) {
Log.d(TAG, "injectLocation: " + location);
From 2a8ab5efe7719336d46761f3fcc925c7fe43b440 Mon Sep 17 00:00:00 2001
From: Curtis Belmonte
Date: Tue, 19 Jan 2021 16:07:30 -0800
Subject: [PATCH 071/288] Fix fingerprint dialog color contrast for dark theme
The code to set the color of the help text shown on the fingerprint
dialog does not currently check the context theme. This means that the
wrong color is used when the system is using a dark theme.
This commit fixes the issue by ensuring the appropriate themed color is
used.
Test: Manual
1. Turn on system dark theme (Settings > Display > Dark theme)
2. Trigger the fingerprint dialog (e.g. Wi-Fi > Network details > Share)
Bug: 174831630
Change-Id: I41dfb536e72063ae8ea84d51fa0e50613f920090
Merged-In: I41dfb536e72063ae8ea84d51fa0e50613f920090
---
.../systemui/biometrics/AuthBiometricFaceView.java | 13 ++++++-------
.../biometrics/AuthBiometricFingerprintView.java | 2 +-
.../systemui/biometrics/AuthBiometricView.java | 8 ++++----
3 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
index 1d47fc520ec2a..7daad1ca7e79d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
@@ -160,12 +160,12 @@ public class AuthBiometricFaceView extends AuthBiometricView {
@Override
protected void handleResetAfterError() {
- resetErrorView(mContext, mIndicatorView);
+ resetErrorView();
}
@Override
protected void handleResetAfterHelp() {
- resetErrorView(mContext, mIndicatorView);
+ resetErrorView();
}
@Override
@@ -185,7 +185,7 @@ public class AuthBiometricFaceView extends AuthBiometricView {
if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
(newState == STATE_AUTHENTICATING && mSize == AuthDialog.SIZE_MEDIUM)) {
- resetErrorView(mContext, mIndicatorView);
+ resetErrorView();
}
// Do this last since the state variable gets updated.
@@ -204,9 +204,8 @@ public class AuthBiometricFaceView extends AuthBiometricView {
super.onAuthenticationFailed(failureReason);
}
- static void resetErrorView(Context context, TextView textView) {
- textView.setTextColor(context.getResources().getColor(
- R.color.biometric_dialog_gray, context.getTheme()));
- textView.setVisibility(View.INVISIBLE);
+ private void resetErrorView() {
+ mIndicatorView.setTextColor(mTextColorHint);
+ mIndicatorView.setVisibility(View.INVISIBLE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
index 176e9e6b1c9bc..45ee4ad9ae501 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
@@ -78,7 +78,7 @@ public class AuthBiometricFingerprintView extends AuthBiometricView {
private void showTouchSensorString() {
mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor);
- mIndicatorView.setTextColor(R.color.biometric_dialog_gray);
+ mIndicatorView.setTextColor(mTextColorHint);
}
private void updateIcon(int lastState, int newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 7c25d28117939..f9c6d32d1d19b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -82,7 +82,7 @@ public abstract class AuthBiometricView extends LinearLayout {
* Authenticated, dialog animating away soon.
*/
protected static final int STATE_AUTHENTICATED = 6;
-
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({STATE_IDLE, STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_HELP,
STATE_ERROR, STATE_PENDING_CONFIRMATION, STATE_AUTHENTICATED})
@@ -155,8 +155,8 @@ public abstract class AuthBiometricView extends LinearLayout {
private final Injector mInjector;
private final Handler mHandler;
private final AccessibilityManager mAccessibilityManager;
- private final int mTextColorError;
- private final int mTextColorHint;
+ protected final int mTextColorError;
+ protected final int mTextColorHint;
private AuthPanelController mPanelController;
private Bundle mBiometricPromptBundle;
@@ -169,7 +169,7 @@ public abstract class AuthBiometricView extends LinearLayout {
private TextView mSubtitleView;
private TextView mDescriptionView;
protected ImageView mIconView;
- @VisibleForTesting protected TextView mIndicatorView;
+ protected TextView mIndicatorView;
@VisibleForTesting Button mNegativeButton;
@VisibleForTesting Button mPositiveButton;
@VisibleForTesting Button mTryAgainButton;
From 6e3806fc3417b29c1e33d908d150dcd737c5944e Mon Sep 17 00:00:00 2001
From: Beverly
Date: Fri, 15 Jan 2021 12:17:02 -0500
Subject: [PATCH 072/288] Don't release WakeLock w/o active client
Once we see the WakeLock isn't held, we should return before attempting
to release a non-existent WakeLock.
Bug: 168002974
Test: manual
Change-Id: I0172c637d7f6f22888c126da42d4fd1fe45fe0db
(cherry picked from commit 386c3fcc8f6678d330f72e900c40f890db9b76f4)
---
.../src/com/android/systemui/util/wakelock/WakeLock.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
index 08cd6e383897a..8d77c4a194a95 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
@@ -33,7 +33,7 @@ public interface WakeLock {
static final String REASON_WRAP = "wrap";
/**
- * Default wake-lock timeout, to avoid battery regressions.
+ * Default wake-lock timeout in milliseconds, to avoid battery regressions.
*/
long DEFAULT_MAX_TIMEOUT = 20000;
@@ -104,6 +104,7 @@ public interface WakeLock {
if (count == null) {
Log.wtf(TAG, "Releasing WakeLock with invalid reason: " + why,
new Throwable());
+ return;
} else if (count == 1) {
mActiveClients.remove(why);
} else {
From bd6fc820a677f5d128149e1152c15ebb20780d33 Mon Sep 17 00:00:00 2001
From: Songchun Fan
Date: Fri, 15 Jan 2021 16:15:00 -0800
Subject: [PATCH 073/288] [SettingsProvider] extend font size scale range
As per requested in b/176940932#comment3 and b/156260178#comment32,
extending the valid font size scale range to [0.25, 5].
Existing CTS tests still pass.
BUG: 156260178
Test: atest android.provider.cts.settings.Settings_SystemTest
Test: atest android.app.cts.ApplicationTest
Change-Id: Icff82d727d63da4353342b0f9a5ca3c2ae1671c1
(cherry picked from commit df8852a0b596f28a4588f553a631fd7122fefac1)
Merged-In: Icff82d727d63da4353342b0f9a5ca3c2ae1671c1
---
.../provider/settings/validators/SystemSettingsValidators.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index cb610fc611421..bcde58494838a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -89,7 +89,7 @@ public class SystemSettingsValidators {
return value == null || value.length() < MAX_LENGTH;
}
});
- VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.85f, 1.3f));
+ VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.25f, 5.0f));
VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR);
VALIDATORS.put(
System.DISPLAY_COLOR_MODE,
From c0b28414d5cd686bf621fd71978699ec28177b21 Mon Sep 17 00:00:00 2001
From: Songchun Fan
Date: Fri, 15 Jan 2021 16:15:00 -0800
Subject: [PATCH 074/288] [SettingsProvider] extend font size scale range
As per requested in b/176940932#comment3 and b/156260178#comment32,
extending the valid font size scale range to [0.25, 5].
Existing CTS tests still pass.
BUG: 156260178
Test: atest android.provider.cts.settings.Settings_SystemTest
Test: atest android.app.cts.ApplicationTest
Change-Id: Icff82d727d63da4353342b0f9a5ca3c2ae1671c1
(cherry picked from commit df8852a0b596f28a4588f553a631fd7122fefac1)
Merged-In: Icff82d727d63da4353342b0f9a5ca3c2ae1671c1
---
core/java/android/provider/Settings.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 344b053af2d82..c35cfcd4bbaa5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3401,7 +3401,7 @@ public final class Settings {
public static final String FONT_SCALE = "font_scale";
private static final Validator FONT_SCALE_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(0.85f, 1.3f);
+ new SettingsValidators.InclusiveFloatRangeValidator(0.25f, 5.0f);
/**
* The serialized system locale value.
From 9b6c0faca38117dcc75ee1763f5488c9d92e4375 Mon Sep 17 00:00:00 2001
From: Songchun Fan
Date: Fri, 15 Jan 2021 16:15:00 -0800
Subject: [PATCH 075/288] [SettingsProvider] extend font size scale range
As per requested in b/176940932#comment3 and b/156260178#comment32,
extending the valid font size scale range to [0.25, 5].
Existing CTS tests still pass.
BUG: 156260178
Test: atest android.provider.cts.settings.Settings_SystemTest
Test: atest android.app.cts.ApplicationTest
Change-Id: Icff82d727d63da4353342b0f9a5ca3c2ae1671c1
(cherry picked from commit df8852a0b596f28a4588f553a631fd7122fefac1)
Merged-In: Icff82d727d63da4353342b0f9a5ca3c2ae1671c1
---
.../provider/settings/validators/SystemSettingsValidators.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index cb610fc611421..bcde58494838a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -89,7 +89,7 @@ public class SystemSettingsValidators {
return value == null || value.length() < MAX_LENGTH;
}
});
- VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.85f, 1.3f));
+ VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.25f, 5.0f));
VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR);
VALIDATORS.put(
System.DISPLAY_COLOR_MODE,
From b7d4c8098dfaf30ce6f4f4ea57a83457dcfbffb6 Mon Sep 17 00:00:00 2001
From: Songchun Fan
Date: Fri, 15 Jan 2021 16:15:00 -0800
Subject: [PATCH 076/288] [SettingsProvider] extend font size scale range
As per requested in b/176940932#comment3 and b/156260178#comment32,
extending the valid font size scale range to [0.25, 5].
Existing CTS tests still pass.
BUG: 156260178
Test: atest android.provider.cts.settings.Settings_SystemTest
Test: atest android.app.cts.ApplicationTest
Change-Id: Icff82d727d63da4353342b0f9a5ca3c2ae1671c1
(cherry picked from commit df8852a0b596f28a4588f553a631fd7122fefac1)
Merged-In: Icff82d727d63da4353342b0f9a5ca3c2ae1671c1
---
core/java/android/provider/Settings.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e8e25fafddad2..6f8088db5edd6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3418,7 +3418,7 @@ public final class Settings {
public static final String FONT_SCALE = "font_scale";
private static final Validator FONT_SCALE_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(0.85f, 1.3f);
+ new SettingsValidators.InclusiveFloatRangeValidator(0.25f, 5.0f);
/**
* The serialized system locale value.
From 80a05232a04a5ef8df9f5e5fb0afd7471ea7eb38 Mon Sep 17 00:00:00 2001
From: Marco Ballesio
Date: Wed, 6 Jan 2021 15:25:50 -0800
Subject: [PATCH 077/288] ActivityManager: don't freeze processes holding file
locks
As a temporary measure waiting for the more robust b/176927978 to be
implemented, query the file lock status of processes prior freezing
them, and skip freeze if any locks are held.
Test: verified form the logs that processes holding file locks are not
frozen
Bug: 176928302
Change-Id: Ib7e1715effb63c1b304ce22a9451294fd9f7b10d
Merged-In: Ib7e1715effb63c1b304ce22a9451294fd9f7b10d
---
core/java/android/os/Process.java | 38 ++++++++
.../android/server/am/CachedAppOptimizer.java | 86 +++++++++++++------
2 files changed, 97 insertions(+), 27 deletions(-)
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index efea9537c4cfe..47598cf35974e 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -31,8 +31,12 @@ import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
+import java.io.BufferedReader;
import java.io.FileDescriptor;
+import java.io.FileReader;
+import java.io.IOException;
import java.util.Map;
+import java.util.StringTokenizer;
import java.util.concurrent.TimeoutException;
/**
@@ -1393,4 +1397,38 @@ public class Process {
}
private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException;
+
+ /**
+ * Checks if a process corresponding to a specific pid owns any file locks.
+ * @param pid The process ID for which we want to know the existence of file locks.
+ * @return true If the process holds any file locks, false otherwise.
+ * @throws IOException if /proc/locks can't be accessed.
+ *
+ * @hide
+ */
+ public static boolean hasFileLocks(int pid) throws IOException {
+ BufferedReader br = null;
+
+ try {
+ br = new BufferedReader(new FileReader("/proc/locks"));
+ String line;
+
+ while ((line = br.readLine()) != null) {
+ StringTokenizer st = new StringTokenizer(line);
+
+ for (int i = 0; i < 5 && st.hasMoreTokens(); i++) {
+ String str = st.nextToken();
+ if (i == 4 && Integer.parseInt(str) == pid) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ } finally {
+ if (br != null) {
+ br.close();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 36d4a38c1624f..9eb7c07baaed3 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1101,15 +1101,26 @@ public final class CachedAppOptimizer {
}
private void freezeProcess(ProcessRecord proc) {
- final int pid;
- final String name;
+ final int pid = proc.pid;
+ final String name = proc.processName;
final long unfrozenDuration;
final boolean frozen;
- synchronized (mAm) {
- pid = proc.pid;
- name = proc.processName;
+ try {
+ // pre-check for locks to avoid unnecessary freeze/unfreeze operations
+ if (Process.hasFileLocks(pid)) {
+ if (DEBUG_FREEZER) {
+ Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, not freezing");
+ }
+ return;
+ }
+ } catch (IOException e) {
+ Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid
+ + "): " + e);
+ return;
+ }
+ synchronized (mAm) {
if (proc.curAdj < ProcessList.CACHED_APP_MIN_ADJ
|| proc.shouldNotFreeze) {
if (DEBUG_FREEZER) {
@@ -1141,29 +1152,50 @@ public final class CachedAppOptimizer {
frozen = proc.frozen;
}
- if (frozen) {
- if (DEBUG_FREEZER) {
- Slog.d(TAG_AM, "froze " + pid + " " + name);
+ if (!frozen) {
+ return;
+ }
+
+
+ if (DEBUG_FREEZER) {
+ Slog.d(TAG_AM, "froze " + pid + " " + name);
+ }
+
+ EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
+
+ try {
+ freezeBinder(pid, true);
+ } catch (RuntimeException e) {
+ Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
+ proc.kill("Unable to freeze binder interface",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ }
+
+ // See above for why we're not taking mPhenotypeFlagLock here
+ if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
+ FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP,
+ pid,
+ name,
+ unfrozenDuration);
+ }
+
+ try {
+ // post-check to prevent races
+ if (Process.hasFileLocks(pid)) {
+ if (DEBUG_FREEZER) {
+ Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze");
+ }
+
+ synchronized (mAm) {
+ unfreezeAppLocked(proc);
+ }
}
-
- EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
-
- try {
- freezeBinder(pid, true);
- } catch (RuntimeException e) {
- Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
- proc.kill("Unable to freeze binder interface",
- ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
- }
-
- // See above for why we're not taking mPhenotypeFlagLock here
- if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
- FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
- FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP,
- pid,
- name,
- unfrozenDuration);
+ } catch (IOException e) {
+ Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
+ synchronized (mAm) {
+ unfreezeAppLocked(proc);
}
}
}
From c7f94ac6347aaa77f890d1b382177968d2581894 Mon Sep 17 00:00:00 2001
From: Tej Singh
Date: Thu, 10 Sep 2020 23:20:58 -0700
Subject: [PATCH 078/288] Fix thread safety issue on clearing cache
Historically, we havent held a lock on ForceClearCache and
ClearCacheIfNecessary. This is not thread safe because kAllPullAtomInfo
must be accessed in a lock, especially now that pullers can be
registered/unregistered.
Test: atest statsd_test, wrote a repro cl.
Bug: 168156854
Bug: 173552790
Change-Id: I47d53a6d9d274bca4c78dbfd87e0097091b7b8cb
Merged-In: I47d53a6d9d274bca4c78dbfd87e0097091b7b8cb
(cherry picked from commit f9a4bb18ffa1605cced3ee4b161675a0c88941be)
---
cmds/statsd/src/external/StatsPullerManager.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 8a9ec7456e557..99c39f6017b18 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -334,6 +334,7 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
}
int StatsPullerManager::ForceClearPullerCache() {
+ std::lock_guard _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second->ForceClearCache();
@@ -342,6 +343,7 @@ int StatsPullerManager::ForceClearPullerCache() {
}
int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
+ std::lock_guard _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs);
From db097214b109be7ca1b3ae470d4a8354c98a5b62 Mon Sep 17 00:00:00 2001
From: Tej Singh
Date: Thu, 10 Sep 2020 23:20:58 -0700
Subject: [PATCH 079/288] Fix thread safety issue on clearing cache
Historically, we havent held a lock on ForceClearCache and
ClearCacheIfNecessary. This is not thread safe because kAllPullAtomInfo
must be accessed in a lock, especially now that pullers can be
registered/unregistered.
Test: atest statsd_test, wrote a repro cl.
Bug: 168156854
Bug: 173552790
Change-Id: I47d53a6d9d274bca4c78dbfd87e0097091b7b8cb
Merged-In: I47d53a6d9d274bca4c78dbfd87e0097091b7b8cb
Merged-In: I8bee7a0a6acecc1274d5acc0adb44c5dde8862e4
(cherry picked from commit f9a4bb18ffa1605cced3ee4b161675a0c88941be)
---
cmds/statsd/src/external/StatsPullerManager.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 8a9ec7456e557..99c39f6017b18 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -334,6 +334,7 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
}
int StatsPullerManager::ForceClearPullerCache() {
+ std::lock_guard _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second->ForceClearCache();
@@ -342,6 +343,7 @@ int StatsPullerManager::ForceClearPullerCache() {
}
int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
+ std::lock_guard _l(mLock);
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs);
From ea53b4ef65b4294bcc248bfbc02ed252726e0bab Mon Sep 17 00:00:00 2001
From: Jack Yu
Date: Tue, 19 Jan 2021 11:09:32 -0800
Subject: [PATCH 080/288] Enabled TEMP_NOT_METERED support for carriers
Enabled TEMP_NOT_METERED support for carriers by default.
Bug: 176119724
Test: Manual
Change-Id: Ie0323a354c89abf1a0effa0b65cde3b4d010115e
---
telephony/java/android/telephony/CarrierConfigManager.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ec7a20c5ca3f9..17467ec65fd2e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4647,7 +4647,7 @@ public class CarrierConfigManager {
sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, "");
sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
- sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, false);
+ sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true);
sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0);
}
From ffdc3f06340b81efeab012867ca0c0b94709a96c Mon Sep 17 00:00:00 2001
From: Keun young Park
Date: Mon, 11 Jan 2021 15:30:52 -0800
Subject: [PATCH 081/288] Change target package for com.android.car.shell
- It now overrides shell package itself
Bug: 177099512
Test: Check if shell package has all necessary permissions
Change-Id: I139b47fcb2992e42da020aaac8680ae8dd2efac0
Merged-In: I139b47fcb2992e42da020aaac8680ae8dd2efac0
(cherry picked from commit 2bd010b7e5782b13e19de189fca3e836b83b6e17)
---
data/etc/car/com.android.car.shell.xml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/data/etc/car/com.android.car.shell.xml b/data/etc/car/com.android.car.shell.xml
index 32666c8d9f68b..6132d53b4651c 100644
--- a/data/etc/car/com.android.car.shell.xml
+++ b/data/etc/car/com.android.car.shell.xml
@@ -15,7 +15,9 @@
~ limitations under the License
-->
-
+
+
From 47bc43a414e6c4ec4effd01802591b59410a16ab Mon Sep 17 00:00:00 2001
From: Keun young Park
Date: Fri, 8 Jan 2021 17:21:36 -0800
Subject: [PATCH 082/288] add Shell-package-library to allow product type level
update
- Car proeducts add additional car permissions to shell package for use cases
like CTS
- This CL will allow overriding the Shell package rather than adding one
more package with shared shell uid
Bug: 177099512
Test: Check added package when built in car product.
Change-Id: I6e9103c3275cb2e6df5dc586588eccd7d2ab6b06
Merged-In: I6e9103c3275cb2e6df5dc586588eccd7d2ab6b06
(cherry picked from commit bd16eb5267759cbf0727536d437eee52f7693f33)
---
packages/Shell/Android.bp | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp
index aaaf0448fc317..c51fb56464b08 100644
--- a/packages/Shell/Android.bp
+++ b/packages/Shell/Android.bp
@@ -1,10 +1,14 @@
+// used both for the android_app and android_library
+shell_srcs = ["src/**/*.java",":dumpstate_aidl"]
+shell_static_libs = ["androidx.legacy_legacy-support-v4"]
+
android_app {
name: "Shell",
- srcs: ["src/**/*.java",":dumpstate_aidl"],
+ srcs: shell_srcs,
aidl: {
include_dirs: ["frameworks/native/cmds/dumpstate/binder"],
},
- static_libs: ["androidx.legacy_legacy-support-v4"],
+ static_libs: shell_static_libs,
platform_apis: true,
certificate: "platform",
privileged: true,
@@ -12,3 +16,17 @@ android_app {
include_filter: ["com.android.shell.*"],
},
}
+
+// A library for product type like auto to create a new shell package
+// with product specific permissions.
+android_library {
+ name: "Shell-package-library",
+ srcs: shell_srcs,
+ aidl: {
+ include_dirs: ["frameworks/native/cmds/dumpstate/binder"],
+ },
+ resource_dirs: ["res"],
+ static_libs: shell_static_libs,
+ platform_apis: true,
+ manifest: "AndroidManifest.xml",
+}
From 8cfbb12fd4f6ef909ce1af8668990ad5625a224d Mon Sep 17 00:00:00 2001
From: Jack Yu
Date: Tue, 19 Jan 2021 11:09:32 -0800
Subject: [PATCH 083/288] Enabled TEMP_NOT_METERED support for carriers
Enabled TEMP_NOT_METERED support for carriers by default.
Bug: 176119724
Test: Manual
Merged-In: Ie0323a354c89abf1a0effa0b65cde3b4d010115e
Change-Id: Ie0323a354c89abf1a0effa0b65cde3b4d010115e
---
telephony/java/android/telephony/CarrierConfigManager.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6f10af1940a2c..1762eb7ee7835 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4473,7 +4473,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
- sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, false);
+ sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true);
}
/**
From 344efe80a96ba804533f0fc7c17a02ee8ffaaf2d Mon Sep 17 00:00:00 2001
From: Heemin Seog
Date: Thu, 3 Dec 2020 09:48:33 -0800
Subject: [PATCH 084/288] DO NOT MERGE Do not update bar control target when
application is starting
Handling the bar control target even when the application is starting
causes there to be a flicker in system bar visibility when switching
from one immersive mode app to another immersive mode app.
Bug: 149312861
Test: manual
Change-Id: If5de2ff97d4ad177e5fc58baa82b3f43ed118c2e
---
services/core/java/com/android/server/wm/InsetsPolicy.java | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index fd67db1d5cc9f..a6a44a9f68e69 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -27,6 +27,7 @@ import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import android.annotation.Nullable;
import android.app.StatusBarManager;
@@ -127,6 +128,9 @@ class InsetsPolicy {
/** Updates the target which can control system bars. */
void updateBarControlTarget(@Nullable WindowState focusedWin) {
+ if (focusedWin != null && (focusedWin.mAttrs.type == TYPE_APPLICATION_STARTING)) {
+ return;
+ }
if (mFocusedWin != focusedWin){
abortTransient();
}
From 0eea704510c873d1a6160d57c4d09d11cf080879 Mon Sep 17 00:00:00 2001
From: Marco Ballesio
Date: Wed, 20 Jan 2021 17:26:43 -0800
Subject: [PATCH 085/288] freezer: increase timeout to 5 mins
Some apps might still be performing leftover work after being
backgrounded. Considering possible latencies due to CPU preemption, and
in the effort to reduce the impact the app freezer has on legacy apps by
allowing them to a significant grace period, extend the freezer timeout to
5 mins.
Bug: 178023335
Test: manually verified that freeze time is increased to 5 minutes
Change-Id: I74c14288a66e9a54850c0ea2b70ef3b915c2ca82
---
.../core/java/com/android/server/am/CachedAppOptimizer.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 9eb7c07baaed3..dd09a1c856826 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -133,7 +133,7 @@ public final class CachedAppOptimizer {
static final int REPORT_UNFREEZE_MSG = 4;
//TODO:change this static definition into a configurable flag.
- static final int FREEZE_TIMEOUT_MS = 10000;
+ static final long FREEZE_TIMEOUT_MS = 600000;
static final int DO_FREEZE = 1;
static final int REPORT_UNFREEZE = 2;
From 25aa649c3e29720466aa467fbed2a317fa78379d Mon Sep 17 00:00:00 2001
From: Dan Harms
Date: Wed, 20 Jan 2021 12:27:36 -0800
Subject: [PATCH 086/288] Remove bundled CompanionDeviceSupport permissions
Bug: 160037666
Test: Compiles
Change-Id: I955916f97815fb55a4e701f69a01a975e0d0c205
Merged-In: Ib96198a39c18a0b9c3cf64c0c58c89b1a5b62c19
---
data/etc/car/Android.bp | 7 ------
...com.android.car.companiondevicesupport.xml | 24 -------------------
2 files changed, 31 deletions(-)
delete mode 100644 data/etc/car/com.android.car.companiondevicesupport.xml
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 3c990abaec871..458ac128b196c 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -128,13 +128,6 @@ prebuilt_etc {
filename_from_src: true,
}
-prebuilt_etc {
- name: "privapp_whitelist_com.android.car.companiondevicesupport",
- sub_dir: "permissions",
- src: "com.android.car.companiondevicesupport.xml",
- filename_from_src: true,
-}
-
prebuilt_etc {
name: "privapp_whitelist_com.google.android.car.kitchensink",
sub_dir: "permissions",
diff --git a/data/etc/car/com.android.car.companiondevicesupport.xml b/data/etc/car/com.android.car.companiondevicesupport.xml
deleted file mode 100644
index 2067bab20d3d5..0000000000000
--- a/data/etc/car/com.android.car.companiondevicesupport.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
From 579b74d5cbb08b8dd3461f484cdebcbd9e64c656 Mon Sep 17 00:00:00 2001
From: Yu-Han Yang
Date: Fri, 15 Jan 2021 16:11:40 -0800
Subject: [PATCH 087/288] DO NOT MERGE: Do not inject mock location to chipset
Bug: 177561690
Test: on device
Change-Id: Icafbdf54fe807f8779377b13cb4e4eb265db692e
---
.../com/android/server/location/GnssLocationProvider.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 59a7cc8c04cb5..7d6b4026b6a73 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1047,6 +1047,9 @@ public class GnssLocationProvider implements LocationProviderInterface, InjectNt
}
private void injectBestLocation(Location location) {
+ if (location.isFromMockProvider()) {
+ return;
+ }
int gnssLocationFlags = LOCATION_HAS_LAT_LONG |
(location.hasAltitude() ? LOCATION_HAS_ALTITUDE : 0) |
(location.hasSpeed() ? LOCATION_HAS_SPEED : 0) |
@@ -1141,6 +1144,9 @@ public class GnssLocationProvider implements LocationProviderInterface, InjectNt
}
private void handleUpdateLocation(Location location) {
+ if (location.isFromMockProvider()) {
+ return;
+ }
if (location.hasAccuracy()) {
native_inject_location(location.getLatitude(), location.getLongitude(),
location.getAccuracy());
From 03fb6e49ae0abbf844f080d484efc837bab69c52 Mon Sep 17 00:00:00 2001
From: Hall Liu
Date: Fri, 22 Jan 2021 16:06:50 -0800
Subject: [PATCH 088/288] Allow carriers to configure RTT while roaming
Add a carrier config key that makes RTT supported even if roaming.
Fixes: 168676696
Test: manual
Change-Id: Ied2418cdec57d2d405d4ed0f0227a251ecd626cf
Merged-In: Ied2418cdec57d2d405d4ed0f0227a251ecd626cf
---
.../java/android/telephony/CarrierConfigManager.java | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1762eb7ee7835..3eedce6523105 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3935,6 +3935,13 @@ public class CarrierConfigManager {
public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL =
"network_temp_not_metered_supported_bool";
+ /**
+ * Indicates whether RTT is supported while roaming.
+ * @hide
+ */
+ public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL =
+ "rtt_supported_while_roaming_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -4290,6 +4297,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false);
+ sDefaults.putBoolean(KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
From bbebaf7a10f1974318adfa7c923b068c96d164bf Mon Sep 17 00:00:00 2001
From: Marco Ballesio
Date: Tue, 26 Jan 2021 09:49:23 -0800
Subject: [PATCH 089/288] ActivityManagerService: all users can handle freezer
state in debug builds
Processes whose proxy maps exceed CRASH_AT_SIZE might need to disable
the freezer prior dumping their binding for debug purposes. This happens
on builds with the IS_DEBUGGABLE flag set, so ActivityManager needs to
allow freezer state toggling from any user in this case.
Bug: 178037173
Test: booted device with userdebug, forced interface dumps from all
binder proxies with no crashes.
Change-Id: Ib1f5c7f6ad2b404d56fef566b3d9f17730592762
---
.../core/java/com/android/server/am/ActivityManagerService.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3f4878e380291..0f6c8fe55d144 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -20367,7 +20367,7 @@ public class ActivityManagerService extends IActivityManager.Stub
int callerUid = Binder.getCallingUid();
// Only system can toggle the freezer state
- if (callerUid == SYSTEM_UID) {
+ if (callerUid == SYSTEM_UID || Build.IS_DEBUGGABLE) {
return mOomAdjuster.mCachedAppOptimizer.enableFreezer(enable);
} else {
throw new SecurityException("Caller uid " + callerUid + " cannot set freezer state ");
From 2cd616165c6de4d523637cd84eb0c7490415beb6 Mon Sep 17 00:00:00 2001
From: Dmitry Dementyev
Date: Wed, 9 Dec 2020 16:21:32 -0800
Subject: [PATCH 090/288] Add IMMUTABLE flag to AccountManager pending intents.
Bug: 174189215,177931355
Test: none
Change-Id: I11c81ce4cfb0f51a2fabf92a45d8425647df2121
(cherry picked from commit 7e615a523cd5dae44a7d2b949319f53e6480194e)
---
.../com/android/server/accounts/AccountManagerService.java | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 27c3ff1b4c096..2e04f9c2bcd1b 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3071,7 +3071,8 @@ public class AccountManagerService
.setContentTitle(title)
.setContentText(subtitle)
.setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
- PendingIntent.FLAG_CANCEL_CURRENT, null, user))
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+ null, user))
.build();
installNotification(getCredentialPermissionNotificationId(
account, authTokenType, uid), n, packageName, user.getIdentifier());
@@ -5293,7 +5294,8 @@ public class AccountManagerService
.setContentTitle(String.format(notificationTitleFormat, account.name))
.setContentText(message)
.setContentIntent(PendingIntent.getActivityAsUser(
- mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
+ mContext, 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
null, new UserHandle(userId)))
.build();
installNotification(id, n, packageName, userId);
From 2f5e1b400449eee83ae87e0a5a9d9a509196d3d9 Mon Sep 17 00:00:00 2001
From: Heemin Seog
Date: Tue, 26 Jan 2021 18:10:56 -0800
Subject: [PATCH 091/288] DO NOT MERGE Handle back button for notification
panel
Bug: 178504029
Test: manual
Change-Id: If44048ab1772b85d13aa1f96a23eded896c53883
---
.../NotificationPanelViewController.java | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index fd804c71c9d0a..b83fcf4bf8b5c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -28,6 +28,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.GestureDetector;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -245,6 +246,17 @@ public class NotificationPanelViewController extends OverlayPanelViewController
mNotificationView = (CarNotificationView) LayoutInflater.from(mContext).inflate(
R.layout.notification_center_activity, container,
/* attachToRoot= */ false);
+ mNotificationView.setKeyEventHandler(
+ event -> {
+ if (event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
+ return false;
+ }
+
+ if (event.getAction() == KeyEvent.ACTION_UP && isPanelExpanded()) {
+ toggle();
+ }
+ return true;
+ });
container.addView(mNotificationView);
onNotificationViewInflated();
From 05f7aef1b07665aa10aab535613a8457296279a3 Mon Sep 17 00:00:00 2001
From: Miranda Kephart
Date: Mon, 16 Nov 2020 11:22:13 -0500
Subject: [PATCH 092/288] [DO NOT MERGE] Close screenshot process on user
switched
Currently, we keep the process up even if the user switches,
meaning that in some cases (if the user is switched while the
screenshot UI is up) we will save images to the wrong profile.
This change makes ScreenshotHelper listen for user switches and
close the screenshot service, so that a new screenshot is
guaranteed to be constructed with the correct user's context.
Bug: 170474245
Fix: 170474245
Test: manual -- verified bad state occurs if user switches within
the timeout period, ensured that screenshots work immediately
after switching with this change.
Change-Id: I9d32d0928e6c2bda161d04555438d0dd7afef0ba
(cherry picked from commit 7ef1a5dd1506075507412626f2533283d9520144)
---
.../internal/util/ScreenshotHelper.java | 42 ++++++++++++++-----
1 file changed, 31 insertions(+), 11 deletions(-)
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index a23fc4b57b457..7ee846e9d8c11 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -1,12 +1,15 @@
package com.android.internal.util;
+import static android.content.Intent.ACTION_USER_SWITCHED;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -161,8 +164,21 @@ public class ScreenshotHelper {
private ServiceConnection mScreenshotConnection = null;
private final Context mContext;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mScreenshotLock) {
+ if (ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ resetConnection();
+ }
+ }
+ }
+ };
+
public ScreenshotHelper(Context context) {
mContext = context;
+ IntentFilter filter = new IntentFilter(ACTION_USER_SWITCHED);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
}
/**
@@ -279,9 +295,8 @@ public class ScreenshotHelper {
final Runnable mScreenshotTimeout = () -> {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- mScreenshotService = null;
+ Log.e(TAG, "Timed out before getting screenshot capture response");
+ resetConnection();
notifyScreenshotError();
}
}
@@ -304,11 +319,7 @@ public class ScreenshotHelper {
break;
case SCREENSHOT_MSG_PROCESS_COMPLETE:
synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- mScreenshotService = null;
- }
+ resetConnection();
}
break;
}
@@ -348,9 +359,7 @@ public class ScreenshotHelper {
public void onServiceDisconnected(ComponentName name) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- mScreenshotService = null;
+ resetConnection();
// only log an error if we're still within the timeout period
if (handler.hasCallbacks(mScreenshotTimeout)) {
handler.removeCallbacks(mScreenshotTimeout);
@@ -382,6 +391,17 @@ public class ScreenshotHelper {
}
}
+ /**
+ * Unbinds the current screenshot connection (if any).
+ */
+ private void resetConnection() {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ mScreenshotService = null;
+ }
+ }
+
/**
* Notifies the screenshot service to show an error.
*/
From 334a66342c799018f25c015c54a2c2c017e1ec36 Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Mon, 16 Nov 2020 10:11:12 +0900
Subject: [PATCH 093/288] Test a VPN with an underlying network that does not
yet exist.
This test checks that if a VPN declares an underlying network
that does not exist, the capabilities of that network are applied
to the VPN as soon as the network starts to exist.
Bug: 172870110
Test: test-only change
Change-Id: Icc0701cb4cea7d91f7738c1e426e94cd26686b74
Merged-In: Icc0701cb4cea7d91f7738c1e426e94cd26686b74
---
.../com/android/server/TestNetIdManager.kt | 1 +
.../server/ConnectivityServiceTest.java | 57 ++++++++++++++++++-
2 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
index eb290dc7d24ad..938a694e8ba98 100644
--- a/tests/net/integration/util/com/android/server/TestNetIdManager.kt
+++ b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
@@ -35,4 +35,5 @@ class TestNetIdManager : NetIdManager() {
private val nextId = AtomicInteger(MAX_NET_ID)
override fun reserveNetId() = nextId.decrementAndGet()
override fun releaseNetId(id: Int) = Unit
+ fun peekNextNetId() = nextId.get() - 1
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 385005f90c3be..0c1bf548db6dc 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -336,6 +336,7 @@ public class ConnectivityServiceTest {
private INetworkPolicyListener mPolicyListener;
private WrappedMultinetworkPolicyTracker mPolicyTracker;
private HandlerThread mAlarmManagerThread;
+ private TestNetIdManager mNetIdManager;
@Mock IIpConnectivityMetrics mIpConnectivityMetrics;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@@ -1194,6 +1195,8 @@ public class ConnectivityServiceTest {
@Before
public void setUp() throws Exception {
+ mNetIdManager = new TestNetIdManager();
+
mContext = InstrumentationRegistry.getContext();
MockitoAnnotations.initMocks(this);
@@ -1264,7 +1267,7 @@ public class ConnectivityServiceTest {
final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
- doReturn(new TestNetIdManager()).when(deps).makeNetIdManager();
+ doReturn(mNetIdManager).when(deps).makeNetIdManager();
doReturn(mNetworkStack).when(deps).getNetworkStack();
doReturn(systemProperties).when(deps).getSystemProperties();
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
@@ -5186,6 +5189,58 @@ public class ConnectivityServiceTest {
assertTrue(lp.getDnsServers().containsAll(dnsServers));
}
+ @Test
+ public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN).build();
+
+ mCm.registerNetworkCallback(request, callback);
+
+ // Bring up a VPN that specifies an underlying network that does not exist yet.
+ // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet,
+ // (and doing so is difficult without using reflection) but it's good to test that the code
+ // behaves approximately correctly.
+ final int uid = Process.myUid();
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
+ final ArraySet ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+
+ final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
+ mMockVpn.setNetworkAgent(vpnNetworkAgent);
+ mMockVpn.setUids(ranges);
+ mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
+ vpnNetworkAgent.connect(false);
+ mMockVpn.connect();
+ callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertFalse(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
+
+ // Make that underlying network connect, and expect to see its capabilities immediately
+ // reflected in the VPN's capabilities.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
+ mWiFiNetworkAgent.connect(false);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
+
+ // Disconnect the network, and expect to see the VPN capabilities change accordingly.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ callback.expectCapabilitiesThat(vpnNetworkAgent, (nc) ->
+ nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
+
+ vpnNetworkAgent.disconnect();
+ mCm.unregisterNetworkCallback(callback);
+ }
+
@Test
public void testVpnNetworkActive() throws Exception {
final int uid = Process.myUid();
From 7c4ae16b94ce8f36e4e047ac4c825794d1c21db2 Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Mon, 16 Nov 2020 16:05:44 +0900
Subject: [PATCH 094/288] Simplify MockVpn.
This CL removes four methods in MockVpn by slightly changing the
test code to leverage the actual methods implemented by the
(production) Vpn superclass.
This works because setting mInterface results in
isRunningLocked() returning true, which makes a number of methods
behave as if the VPN is connected (which is what the test
expects).
The more realistic behaviour exposes a minor bug in the treatment
of underlying networks. Add a TODO to fix it.
Bug: 172870110
Test: test-only change
Change-Id: I49421183538ba61ca790af71e309ece36b653bf9
Merged-In: I49421183538ba61ca790af71e309ece36b653bf9
---
.../com/android/server/connectivity/Vpn.java | 3 +-
.../server/ConnectivityServiceTest.java | 53 ++++++++-----------
2 files changed, 25 insertions(+), 31 deletions(-)
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index dbb91a58369dc..af4df1a0f8d16 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -194,7 +194,8 @@ public class Vpn {
@VisibleForTesting protected String mPackage;
private int mOwnerUID;
private boolean mIsPackageTargetingAtLeastQ;
- private String mInterface;
+ @VisibleForTesting
+ protected String mInterface;
private Connection mConnection;
/** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 0c1bf548db6dc..572ea12358ec1 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -319,6 +319,7 @@ public class ConnectivityServiceTest {
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
+ private static final String VPN_IFNAME = "tun10042";
private static final String TEST_PACKAGE_NAME = "com.android.test.package";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
@@ -1033,12 +1034,14 @@ public class ConnectivityServiceTest {
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
userId, mock(KeyStore.class));
+ mConfig = new VpnConfig();
}
public void setNetworkAgent(TestNetworkAgentWrapper agent) {
agent.waitForIdle(TIMEOUT_MS);
mMockNetworkAgent = agent;
mNetworkAgent = agent.getNetworkAgent();
+ mInterface = VPN_IFNAME;
mNetworkCapabilities.set(agent.getNetworkCapabilities());
}
@@ -1059,16 +1062,6 @@ public class ConnectivityServiceTest {
return mMockNetworkAgent.getNetwork().netId;
}
- @Override
- public boolean appliesToUid(int uid) {
- return mConnected; // Trickery to simplify testing.
- }
-
- @Override
- protected boolean isCallerEstablishedOwnerLocked() {
- return mConnected; // Similar trickery
- }
-
@Override
public int getActiveAppVpnType() {
return mVpnType;
@@ -1077,7 +1070,6 @@ public class ConnectivityServiceTest {
private void connect(boolean isAlwaysMetered) {
mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
mConnected = true;
- mConfig = new VpnConfig();
mConfig.isMetered = isAlwaysMetered;
}
@@ -1108,7 +1100,6 @@ public class ConnectivityServiceTest {
public void disconnect() {
mConnected = false;
- mConfig = null;
}
@Override
@@ -1121,18 +1112,6 @@ public class ConnectivityServiceTest {
private synchronized void setVpnInfo(VpnInfo vpnInfo) {
mVpnInfo = vpnInfo;
}
-
- @Override
- public synchronized Network[] getUnderlyingNetworks() {
- if (mUnderlyingNetworks != null) return mUnderlyingNetworks;
-
- return super.getUnderlyingNetworks();
- }
-
- /** Don't override behavior for {@link Vpn#setUnderlyingNetworks}. */
- private synchronized void overrideUnderlyingNetworks(Network[] underlyingNetworks) {
- mUnderlyingNetworks = underlyingNetworks;
- }
}
private void mockVpn(int uid) {
@@ -5210,7 +5189,7 @@ public class ConnectivityServiceTest {
final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
- mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
+ mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
vpnNetworkAgent.connect(false);
mMockVpn.connect();
callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
@@ -5224,8 +5203,17 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
mWiFiNetworkAgent.connect(false);
- callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ // TODO: the callback for the VPN happens before any callbacks are called for the wifi
+ // network that has just connected. There appear to be two issues here:
+ // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for
+ // it returns non-null (which happens very early, during handleRegisterNetworkAgent).
+ // This is not correct because that that point the network is not connected and cannot
+ // pass any traffic.
+ // 2. When a network connects, updateNetworkInfo propagates underlying network capabilities
+ // before rematching networks.
+ // Given that this scenario can't really happen, this is probably fine for now.
callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
.hasTransport(TRANSPORT_VPN));
assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
@@ -5289,7 +5277,7 @@ public class ConnectivityServiceTest {
vpnNetworkAgent.connect(false);
mMockVpn.connect();
- mMockVpn.setUnderlyingNetworks(new Network[0]);
+ mService.setUnderlyingNetworksForVpn(new Network[0]);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
@@ -7249,16 +7237,21 @@ public class ConnectivityServiceTest {
// active
final VpnInfo info = new VpnInfo();
info.ownerUid = Process.myUid();
- info.vpnIface = "interface";
+ info.vpnIface = VPN_IFNAME;
mMockVpn.setVpnInfo(info);
- mMockVpn.overrideUnderlyingNetworks(new Network[] {network});
+
+ final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
+ mMockVpn.setNetworkAgent(vpnNetworkAgent);
+ mMockVpn.connect();
+
+ assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network}));
assertTrue(
"Active VPN permission not applied",
mService.checkConnectivityDiagnosticsPermissions(
Process.myPid(), Process.myUid(), naiWithoutUid,
mContext.getOpPackageName()));
- mMockVpn.overrideUnderlyingNetworks(null);
+ assertTrue(mService.setUnderlyingNetworksForVpn(null));
assertFalse(
"VPN shouldn't receive callback on non-underlying network",
mService.checkConnectivityDiagnosticsPermissions(
From d10299582ae9965c0ff70b5fceb4ce8ee591734e Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Tue, 17 Nov 2020 23:23:58 +0900
Subject: [PATCH 095/288] Increase test coverage for VPN info sent to
NetworkStatsService.
Bug: 172870110
Test: test-only change
Change-Id: I3711b362f31cb92b759e9f5c9d244fb88d9bd5e7
Merged-In: I3711b362f31cb92b759e9f5c9d244fb88d9bd5e7
---
.../server/ConnectivityServiceTest.java | 169 +++++++++++++++---
1 file changed, 149 insertions(+), 20 deletions(-)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 572ea12358ec1..c0293c157c277 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4745,13 +4745,52 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(networkCallback);
}
+ private void assertSameElementsNoDuplicates(T[] expected, T[] actual) {
+ // Easier to implement than a proper "assertSameElements" method that also correctly deals
+ // with duplicates.
+ final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual);
+ assertEquals(msg, expected.length, actual.length);
+ Set expectedSet = new ArraySet<>(Arrays.asList(expected));
+ assertEquals("expected contains duplicates", expectedSet.size(), expected.length);
+ // actual cannot have duplicates because it's the same length and has the same elements.
+ Set actualSet = new ArraySet<>(Arrays.asList(actual));
+ assertEquals(expectedSet, actualSet);
+ }
+
+ private void expectForceUpdateIfaces(Network[] networks, String defaultIface,
+ Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception {
+ ArgumentCaptor networksCaptor = ArgumentCaptor.forClass(Network[].class);
+ ArgumentCaptor vpnInfosCaptor = ArgumentCaptor.forClass(VpnInfo[].class);
+
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(),
+ any(NetworkState[].class), eq(defaultIface), vpnInfosCaptor.capture());
+
+ assertSameElementsNoDuplicates(networksCaptor.getValue(), networks);
+
+ VpnInfo[] infos = vpnInfosCaptor.getValue();
+ if (vpnUid != null) {
+ assertEquals("Should have exactly one VPN:", 1, infos.length);
+ VpnInfo info = infos[0];
+ assertEquals("Unexpected VPN owner:", (int) vpnUid, info.ownerUid);
+ assertEquals("Unexpected VPN interface:", vpnIfname, info.vpnIface);
+ assertSameElementsNoDuplicates(underlyingIfaces, info.underlyingIfaces);
+ } else {
+ assertEquals(0, infos.length);
+ return;
+ }
+ }
+
+ private void expectForceUpdateIfaces(Network[] networks, String defaultIface) throws Exception {
+ expectForceUpdateIfaces(networks, defaultIface, null, null, new String[0]);
+ }
+
@Test
public void testStatsIfacesChanged() throws Exception {
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
- Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
+ final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
+ final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -4762,9 +4801,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false);
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Default network switch should update ifaces.
@@ -4772,32 +4809,24 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
waitForIdle();
assertEquals(wifiLp, mService.getActiveLinkProperties());
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyWifi), any(NetworkState[].class), eq(WIFI_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyWifi, WIFI_IFNAME);
reset(mStatsService);
// Disconnect should update ifaces.
mWiFiNetworkAgent.disconnect();
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class),
- eq(MOBILE_IFNAME), eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Metered change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Captive portal change shouldn't update ifaces
@@ -4811,9 +4840,101 @@ public class ConnectivityServiceTest {
// Roaming change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
+ reset(mStatsService);
+
+ // Test VPNs.
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(VPN_IFNAME);
+
+ final NetworkAgentWrapper vpnNetworkAgent = establishVpnForMyUid(lp);
+ final Network[] cellAndVpn = new Network[] {
+ mCellNetworkAgent.getNetwork(), vpnNetworkAgent.getNetwork()};
+ Network[] cellAndWifi = new Network[] {
+ mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
+
+ // A VPN with default (null) underlying networks sets the underlying network's interfaces...
+ expectForceUpdateIfaces(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME});
+
+ // ...and updates them as the default network switches.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+ final Network[] wifiAndVpn = new Network[] {
+ mWiFiNetworkAgent.getNetwork(), vpnNetworkAgent.getNetwork()};
+ cellAndWifi = new Network[] {
+ mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
+
+ waitForIdle();
+ assertEquals(wifiLp, mService.getActiveLinkProperties());
+ expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{WIFI_IFNAME});
+ reset(mStatsService);
+
+ // A VPN that sets its underlying networks passes the underlying interfaces, and influences
+ // the default interface sent to NetworkStatsService by virtue of applying to the system
+ // server UID (or, in this test, to the test's UID). This is the reason for sending
+ // MOBILE_IFNAME even though the default network is wifi.
+ // TODO: fix this to pass in the actual default network interface. Whether or not the VPN
+ // applies to the system server UID should not have any bearing on network stats.
+ mService.setUnderlyingNetworksForVpn(onlyCell);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME});
+ reset(mStatsService);
+
+ mService.setUnderlyingNetworksForVpn(cellAndWifi);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+ reset(mStatsService);
+
+ // If an underlying network disconnects, that interface should no longer be underlying.
+ // This doesn't actually work because disconnectAndDestroyNetwork only notifies
+ // NetworkStatsService before the underlying network is actually removed. So the underlying
+ // network will only be removed if notifyIfacesChangedForNetworkStats is called again. This
+ // could result in incorrect data usage measurements if the interface used by the
+ // disconnected network is reused by a system component that does not register an agent for
+ // it (e.g., tethering).
+ mCellNetworkAgent.disconnect();
+ waitForIdle();
+ assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork()));
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+
+ // Confirm that we never tell NetworkStatsService that cell is no longer the underlying
+ // network for the VPN...
+ verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class),
+ any(NetworkState[].class), any() /* anyString() doesn't match null */,
+ argThat(infos -> infos[0].underlyingIfaces.length == 1
+ && WIFI_IFNAME.equals(infos[0].underlyingIfaces[0])));
+ verifyNoMoreInteractions(mStatsService);
+ reset(mStatsService);
+
+ // ... but if something else happens that causes notifyIfacesChangedForNetworkStats to be
+ // called again, it does. For example, connect Ethernet, but with a low score, such that it
+ // does not become the default network.
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent.adjustScore(-40);
+ mEthernetNetworkAgent.connect(false);
+ waitForIdle();
+ verify(mStatsService).forceUpdateIfaces(any(Network[].class),
+ any(NetworkState[].class), any() /* anyString() doesn't match null */,
+ argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1
+ && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0])));
+ mEthernetNetworkAgent.disconnect();
+ reset(mStatsService);
+
+ // When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo
+ // does not return the VPN, so CS does not pass it to NetworkStatsService. This causes
+ // NetworkStatsFactory#adjustForTunAnd464Xlat not to attempt any VPN data migration, which
+ // is probably a performance improvement (though it's very unlikely that a VPN would declare
+ // no underlying networks).
+ // Also, for the same reason as above, the active interface passed in is null.
+ mService.setUnderlyingNetworksForVpn(new Network[0]);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
}
@@ -7052,6 +7173,14 @@ public class ConnectivityServiceTest {
return vpnNetworkAgent;
}
+ private TestNetworkAgentWrapper establishVpnForMyUid(LinkProperties lp)
+ throws Exception {
+ final int uid = Process.myUid();
+ final ArraySet ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ return establishVpn(lp, uid, ranges);
+ }
+
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
final PackageInfo packageInfo = new PackageInfo();
if (hasSystemPermission) {
From f3d35fdeaab97b8f2646fbcadcc50413138d369a Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Wed, 18 Nov 2020 22:50:57 +0900
Subject: [PATCH 096/288] Make MockVpn more realistic and easier to use.
MockVpn is very difficult to use because it requires the test
caller keeping track of both the MockVpn object and an
accompanying TestNetworkAgentWrapper.
It's also not very realistic: for example, connect() doesn't
actually connect anything, it just makes it so that if
ConnectivityService tries to update the capabilities, the attempt
will not be ignored. Also, unlike the real code in Vpn, it
connects with empty NetworkCapabilities (in particular, with
empty UID ranges).
Make this easier to use and a bit more realistic by:
- Allowing TestNetworkAgentWrapper to take a "NetworkCapabilities
template" that will form the initial capabilities sent when the
agent registers with ConnectivityService. This allows the VPN
to register its agent with its UID ranges already set, like the
production code does.
- Providing separate methods to register the NetworkAgent and
mark it connected for cases where the test needs to make
changes to the NetworkAgent before connecting (e.g., poking
NetworkMonitor).
- Putting the TestNetworkAgentWrapper inside MockVpn and driving
it through MockVpn's methods. In order not to have too many
wrapper functions (and because we can't delegate like in
Kotlin), there's still an agent() method that returns the
TestNetworkAgentWrapper.
Bug: 172870110
Test: test-only change
Change-Id: I749ff325bc13ac96f512270b86d1f67686eec378
Merged-In: I749ff325bc13ac96f512270b86d1f67686eec378
---
.../ConnectivityServiceIntegrationTest.kt | 5 +-
.../android/server/NetworkAgentWrapper.java | 6 +-
.../server/ConnectivityServiceTest.java | 514 ++++++++----------
3 files changed, 227 insertions(+), 298 deletions(-)
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index c4801aab5c321..344d0c3260c9a 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -195,7 +195,8 @@ class ConnectivityServiceIntegrationTest {
"https://secure.test.android.com",
responseCode = 204, contentLength = 42, redirectUrl = null))
- val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context)
+ val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), null /* ncTemplate */,
+ context)
networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
na.addCapability(NET_CAPABILITY_INTERNET)
@@ -204,4 +205,4 @@ class ConnectivityServiceIntegrationTest {
testCallback.expectAvailableThenValidatedCallbacks(na.network, TEST_TIMEOUT_MS)
assertEquals(2, nsInstrumentation.getRequestUrls().size)
}
-}
\ No newline at end of file
+}
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 0ffafd45613a8..03954de984386 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -69,12 +69,12 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
private Integer mExpectedKeepaliveSlot = null;
- public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
- throws Exception {
+ public NetworkAgentWrapper(int transport, LinkProperties linkProperties,
+ NetworkCapabilities ncTemplate, Context context) throws Exception {
final int type = transportToLegacyType(transport);
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
- mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mNetworkCapabilities.addTransportType(transport);
switch (transport) {
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c0293c157c277..810ccbe252ca0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -606,12 +606,17 @@ public class ConnectivityServiceTest {
private String mRedirectUrl;
TestNetworkAgentWrapper(int transport) throws Exception {
- this(transport, new LinkProperties());
+ this(transport, new LinkProperties(), null);
}
TestNetworkAgentWrapper(int transport, LinkProperties linkProperties)
throws Exception {
- super(transport, linkProperties, mServiceContext);
+ this(transport, linkProperties, null);
+ }
+
+ private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
+ NetworkCapabilities ncTemplate) throws Exception {
+ super(transport, linkProperties, ncTemplate, mServiceContext);
// Waits for the NetworkAgent to be registered, which includes the creation of the
// NetworkMonitor.
@@ -1006,30 +1011,26 @@ public class ConnectivityServiceTest {
}
}
+ private Set uidRangesForUid(int uid) {
+ final ArraySet ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ return ranges;
+ }
+
private static Looper startHandlerThreadAndReturnLooper() {
final HandlerThread handlerThread = new HandlerThread("MockVpnThread");
handlerThread.start();
return handlerThread.getLooper();
}
- private class MockVpn extends Vpn {
- // TODO : the interactions between this mock and the mock network agent are too
- // hard to get right at this moment, because it's unclear in which case which
- // target needs to get a method call or both, and in what order. It's because
- // MockNetworkAgent wants to manage its own NetworkCapabilities, but the Vpn
- // parent class of MockVpn agent wants that responsibility.
- // That being said inside the test it should be possible to make the interactions
- // harder to get wrong with precise speccing, judicious comments, helper methods
- // and a few sprinkled assertions.
-
- private boolean mConnected = false;
+ private class MockVpn extends Vpn implements TestableNetworkCallback.HasNetwork {
// Careful ! This is different from mNetworkAgent, because MockNetworkAgent does
// not inherit from NetworkAgent.
private TestNetworkAgentWrapper mMockNetworkAgent;
- private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
+ private boolean mAgentRegistered = false;
+ private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
private VpnInfo mVpnInfo;
- private Network[] mUnderlyingNetworks;
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
@@ -1037,29 +1038,23 @@ public class ConnectivityServiceTest {
mConfig = new VpnConfig();
}
- public void setNetworkAgent(TestNetworkAgentWrapper agent) {
- agent.waitForIdle(TIMEOUT_MS);
- mMockNetworkAgent = agent;
- mNetworkAgent = agent.getNetworkAgent();
- mInterface = VPN_IFNAME;
- mNetworkCapabilities.set(agent.getNetworkCapabilities());
- }
-
public void setUids(Set uids) {
mNetworkCapabilities.setUids(uids);
- updateCapabilities(null /* defaultNetwork */);
+ updateCapabilitiesInternal(null /* defaultNetwork */, true);
}
public void setVpnType(int vpnType) {
mVpnType = vpnType;
}
+ @Override
+ public Network getNetwork() {
+ return (mMockNetworkAgent == null) ? null : mMockNetworkAgent.getNetwork();
+ }
+
@Override
public int getNetId() {
- if (mMockNetworkAgent == null) {
- return NETID_UNSET;
- }
- return mMockNetworkAgent.getNetwork().netId;
+ return (mMockNetworkAgent == null) ? NETID_UNSET : mMockNetworkAgent.getNetwork().netId;
}
@Override
@@ -1067,39 +1062,94 @@ public class ConnectivityServiceTest {
return mVpnType;
}
- private void connect(boolean isAlwaysMetered) {
- mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
- mConnected = true;
+ private void registerAgent(boolean isAlwaysMetered, Set uids, LinkProperties lp)
+ throws Exception {
+ if (mAgentRegistered) throw new IllegalStateException("already registered");
+ setUids(uids);
mConfig.isMetered = isAlwaysMetered;
+ mInterface = VPN_IFNAME;
+ mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
+ mNetworkCapabilities);
+ mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
+ mAgentRegistered = true;
+ mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
+ mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
}
- public void connectAsAlwaysMetered() {
- connect(true /* isAlwaysMetered */);
+ private void registerAgent(Set uids) throws Exception {
+ registerAgent(false /* isAlwaysMetered */, uids, new LinkProperties());
}
- public void connect() {
- connect(false /* isAlwaysMetered */);
+ private void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
+ mMockNetworkAgent.connect(validated, hasInternet, isStrictMode);
+ }
+
+ private void connect(boolean validated) {
+ mMockNetworkAgent.connect(validated);
+ }
+
+ private TestNetworkAgentWrapper getAgent() {
+ return mMockNetworkAgent;
+ }
+
+ public void establish(LinkProperties lp, int uid, Set ranges, boolean validated,
+ boolean hasInternet, boolean isStrictMode) throws Exception {
+ mNetworkCapabilities.setOwnerUid(uid);
+ mNetworkCapabilities.setAdministratorUids(new int[]{uid});
+ registerAgent(false, ranges, lp);
+ connect(validated, hasInternet, isStrictMode);
+ waitForIdle();
+ }
+
+ public void establish(LinkProperties lp, int uid, Set ranges) throws Exception {
+ establish(lp, uid, ranges, true, true, false);
+ }
+
+ public void establishForMyUid(LinkProperties lp) throws Exception {
+ final int uid = Process.myUid();
+ establish(lp, uid, uidRangesForUid(uid), true, true, false);
+ }
+
+ public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode)
+ throws Exception {
+ final int uid = Process.myUid();
+ establish(new LinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet,
+ isStrictMode);
+ }
+
+ public void establishForMyUid() throws Exception {
+ establishForMyUid(new LinkProperties());
+ }
+
+ public void sendLinkProperties(LinkProperties lp) {
+ mMockNetworkAgent.sendLinkProperties(lp);
+ }
+
+ private NetworkCapabilities updateCapabilitiesInternal(Network defaultNetwork,
+ boolean sendToConnectivityService) {
+ if (!mAgentRegistered) return null;
+ super.updateCapabilities(defaultNetwork);
+ // Because super.updateCapabilities will update the capabilities of the agent but
+ // not the mock agent, the mock agent needs to know about them.
+ copyCapabilitiesToNetworkAgent(sendToConnectivityService);
+ return new NetworkCapabilities(mNetworkCapabilities);
+ }
+
+ private void copyCapabilitiesToNetworkAgent(boolean sendToConnectivityService) {
+ if (null != mMockNetworkAgent) {
+ mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities,
+ sendToConnectivityService);
+ }
}
@Override
public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
- if (!mConnected) return null;
- super.updateCapabilities(defaultNetwork);
- // Because super.updateCapabilities will update the capabilities of the agent but
- // not the mock agent, the mock agent needs to know about them.
- copyCapabilitiesToNetworkAgent();
- return new NetworkCapabilities(mNetworkCapabilities);
- }
-
- private void copyCapabilitiesToNetworkAgent() {
- if (null != mMockNetworkAgent) {
- mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities,
- false /* sendToConnectivityService */);
- }
+ return updateCapabilitiesInternal(defaultNetwork, false);
}
public void disconnect() {
- mConnected = false;
+ if (mMockNetworkAgent != null) mMockNetworkAgent.disconnect();
+ mAgentRegistered = false;
}
@Override
@@ -1305,6 +1355,9 @@ public class ConnectivityServiceTest {
mEthernetNetworkAgent.disconnect();
mEthernetNetworkAgent = null;
}
+ mMockVpn.disconnect();
+ waitForIdle();
+
FakeSettingsProvider.clearSettingsProvider();
mCsHandlerThread.quitSafely();
@@ -3180,20 +3233,12 @@ public class ConnectivityServiceTest {
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
- final int uid = Process.myUid();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- defaultNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+ mMockVpn.establishForMyUid();
+ defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- vpnNetworkAgent.disconnect();
- defaultNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ mMockVpn.disconnect();
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
}
@@ -4847,9 +4892,10 @@ public class ConnectivityServiceTest {
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(VPN_IFNAME);
- final NetworkAgentWrapper vpnNetworkAgent = establishVpnForMyUid(lp);
+ mMockVpn.establishForMyUid(lp);
+
final Network[] cellAndVpn = new Network[] {
- mCellNetworkAgent.getNetwork(), vpnNetworkAgent.getNetwork()};
+ mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
Network[] cellAndWifi = new Network[] {
mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
@@ -4862,7 +4908,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.connect(false);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
final Network[] wifiAndVpn = new Network[] {
- mWiFiNetworkAgent.getNetwork(), vpnNetworkAgent.getNetwork()};
+ mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
cellAndWifi = new Network[] {
mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
@@ -5301,22 +5347,13 @@ public class ConnectivityServiceTest {
// Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet,
// (and doing so is difficult without using reflection) but it's good to test that the code
// behaves approximately correctly.
- final int uid = Process.myUid();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
-
+ mMockVpn.establishForMyUid(false, true, false);
final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
- vpnNetworkAgent.connect(false);
- mMockVpn.connect();
- callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
- assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ callback.expectAvailableCallbacksUnvalidated(mMockVpn);
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_VPN));
- assertFalse(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_WIFI));
// Make that underlying network connect, and expect to see its capabilities immediately
@@ -5333,20 +5370,20 @@ public class ConnectivityServiceTest {
// 2. When a network connects, updateNetworkInfo propagates underlying network capabilities
// before rematching networks.
// Given that this scenario can't really happen, this is probably fine for now.
- callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_VPN));
- assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_WIFI));
// Disconnect the network, and expect to see the VPN capabilities change accordingly.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
- callback.expectCapabilitiesThat(vpnNetworkAgent, (nc) ->
+ callback.expectCapabilitiesThat(mMockVpn, (nc) ->
nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
mCm.unregisterNetworkCallback(callback);
}
@@ -5384,42 +5421,38 @@ public class ConnectivityServiceTest {
vpnNetworkCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
+ final Set ranges = uidRangesForUid(uid);
+ mMockVpn.registerAgent(ranges);
+
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
assertFalse(NetworkMonitorUtils.isValidationRequired(
- vpnNetworkAgent.getNetworkCapabilities()));
- vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
+ mMockVpn.getAgent().getNetworkCapabilities()));
+ mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */);
- vpnNetworkAgent.connect(false);
- mMockVpn.connect();
+ mMockVpn.connect(false);
mService.setUnderlyingNetworksForVpn(new Network[0]);
- genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
- defaultCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ vpnNetworkCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, nc -> null == nc.getUids());
- defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, nc -> null == nc.getUids());
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
ranges.clear();
- vpnNetworkAgent.setUids(ranges);
+ mMockVpn.setUids(ranges);
- genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
// TODO : The default network callback should actually get a LOST call here (also see the
// comment below for AVAILABLE). This is because ConnectivityService does not look at UID
@@ -5427,19 +5460,18 @@ public class ConnectivityServiceTest {
// can't currently update their UIDs without disconnecting, so this does not matter too
// much, but that is the reason the test here has to check for an update to the
// capabilities instead of the expected LOST then AVAILABLE.
- defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
- vpnNetworkAgent.setUids(ranges);
- genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksValidated(mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+ vpnNetworkCallback.expectAvailableCallbacksValidated(mMockVpn);
// TODO : Here like above, AVAILABLE would be correct, but because this can't actually
// happen outside of the test, ConnectivityService does not rematch callbacks.
- defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
mWiFiNetworkAgent.disconnect();
@@ -5449,13 +5481,13 @@ public class ConnectivityServiceTest {
vpnNetworkCallback.assertNoCallback();
defaultCallback.assertNoCallback();
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
- genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
- defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
assertEquals(null, mCm.getActiveNetwork());
mCm.unregisterNetworkCallback(genericNetworkCallback);
@@ -5477,20 +5509,13 @@ public class ConnectivityServiceTest {
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- mMockVpn.connect();
defaultCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
defaultCallback.assertNoCallback();
mCm.unregisterNetworkCallback(defaultCallback);
@@ -5509,21 +5534,14 @@ public class ConnectivityServiceTest {
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */,
false /* isStrictMode */);
- mMockVpn.connect();
- defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- vpnNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ mMockVpn.disconnect();
+ defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(defaultCallback);
@@ -5541,44 +5559,36 @@ public class ConnectivityServiceTest {
callback.assertNoCallback();
// Bring up a VPN that has the INTERNET capability, initially unvalidated.
- final int uid = Process.myUid();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */,
+ mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */,
false /* isStrictMode */);
- mMockVpn.connect();
// Even though the VPN is unvalidated, it becomes the default network for our app.
- callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mMockVpn);
callback.assertNoCallback();
- assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
- assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore());
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertTrue(mMockVpn.getAgent().getScore() > mEthernetNetworkAgent.getScore());
+ assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, mMockVpn.getAgent().getScore());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
- NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
assertFalse(NetworkMonitorUtils.isValidationRequired(
- vpnNetworkAgent.getNetworkCapabilities()));
+ mMockVpn.getAgent().getNetworkCapabilities()));
assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
- vpnNetworkAgent.getNetworkCapabilities()));
+ mMockVpn.getAgent().getNetworkCapabilities()));
// Pretend that the VPN network validates.
- vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
- vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */);
+ mMockVpn.getAgent().mNetworkMonitor.forceReevaluation(Process.myUid());
// Expect to see the validated capability, but no other changes, because the VPN is already
// the default network for the app.
- callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mMockVpn);
callback.assertNoCallback();
- vpnNetworkAgent.disconnect();
- callback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ mMockVpn.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mMockVpn);
callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
}
@@ -5600,21 +5610,15 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mCellNetworkAgent.connect(true);
- final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- vpnNetworkCallback.expectAvailableCallbacks(vpnNetworkAgent.getNetwork(),
+ vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(),
false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent.getNetwork(), TIMEOUT_MS,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn.getNetwork(), TIMEOUT_MS,
nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED));
- final NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertTrue(nc.hasTransport(TRANSPORT_VPN));
assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5636,18 +5640,11 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
- nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertTrue(nc.hasTransport(TRANSPORT_VPN));
assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5664,7 +5661,7 @@ public class ConnectivityServiceTest {
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5678,7 +5675,7 @@ public class ConnectivityServiceTest {
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5688,7 +5685,7 @@ public class ConnectivityServiceTest {
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5696,27 +5693,27 @@ public class ConnectivityServiceTest {
// Remove NOT_SUSPENDED from the only network and observe VPN is now suspended.
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
// Add NOT_SUSPENDED again and observe VPN is no longer suspended.
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
// Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
mService.setUnderlyingNetworksForVpn(
new Network[] { mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5726,7 +5723,7 @@ public class ConnectivityServiceTest {
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5739,7 +5736,7 @@ public class ConnectivityServiceTest {
// Stop using WiFi. The VPN is suspended again.
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5753,7 +5750,7 @@ public class ConnectivityServiceTest {
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5764,14 +5761,14 @@ public class ConnectivityServiceTest {
// Disconnect cell. Receive update without even removing the dead network from the
// underlying networks – it's dead anyway. Not metered any more.
mCellNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED));
// Disconnect wifi too. No underlying networks means this is now metered.
mWiFiNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5792,18 +5789,11 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
- nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertTrue(nc.hasTransport(TRANSPORT_VPN));
assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5815,7 +5805,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5825,7 +5815,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5837,7 +5827,7 @@ public class ConnectivityServiceTest {
// Disconnect wifi too. Now we have no default network.
mWiFiNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5880,18 +5870,10 @@ public class ConnectivityServiceTest {
assertTrue(mCm.isActiveNetworkMetered());
// Connect VPN network. By default it is using current default network (Cell).
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- final int uid = Process.myUid();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- waitForIdle();
+ mMockVpn.establishForMyUid();
+
// Ensure VPN is now the active network.
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// Expect VPN to be metered.
assertTrue(mCm.isActiveNetworkMetered());
@@ -5902,7 +5884,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.connect(true);
waitForIdle();
// VPN should still be the active network.
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// Expect VPN to be unmetered as it should now be using WiFi (new default).
assertFalse(mCm.isActiveNetworkMetered());
@@ -5920,7 +5902,6 @@ public class ConnectivityServiceTest {
// VPN without any underlying networks is treated as metered.
assertTrue(mCm.isActiveNetworkMetered());
- vpnNetworkAgent.disconnect();
mMockVpn.disconnect();
}
@@ -5941,18 +5922,10 @@ public class ConnectivityServiceTest {
assertFalse(mCm.isActiveNetworkMetered());
// Connect VPN network.
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- final int uid = Process.myUid();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- waitForIdle();
+ mMockVpn.establishForMyUid();
+
// Ensure VPN is now the active network.
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is using Cell
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
@@ -5992,7 +5965,6 @@ public class ConnectivityServiceTest {
// VPN without underlying networks is treated as metered.
assertTrue(mCm.isActiveNetworkMetered());
- vpnNetworkAgent.disconnect();
mMockVpn.disconnect();
}
@@ -6007,17 +5979,11 @@ public class ConnectivityServiceTest {
assertFalse(mCm.isActiveNetworkMetered());
// Connect VPN network.
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- final int uid = Process.myUid();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connectAsAlwaysMetered();
+ mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUid(Process.myUid()),
+ new LinkProperties());
+ mMockVpn.connect(true);
waitForIdle();
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is tracking current platform default (WiFi).
mService.setUnderlyingNetworksForVpn(null);
@@ -6041,7 +6007,7 @@ public class ConnectivityServiceTest {
assertTrue(mCm.isActiveNetworkMetered());
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
}
@Test
@@ -6766,34 +6732,21 @@ public class ConnectivityServiceTest {
waitForIdle();
assertNull(mService.getProxyForNetwork(null));
- // Set up a VPN network with a proxy
- final int uid = Process.myUid();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setUids(ranges);
+ // Connect a VPN network with a proxy.
LinkProperties testLinkProperties = new LinkProperties();
testLinkProperties.setHttpProxy(testProxyInfo);
- vpnNetworkAgent.sendLinkProperties(testLinkProperties);
- waitForIdle();
-
- // Connect to VPN with proxy
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- waitForIdle();
+ mMockVpn.establishForMyUid(testLinkProperties);
// Test that the VPN network returns a proxy, and the WiFi does not.
- assertEquals(testProxyInfo, mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
+ assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork()));
assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
assertNull(mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
// Test that the VPN network returns no proxy when it is set to null.
testLinkProperties.setHttpProxy(null);
- vpnNetworkAgent.sendLinkProperties(testLinkProperties);
+ mMockVpn.sendLinkProperties(testLinkProperties);
waitForIdle();
- assertNull(mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
+ assertNull(mService.getProxyForNetwork(mMockVpn.getNetwork()));
assertNull(mService.getProxyForNetwork(null));
// Set WiFi proxy and check that the vpn proxy is still null.
@@ -6804,7 +6757,7 @@ public class ConnectivityServiceTest {
// Disconnect from VPN and check that the active network, which is now the WiFi, has the
// correct proxy setting.
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
waitForIdle();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
@@ -6819,7 +6772,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+ mMockVpn.establish(lp, VPN_UID, vpnRange);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
@@ -6829,7 +6782,7 @@ public class ConnectivityServiceTest {
assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID);
assertTrue(mService.mPermissionMonitor.getVpnUidRanges("tun0").equals(vpnRange));
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
waitForIdle();
// Disconnected VPN should have interface rules removed
@@ -6846,8 +6799,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
- lp, Process.SYSTEM_UID, vpnRange);
+ mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
// Legacy VPN should not have interface rules set up
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6862,8 +6814,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
- lp, Process.SYSTEM_UID, vpnRange);
+ mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
// IPv6 unreachable route should not be misinterpreted as a default route
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6877,7 +6828,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+ mMockVpn.establish(lp, VPN_UID, vpnRange);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
@@ -6889,7 +6840,7 @@ public class ConnectivityServiceTest {
reset(mMockNetd);
InOrder inOrder = inOrder(mMockNetd);
lp.setInterfaceName("tun1");
- vpnNetworkAgent.sendLinkProperties(lp);
+ mMockVpn.sendLinkProperties(lp);
waitForIdle();
// VPN handover (switch to a new interface) should result in rules being updated (old rules
// removed first, then new rules added)
@@ -6902,7 +6853,7 @@ public class ConnectivityServiceTest {
lp = new LinkProperties();
lp.setInterfaceName("tun1");
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun1"));
- vpnNetworkAgent.sendLinkProperties(lp);
+ mMockVpn.sendLinkProperties(lp);
waitForIdle();
// VPN not routing everything should no longer have interface filtering rules
verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture());
@@ -6913,7 +6864,7 @@ public class ConnectivityServiceTest {
lp.setInterfaceName("tun1");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
- vpnNetworkAgent.sendLinkProperties(lp);
+ mMockVpn.sendLinkProperties(lp);
waitForIdle();
// Back to routing all IPv6 traffic should have filtering rules
verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture());
@@ -6928,8 +6879,7 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID,
- Collections.singleton(vpnRange));
+ mMockVpn.establish(lp, VPN_UID, Collections.singleton(vpnRange));
reset(mMockNetd);
InOrder inOrder = inOrder(mMockNetd);
@@ -6938,7 +6888,7 @@ public class ConnectivityServiceTest {
final Set newRanges = new HashSet<>(Arrays.asList(
new UidRange(vpnRange.start, APP1_UID - 1),
new UidRange(APP1_UID + 1, vpnRange.stop)));
- vpnNetworkAgent.setUids(newRanges);
+ mMockVpn.setUids(newRanges);
waitForIdle();
ArgumentCaptor uidCaptor = ArgumentCaptor.forClass(int[].class);
@@ -7079,7 +7029,7 @@ public class ConnectivityServiceTest {
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
final Set vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- establishVpn(new LinkProperties(), vpnOwnerUid, vpnRange);
+ mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
mMockVpn.setVpnType(vpnType);
final VpnInfo vpnInfo = new VpnInfo();
@@ -7160,27 +7110,6 @@ public class ConnectivityServiceTest {
mService.getConnectionOwnerUid(getTestConnectionInfo());
}
- private TestNetworkAgentWrapper establishVpn(
- LinkProperties lp, int ownerUid, Set vpnRange) throws Exception {
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp);
- vpnNetworkAgent.getNetworkCapabilities().setOwnerUid(ownerUid);
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(vpnRange);
- vpnNetworkAgent.connect(true);
- waitForIdle();
- return vpnNetworkAgent;
- }
-
- private TestNetworkAgentWrapper establishVpnForMyUid(LinkProperties lp)
- throws Exception {
- final int uid = Process.myUid();
- final ArraySet ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- return establishVpn(lp, uid, ranges);
- }
-
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
final PackageInfo packageInfo = new PackageInfo();
if (hasSystemPermission) {
@@ -7360,7 +7289,6 @@ public class ConnectivityServiceTest {
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
// setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be
// active
@@ -7369,9 +7297,11 @@ public class ConnectivityServiceTest {
info.vpnIface = VPN_IFNAME;
mMockVpn.setVpnInfo(info);
- final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
+ mMockVpn.establishForMyUid();
+ waitForIdle();
+
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network}));
assertTrue(
@@ -7401,8 +7331,6 @@ public class ConnectivityServiceTest {
Manifest.permission.ACCESS_FINE_LOCATION);
mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
- // Disconnect mock vpn so the uid check on NetworkAgentInfo is tested
- mMockVpn.disconnect();
assertTrue(
"NetworkCapabilities administrator uid permission not applied",
mService.checkConnectivityDiagnosticsPermissions(
From d2adbfbf40aaf5d533607fe71e93d6ff6cba2ba1 Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Thu, 19 Nov 2020 23:20:55 +0900
Subject: [PATCH 097/288] Make testVpnNetworkActive more deterministic.
This test is a bit brittle because it sets the underlying
networks while the VPN is undergoing validation by
NetworkMonitor. The test does attempt to disable validation,
but that's not actually possible - the only thing that's possible
is to tell NetworkMonitor to validate immediately without sending
any probes. So the underlying network change races with the
validation. I'm not sure why the test isn't flaky. It might be
because both the network change and the validation result in a
capabilities change, and the test expects "a capabilities change"
without expressing what change that should be.
Make this a bit more predictable by ensuring that the network
validates before the underlying networks are set.
This is useful because an upcoming CL will change the way
underlying network capabilities are propagated. With this test
CL, both the old and the new code pass.
Bug: 172870110
Test: test-only change
Change-Id: I319858228e8d097c0b60a107029f296385f91269
Merged-In: I319858228e8d097c0b60a107029f296385f91269
---
.../android/server/ConnectivityServiceTest.java | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 810ccbe252ca0..bb39af9a1b9e9 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5423,6 +5423,7 @@ public class ConnectivityServiceTest {
final Set ranges = uidRangesForUid(uid);
mMockVpn.registerAgent(ranges);
+ mService.setUnderlyingNetworksForVpn(new Network[0]);
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
@@ -5431,19 +5432,12 @@ public class ConnectivityServiceTest {
mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */);
mMockVpn.connect(false);
- mService.setUnderlyingNetworksForVpn(new Network[0]);
- genericNetworkCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
+ genericNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
- defaultCallback.expectAvailableCallbacksUnvalidated(mMockVpn);
- assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
-
- genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
- genericNotVpnNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, nc -> null == nc.getUids());
- defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
ranges.clear();
From 1a05734c87f9e3ce0cbd33ea1f13ee7a27c57f01 Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Wed, 25 Nov 2020 22:59:08 +0900
Subject: [PATCH 098/288] Test passing an underlying network array with null
network in it.
Current code treats these nulls as if they weren't there.
Bug: 172870110
Test: test-only change
Change-Id: Id4632e1b004c09910b4b7613f7233d2c19e2f0ac
Merged-In: Id4632e1b004c09910b4b7613f7233d2c19e2f0ac
---
.../server/ConnectivityServiceTest.java | 27 ++++++++++++++++---
1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index bb39af9a1b9e9..9945853c5a6f9 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4896,8 +4896,6 @@ public class ConnectivityServiceTest {
final Network[] cellAndVpn = new Network[] {
mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
- Network[] cellAndWifi = new Network[] {
- mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
// A VPN with default (null) underlying networks sets the underlying network's interfaces...
expectForceUpdateIfaces(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
@@ -4907,10 +4905,13 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+ final Network[] onlyNull = new Network[]{null};
final Network[] wifiAndVpn = new Network[] {
mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
- cellAndWifi = new Network[] {
+ final Network[] cellAndWifi = new Network[] {
mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
+ final Network[] cellNullAndWifi = new Network[] {
+ mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()};
waitForIdle();
assertEquals(wifiLp, mService.getActiveLinkProperties());
@@ -4936,6 +4937,13 @@ public class ConnectivityServiceTest {
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
reset(mStatsService);
+ // Null underlying networks are ignored.
+ mService.setUnderlyingNetworksForVpn(cellNullAndWifi);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+ reset(mStatsService);
+
// If an underlying network disconnects, that interface should no longer be underlying.
// This doesn't actually work because disconnectAndDestroyNetwork only notifies
// NetworkStatsService before the underlying network is actually removed. So the underlying
@@ -4970,6 +4978,7 @@ public class ConnectivityServiceTest {
argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1
&& WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0])));
mEthernetNetworkAgent.disconnect();
+ waitForIdle();
reset(mStatsService);
// When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo
@@ -4982,6 +4991,18 @@ public class ConnectivityServiceTest {
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
+
+ // Specifying only a null underlying network is the same as no networks.
+ mService.setUnderlyingNetworksForVpn(onlyNull);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, null);
+ reset(mStatsService);
+
+ // Specifying networks that are all disconnected is the same as specifying no networks.
+ mService.setUnderlyingNetworksForVpn(onlyCell);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, null);
+ reset(mStatsService);
}
@Test
From 4a66aedf09ccdf19d302a5cff6dec5ec9da076bf Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Wed, 6 Jan 2021 01:36:07 +0900
Subject: [PATCH 099/288] Improve testing of CONNECTIVITY_ACTION broadcasts.
We currently test CONNECTIVITY_ACTION broadcasts by directly
registering BroadcastReceivers with BroadcastInterceptingContext,
and making the receivers unregister themselves when all the
broadcasts they expect have been received.
This works for current test cases, but does not work if anything
registers another receiver for CONNECTIVITY_ACTION. In that case,
when we unregister the receiver in the receiver's onReceive
method, BroadcastInterceptingContext will throw a
ConcurrentModificationException because the list of receivers is
being modified during iteration.
Fix this by adding an ExpectedBroadcast class that stores the
receiver and unregisters the receiver only when the test checks
that the broadcast was received, which happens after the receiver
runs. This is easier to use and also guarantees that the receiver
is unregistered even if the test is expecting that the broadcast
is never fired. Accordingly, remove mRegisteredReceivers and the
code that uses it; it's no longer necessary now that
ExpectedBroadcast always unregisters its receivers.
Also add a convenience expectConnectivityAction method to expect
a CONNECTIVITY_ACTION broadcast with specific contents. This
makes the test easier to read and more detailed. Convert some
existing tests to this method.
While I'm at it, fix a test that was using "mCellNetworkAgent" to
represent a wifi network.
R backport notes: added import for NetworkInfo.DetailedState.
That was added in aosp/1527378, which is impractical to backport.
Bug: 172870110
Test: test-only change
Change-Id: Ibada8b4215625e1016d9fd170526206920af76f5
Merged-In: Ibada8b4215625e1016d9fd170526206920af76f5
---
.../server/ConnectivityServiceTest.java | 335 ++++++++++--------
1 file changed, 180 insertions(+), 155 deletions(-)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 9945853c5a6f9..6cbcb6784ddd6 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -170,6 +170,7 @@ import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
@@ -274,13 +275,16 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -384,9 +388,6 @@ public class ConnectivityServiceTest {
private class MockContext extends BroadcastInterceptingContext {
private final MockContentResolver mContentResolver;
- // Contains all registered receivers since this object was created. Useful to clear
- // them when needed, as BroadcastInterceptingContext does not provide this facility.
- private final List mRegisteredReceivers = new ArrayList<>();
@Spy private Resources mResources;
private final LinkedBlockingQueue mStartedActivities = new LinkedBlockingQueue<>();
@@ -504,19 +505,6 @@ public class ConnectivityServiceTest {
public void setPermission(String permission, Integer granted) {
mMockedPermissions.put(permission, granted);
}
-
- @Override
- public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
- mRegisteredReceivers.add(receiver);
- return super.registerReceiver(receiver, filter);
- }
-
- public void clearRegisteredReceivers() {
- // super.unregisterReceiver is a no-op for receivers that are not registered (because
- // they haven't been registered or because they have already been unregistered).
- // For the same reason, don't bother clearing mRegisteredReceivers.
- for (final BroadcastReceiver rcv : mRegisteredReceivers) unregisterReceiver(rcv);
- }
}
private void waitForIdle() {
@@ -545,10 +533,10 @@ public class ConnectivityServiceTest {
}
// Bring up a network that we can use to send messages to ConnectivityService.
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
Network n = mWiFiNetworkAgent.getNetwork();
assertNotNull(n);
@@ -565,10 +553,10 @@ public class ConnectivityServiceTest {
@Ignore
public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception {
// Bring up a network that we can use to send messages to ConnectivityService.
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
Network n = mWiFiNetworkAgent.getNetwork();
assertNotNull(n);
@@ -1422,29 +1410,79 @@ public class ConnectivityServiceTest {
}
/**
- * Return a ConditionVariable that opens when {@code count} numbers of CONNECTIVITY_ACTION
- * broadcasts are received.
+ * Class to simplify expecting broadcasts using BroadcastInterceptingContext.
+ * Ensures that the receiver is unregistered after the expected broadcast is received. This
+ * cannot be done in the BroadcastReceiver itself because BroadcastInterceptingContext runs
+ * the receivers' receive method while iterating over the list of receivers, and unregistering
+ * the receiver during iteration throws ConcurrentModificationException.
*/
- private ConditionVariable registerConnectivityBroadcast(final int count) {
+ private class ExpectedBroadcast extends CompletableFuture {
+ private final BroadcastReceiver mReceiver;
+
+ ExpectedBroadcast(BroadcastReceiver receiver) {
+ mReceiver = receiver;
+ }
+
+ public Intent expectBroadcast(int timeoutMs) throws Exception {
+ try {
+ return get(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ fail("Expected broadcast not received after " + timeoutMs + " ms");
+ return null;
+ } finally {
+ mServiceContext.unregisterReceiver(mReceiver);
+ }
+ }
+
+ public Intent expectBroadcast() throws Exception {
+ return expectBroadcast(TIMEOUT_MS);
+ }
+
+ public void expectNoBroadcast(int timeoutMs) throws Exception {
+ waitForIdle();
+ try {
+ final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS);
+ fail("Unexpected broadcast: " + intent.getAction());
+ } catch (TimeoutException expected) {
+ } finally {
+ mServiceContext.unregisterReceiver(mReceiver);
+ }
+ }
+ }
+
+ /** Expects that {@code count} CONNECTIVITY_ACTION broadcasts are received. */
+ private ExpectedBroadcast registerConnectivityBroadcast(final int count) {
return registerConnectivityBroadcastThat(count, intent -> true);
}
- private ConditionVariable registerConnectivityBroadcastThat(final int count,
+ private ExpectedBroadcast registerConnectivityBroadcastThat(final int count,
@NonNull final Predicate filter) {
- final ConditionVariable cv = new ConditionVariable();
final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION);
+ // AtomicReference allows receiver to access expected even though it is constructed later.
+ final AtomicReference expectedRef = new AtomicReference<>();
final BroadcastReceiver receiver = new BroadcastReceiver() {
- private int remaining = count;
- public void onReceive(Context context, Intent intent) {
- if (!filter.test(intent)) return;
- if (--remaining == 0) {
- cv.open();
- mServiceContext.unregisterReceiver(this);
- }
- }
- };
+ private int mRemaining = count;
+ public void onReceive(Context context, Intent intent) {
+ final int type = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1);
+ final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO);
+ Log.d(TAG, "Received CONNECTIVITY_ACTION type=" + type + " ni=" + ni);
+ if (!filter.test(intent)) return;
+ if (--mRemaining == 0) {
+ expectedRef.get().complete(intent);
+ }
+ }
+ };
+ final ExpectedBroadcast expected = new ExpectedBroadcast(receiver);
+ expectedRef.set(expected);
mServiceContext.registerReceiver(receiver, intentFilter);
- return cv;
+ return expected;
+ }
+
+ private ExpectedBroadcast expectConnectivityAction(int type, NetworkInfo.DetailedState state) {
+ return registerConnectivityBroadcastThat(1, intent ->
+ type == intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) && state.equals(
+ ((NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO))
+ .getDetailedState()));
}
@Test
@@ -1468,10 +1506,9 @@ public class ConnectivityServiceTest {
// Connect the cell agent and wait for the connected broadcast.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_SUPL);
- final ConditionVariable cv1 = registerConnectivityBroadcastThat(1,
- intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
mCellNetworkAgent.connect(true);
- waitFor(cv1);
+ b.expectBroadcast();
// Build legacy request for SUPL.
final NetworkCapabilities legacyCaps = new NetworkCapabilities();
@@ -1481,27 +1518,17 @@ public class ConnectivityServiceTest {
ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST);
// File request, withdraw it and make sure no broadcast is sent
- final ConditionVariable cv2 = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.requestNetwork(legacyRequest, callback);
callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
mCm.unregisterNetworkCallback(callback);
- assertFalse(cv2.block(800)); // 800ms long enough to at least flake if this is sent
- // As the broadcast did not fire, the receiver was not unregistered. Do this now.
- mServiceContext.clearRegisteredReceivers();
+ b.expectNoBroadcast(800); // 800ms long enough to at least flake if this is sent
- // Disconnect the network and expect mobile disconnected broadcast. Use a small hack to
- // check that has been sent.
- final AtomicBoolean vanillaAction = new AtomicBoolean(false);
- final ConditionVariable cv3 = registerConnectivityBroadcastThat(1, intent -> {
- if (intent.getAction().equals(CONNECTIVITY_ACTION)) {
- vanillaAction.set(true);
- }
- return !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected();
- });
+ // Disconnect the network and expect mobile disconnected broadcast.
+ b = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
mCellNetworkAgent.disconnect();
- waitFor(cv3);
- assertTrue(vanillaAction.get());
+ b.expectBroadcast();
}
@Test
@@ -1512,9 +1539,9 @@ public class ConnectivityServiceTest {
assertNull(mCm.getActiveNetworkInfo());
assertNull(mCm.getActiveNetwork());
// Test bringing up validated cellular.
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
assertLength(2, mCm.getAllNetworks());
assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
@@ -1522,9 +1549,9 @@ public class ConnectivityServiceTest {
assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) ||
mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork()));
// Test bringing up validated WiFi.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
assertLength(2, mCm.getAllNetworks());
assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
@@ -1539,9 +1566,9 @@ public class ConnectivityServiceTest {
assertLength(1, mCm.getAllNetworks());
assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork());
// Test WiFi disconnect.
- cv = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyNoNetwork();
}
@@ -1549,9 +1576,9 @@ public class ConnectivityServiceTest {
public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
// Test bringing up unvalidated WiFi
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up unvalidated cellular
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
@@ -1564,19 +1591,19 @@ public class ConnectivityServiceTest {
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up validated cellular
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test cellular disconnect.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mCellNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test WiFi disconnect.
- cv = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyNoNetwork();
}
@@ -1584,25 +1611,25 @@ public class ConnectivityServiceTest {
public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
// Test bringing up unvalidated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mCellNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test WiFi disconnect.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test cellular disconnect.
- cv = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
mCellNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyNoNetwork();
}
@@ -1610,24 +1637,24 @@ public class ConnectivityServiceTest {
public void testUnlingeringDoesNotValidate() throws Exception {
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
// Test cellular disconnect.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mCellNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Unlingering a network should not cause it to be marked as validated.
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
@@ -1638,25 +1665,25 @@ public class ConnectivityServiceTest {
public void testCellularOutscoresWeakWifi() throws Exception {
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up validated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test WiFi getting really weak.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.adjustScore(-11);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test WiFi restoring signal strength.
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.adjustScore(11);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
}
@@ -1674,9 +1701,9 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.expectDisconnected();
// Test bringing up validated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- final ConditionVariable cv = registerConnectivityBroadcast(1);
+ final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up unvalidated cellular.
// Expect it to be torn down because it could never be the highest scoring network
@@ -1693,33 +1720,33 @@ public class ConnectivityServiceTest {
public void testCellularFallback() throws Exception {
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test bringing up validated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Reevaluate WiFi (it'll instantly fail DNS).
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
mCm.reportBadNetwork(mWiFiNetworkAgent.getNetwork());
// Should quickly fall back to Cellular.
- waitFor(cv);
+ b.expectBroadcast();
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Reevaluate cellular (it'll instantly fail DNS).
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
mCm.reportBadNetwork(mCellNetworkAgent.getNetwork());
// Should quickly fall back to WiFi.
- waitFor(cv);
+ b.expectBroadcast();
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
@@ -1731,23 +1758,23 @@ public class ConnectivityServiceTest {
public void testWiFiFallback() throws Exception {
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mWiFiNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Reevaluate cellular (it'll instantly fail DNS).
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
mCm.reportBadNetwork(mCellNetworkAgent.getNetwork());
// Should quickly fall back to WiFi.
- waitFor(cv);
+ b.expectBroadcast();
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
NET_CAPABILITY_VALIDATED));
verifyActiveNetwork(TRANSPORT_WIFI);
@@ -1817,13 +1844,13 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
// Test unvalidated networks
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- waitFor(cv);
+ b.expectBroadcast();
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
// This should not trigger spurious onAvailable() callbacks, b/21762680.
@@ -1832,28 +1859,28 @@ public class ConnectivityServiceTest {
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- waitFor(cv);
+ b.expectBroadcast();
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
- cv = registerConnectivityBroadcast(2);
+ b = registerConnectivityBroadcast(2);
mWiFiNetworkAgent.disconnect();
genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
cellNetworkCallback.assertNoCallback();
- waitFor(cv);
+ b.expectBroadcast();
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
- cv = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
mCellNetworkAgent.disconnect();
genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- waitFor(cv);
+ b.expectBroadcast();
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
// Test validated networks
@@ -2542,9 +2569,9 @@ public class ConnectivityServiceTest {
// Test bringing up validated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- final ConditionVariable cv = registerConnectivityBroadcast(1);
+ final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
// Register MMS NetworkRequest
@@ -2570,9 +2597,9 @@ public class ConnectivityServiceTest {
public void testMMSonCell() throws Exception {
// Test bringing up cellular without MMS
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
mCellNetworkAgent.connect(false);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Register MMS NetworkRequest
@@ -4037,9 +4064,9 @@ public class ConnectivityServiceTest {
}
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
mWiFiNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
verifyActiveNetwork(TRANSPORT_WIFI);
mWiFiNetworkAgent.sendLinkProperties(lp);
waitForIdle();
@@ -4570,10 +4597,10 @@ public class ConnectivityServiceTest {
assertNotPinnedToWifi();
// Disconnect cell and wifi.
- ConditionVariable cv = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down.
+ ExpectedBroadcast b = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down.
mCellNetworkAgent.disconnect();
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
// Pinning takes effect even if the pinned network is the default when the pin is set...
TestNetworkPinner.pin(mServiceContext, wifiRequest);
@@ -4583,10 +4610,10 @@ public class ConnectivityServiceTest {
assertPinnedToWifiWithWifiDefault();
// ... and is maintained even when that network is no longer the default.
- cv = registerConnectivityBroadcast(1);
+ b = registerConnectivityBroadcast(1);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mCellNetworkAgent.connect(true);
- waitFor(cv);
+ b.expectBroadcast();
assertPinnedToWifiWithCellDefault();
}
@@ -4684,7 +4711,7 @@ public class ConnectivityServiceTest {
@Test
public void testNetworkInfoOfTypeNone() throws Exception {
- ConditionVariable broadcastCV = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = registerConnectivityBroadcast(1);
verifyNoNetwork();
TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE);
@@ -4717,9 +4744,7 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(callback);
verifyNoNetwork();
- if (broadcastCV.block(10)) {
- fail("expected no broadcast, but got CONNECTIVITY_ACTION broadcast");
- }
+ b.expectNoBroadcast(10);
}
@Test
@@ -6456,11 +6481,11 @@ public class ConnectivityServiceTest {
// prefix discovery is never started.
LinkProperties lp = new LinkProperties(baseLp);
lp.setNat64Prefix(pref64FromRa);
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
- mCellNetworkAgent.connect(false);
- final Network network = mCellNetworkAgent.getNetwork();
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
+ mWiFiNetworkAgent.connect(false);
+ final Network network = mWiFiNetworkAgent.getNetwork();
int netId = network.getNetId();
- callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
@@ -6469,8 +6494,8 @@ public class ConnectivityServiceTest {
// If the RA prefix is withdrawn, clatd is stopped and prefix discovery is started.
lp.setNat64Prefix(null);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
@@ -6478,8 +6503,8 @@ public class ConnectivityServiceTest {
// If the RA prefix appears while DNS discovery is in progress, discovery is stopped and
// clatd is started with the prefix from the RA.
lp.setNat64Prefix(pref64FromRa);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
@@ -6487,21 +6512,21 @@ public class ConnectivityServiceTest {
// Withdraw the RA prefix so we can test the case where an RA prefix appears after DNS
// discovery has succeeded.
lp.setNat64Prefix(null);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
pref64FromDnsStr, 96);
- expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
// If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix
// discovery is not stopped, and there are no callbacks.
lp.setNat64Prefix(pref64FromDns);
- mCellNetworkAgent.sendLinkProperties(lp);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
inOrder.verify(mMockNetd, never()).clatdStop(iface);
inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
@@ -6511,7 +6536,7 @@ public class ConnectivityServiceTest {
// If the RA is later withdrawn, nothing happens again.
lp.setNat64Prefix(null);
- mCellNetworkAgent.sendLinkProperties(lp);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
inOrder.verify(mMockNetd, never()).clatdStop(iface);
inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
@@ -6521,8 +6546,8 @@ public class ConnectivityServiceTest {
// If the RA prefix changes, clatd is restarted and prefix discovery is stopped.
lp.setNat64Prefix(pref64FromRa);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa);
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
@@ -6536,8 +6561,8 @@ public class ConnectivityServiceTest {
// If the RA prefix changes, clatd is restarted and prefix discovery is not started.
lp.setNat64Prefix(newPref64FromRa);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, newPref64FromRa);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, newPref64FromRa);
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString());
@@ -6547,7 +6572,7 @@ public class ConnectivityServiceTest {
// If the RA prefix changes to the same value, nothing happens.
lp.setNat64Prefix(newPref64FromRa);
- mCellNetworkAgent.sendLinkProperties(lp);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
assertEquals(newPref64FromRa, mCm.getLinkProperties(network).getNat64Prefix());
inOrder.verify(mMockNetd, never()).clatdStop(iface);
@@ -6561,19 +6586,19 @@ public class ConnectivityServiceTest {
// If the same prefix is learned first by DNS and then by RA, and clat is later stopped,
// (e.g., because the network disconnects) setPrefix64(netid, "") is never called.
lp.setNat64Prefix(null);
- mCellNetworkAgent.sendLinkProperties(lp);
- expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
pref64FromDnsStr, 96);
- expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
+ expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns);
inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any());
lp.setNat64Prefix(pref64FromDns);
- mCellNetworkAgent.sendLinkProperties(lp);
+ mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
inOrder.verify(mMockNetd, never()).clatdStop(iface);
inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
@@ -6584,10 +6609,10 @@ public class ConnectivityServiceTest {
// When tearing down a network, clat state is only updated after CALLBACK_LOST is fired, but
// before CONNECTIVITY_ACTION is sent. Wait for CONNECTIVITY_ACTION before verifying that
// clat has been stopped, or the test will be flaky.
- ConditionVariable cv = registerConnectivityBroadcast(1);
- mCellNetworkAgent.disconnect();
- callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- waitFor(cv);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ b.expectBroadcast();
inOrder.verify(mMockNetd).clatdStop(iface);
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
@@ -6662,10 +6687,10 @@ public class ConnectivityServiceTest {
.destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId));
// Disconnect wifi
- ConditionVariable cv = registerConnectivityBroadcast(1);
+ ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
reset(mNetworkManagementService);
mWiFiNetworkAgent.disconnect();
- waitFor(cv);
+ b.expectBroadcast();
verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
// Clean up
From cb2feba0fbc5f9a80e3c5cc863ecd56b26113e39 Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Tue, 15 Dec 2020 00:45:30 +0900
Subject: [PATCH 100/288] Add a test for getDefaultNetworkCapabilitiesForUser.
Bug: 172870110
Test: test-only change
Test: new test passes 100 times in a row
Change-Id: I210284578e38cd25b8b95235d3390d5bd66a5a70
Merged-In: I210284578e38cd25b8b95235d3390d5bd66a5a70
---
.../server/ConnectivityServiceTest.java | 33 +++++++++++++++++--
1 file changed, 31 insertions(+), 2 deletions(-)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 6cbcb6784ddd6..fb370a0f7124b 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5667,10 +5667,21 @@ public class ConnectivityServiceTest {
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
}
+ private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) {
+ final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser(
+ userId, "com.android.calling.package");
+ final String defaultCapsString = Arrays.toString(defaultCaps);
+ assertEquals(defaultCapsString, defaultCaps.length, networks.length);
+ final Set defaultCapsSet = new ArraySet<>(defaultCaps);
+ for (NetworkAgentWrapper network : networks) {
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork());
+ final String msg = "Did not find " + nc + " in " + Arrays.toString(defaultCaps);
+ assertTrue(msg, defaultCapsSet.contains(nc));
+ }
+ }
+
@Test
public void testVpnSetUnderlyingNetworks() throws Exception {
- final int uid = Process.myUid();
-
final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
.removeCapability(NET_CAPABILITY_NOT_VPN)
@@ -5693,6 +5704,9 @@ public class ConnectivityServiceTest {
// A VPN without underlying networks is not suspended.
assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ final int userId = UserHandle.getUserId(Process.myUid());
+ assertDefaultNetworkCapabilities(userId /* no networks */);
+
// Connect cell and use it as an underlying network.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
@@ -5706,6 +5720,7 @@ public class ConnectivityServiceTest {
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -5720,6 +5735,7 @@ public class ConnectivityServiceTest {
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Don't disconnect, but note the VPN is not using wifi any more.
mService.setUnderlyingNetworksForVpn(
@@ -5730,6 +5746,9 @@ public class ConnectivityServiceTest {
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ // The return value of getDefaultNetworkCapabilitiesForUser always includes the default
+ // network (wifi) as well as the underlying networks (cell).
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Remove NOT_SUSPENDED from the only network and observe VPN is now suspended.
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
@@ -5758,6 +5777,7 @@ public class ConnectivityServiceTest {
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
// Use both again.
mService.setUnderlyingNetworksForVpn(
@@ -5768,6 +5788,7 @@ public class ConnectivityServiceTest {
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Cell is suspended again. As WiFi is not, this should not cause a callback.
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
@@ -5785,6 +5806,7 @@ public class ConnectivityServiceTest {
// a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never
// been public and are deprecated and slated for removal, there is no sense in spending
// resources fixing this bug now.
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Use both again.
mService.setUnderlyingNetworksForVpn(
@@ -5797,6 +5819,7 @@ public class ConnectivityServiceTest {
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
// As above, the RESUMED callback not being sent here is a bug, but not a bug that's
// worth anybody's time to fix.
+ assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Disconnect cell. Receive update without even removing the dead network from the
// underlying networks – it's dead anyway. Not metered any more.
@@ -5805,6 +5828,7 @@ public class ConnectivityServiceTest {
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
// Disconnect wifi too. No underlying networks means this is now metered.
mWiFiNetworkAgent.disconnect();
@@ -5812,6 +5836,11 @@ public class ConnectivityServiceTest {
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ // When a network disconnects, the callbacks are fired before all state is updated, so for a
+ // short time, synchronous calls will behave as if the network is still connected. Wait for
+ // things to settle.
+ waitForIdle();
+ assertDefaultNetworkCapabilities(userId /* no networks */);
mMockVpn.disconnect();
}
From 9687fd7c3bcb563d8bd3b53874f6e35876275dda Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Thu, 7 Jan 2021 23:33:18 +0900
Subject: [PATCH 101/288] Test for bugs with suspended VPN underlying networks.
Bug: 172870110
Test: atest --rerun-until-failure 100 ConnectivityServiceTest#testVpnSwitchFromSuspendedToNonSuspended
Change-Id: Ia52f9cafef3f49ae70ad135d017e207eb57fddfe
Merged-In: Ia52f9cafef3f49ae70ad135d017e207eb57fddfe
---
.../server/ConnectivityServiceTest.java | 115 ++++++++++++++++++
1 file changed, 115 insertions(+)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index fb370a0f7124b..ffd6b28e8b0d5 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5433,6 +5433,121 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(callback);
}
+ private void assertGetNetworkInfoOfGetActiveNetworkIsConnected(boolean expectedConnectivity) {
+ // What Chromium used to do before https://chromium-review.googlesource.com/2605304
+ assertEquals("Unexpected result for getActiveNetworkInfo(getActiveNetwork())",
+ expectedConnectivity, mCm.getNetworkInfo(mCm.getActiveNetwork()).isConnected());
+ }
+
+ @Test
+ public void testVpnUnderlyingNetworkSuspended() throws Exception {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+
+ // Connect a VPN.
+ mMockVpn.establishForMyUid(false, true, false);
+ callback.expectAvailableCallbacksUnvalidated(mMockVpn);
+
+ // Connect cellular data.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(false /* validated */);
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.assertNoCallback();
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
+
+ // Suspend the cellular network and expect the VPN to be suspended.
+ mCellNetworkAgent.suspend();
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
+ callback.assertNoCallback();
+
+ assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
+ // VPN's main underlying network is suspended, so no connectivity.
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ // Switch to another network. The VPN should no longer be suspended.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false /* validated */);
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_WIFI));
+
+ // BUG: the VPN is no longer suspended, so a RESUMED callback should have been sent.
+ // callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+ callback.assertNoCallback();
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
+ // BUG: the device has connectivity, so this should return true.
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ // Unsuspend cellular and then switch back to it.
+ // The same bug happens in the opposite direction: the VPN's capabilities correctly have
+ // NOT_SUSPENDED, but the VPN's NetworkInfo is in state SUSPENDED.
+ mCellNetworkAgent.resume();
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ // Spurious double callback?
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.assertNoCallback();
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertNull(mCm.getActiveNetworkInfo()); // ???
+ // BUG: the device has connectivity, so this should return true.
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ // Suspending and resuming reveals other bugs.
+ mCellNetworkAgent.suspend();
+ callback.assertNoCallback(); // BUG: should get callback that VPN is suspended.
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); // BUG: VPN should be SUSPENDED.
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);
+ assertNull(mCm.getActiveNetworkInfo()); // ???
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+
+ mCellNetworkAgent.resume();
+ callback.assertNoCallback(); // BUG: should get callback that VPN is no longer suspended.
+
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
+ assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);
+ assertNull(mCm.getActiveNetworkInfo()); // ???
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ }
+
@Test
public void testVpnNetworkActive() throws Exception {
final int uid = Process.myUid();
From be317557fe11d9a27412712556c9203778fab645 Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Thu, 28 Jan 2021 18:31:31 +0900
Subject: [PATCH 102/288] Backport some helpers in ConnectivityServiceTest.
These were added in aosp/1527378, which is impractical to
backport.
Bug: 172870110
Test: test-only change
Change-Id: Id3d12b23034b284c8f7dffb5167244e1e43987e2
Merged-In: I827543751dbf5e626a24ec02cd6f50b423f5f761
---
.../android/server/ConnectivityServiceTest.java | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index ffd6b28e8b0d5..d8e1f0a434f1d 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -36,6 +36,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
+import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
@@ -6289,6 +6290,19 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(defaultCallback);
}
+ private void checkNetworkInfo(NetworkInfo ni, int type, DetailedState state) {
+ assertNotNull(ni);
+ assertEquals(type, ni.getType());
+ assertEquals(ConnectivityManager.getNetworkTypeName(type), state, ni.getDetailedState());
+ }
+
+ private void assertActiveNetworkInfo(int type, DetailedState state) {
+ checkNetworkInfo(mCm.getActiveNetworkInfo(), type, state);
+ }
+ private void assertNetworkInfo(int type, DetailedState state) {
+ checkNetworkInfo(mCm.getNetworkInfo(type), type, state);
+ }
+
@Test
public final void testLoseTrusted() throws Exception {
final NetworkRequest trustedRequest = new NetworkRequest.Builder()
From 1255ddbc76df53c9562a87cd5701d791e0ec55ad Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Tue, 12 Jan 2021 00:34:44 +0900
Subject: [PATCH 103/288] Backport test coverage from aosp/1547496.
This test coverage is necessary to fix an upcoming bug in R.
Backport it from the change that added it. The non-test portion
of that change is not necessary in R because it fixes a
bug that was introduced in S.
Bug: 172870110
Test: accompanying unit test shows lots of bugs removed
Change-Id: If7eb8857474d8b4f774f5fa5db2a3112e85c9cae
Merged-In: Ibf376a6fa4b34d1c96f8506fa8abbb7595a8c272
---
.../server/ConnectivityServiceTest.java | 28 ++++++++++++-------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index d8e1f0a434f1d..3d4ff41d65fbc 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5521,32 +5521,40 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
- assertNull(mCm.getActiveNetworkInfo()); // ???
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
// BUG: the device has connectivity, so this should return true.
assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
- // Suspending and resuming reveals other bugs.
+ // Re-suspending the current network fixes the problem.
mCellNetworkAgent.suspend();
- callback.assertNoCallback(); // BUG: should get callback that VPN is suspended.
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
+ callback.assertNoCallback();
- assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
- .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); // BUG: VPN should be SUSPENDED.
+ assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);
- assertNull(mCm.getActiveNetworkInfo()); // ???
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
mCellNetworkAgent.resume();
- callback.assertNoCallback(); // BUG: should get callback that VPN is no longer suspended.
+ callback.expectCapabilitiesThat(mMockVpn,
+ nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ && nc.hasTransport(TRANSPORT_CELLULAR));
+ callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+ callback.assertNoCallback();
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
- assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);
- assertNull(mCm.getActiveNetworkInfo()); // ???
- assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
}
@Test
From 9d680e7cd3c58da63d7753f2e4dce5683d2fd893 Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Mon, 11 Jan 2021 22:27:57 +0900
Subject: [PATCH 104/288] Fix legacy APIs when VPN switches to suspended
underlying network.
Currently, when the VPN underlying network changes from a
network that is not suspended to one that is suspended (or vice
versa), some of the legacy APIs return incorrect results.
This is because the VPN's NetworkInfo can get into SUSPENDED
state even though the capabilities have the NOT_SUSPENDED
capability. This happens because the code in updateCapabilities
that checks for changes in NOT_SUSPENDED and NOT_ROAMING (which
are the capabilities that can affect the NetworkInfo state) is
only run when the capabilities change in a certain way.
Fix this by always checking for changes in these capabilities,
regardless of what else has changed.
This results in sending a lot more SUSPENDED and RESUMED
callbacks than the code sent previously. This should hopefully
not impact apps because those callback methods have never been
public API, though because they're just callbacks, it's possible
that apps found out via code inspection that the callbacks
existed and implemented them.
Bug: 172870110
Test: changes to existing tests in ConnectivityServiceTest
Change-Id: I6ec246a6a4e61f634956a165797fbb80296efd6a
Merged-In: I6ec246a6a4e61f634956a165797fbb80296efd6a
---
.../android/server/ConnectivityService.java | 33 +++++++++++--------
.../server/ConnectivityServiceTest.java | 29 ++++++----------
2 files changed, 30 insertions(+), 32 deletions(-)
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 77cd5d2ffdabb..fcf73ac7ce15a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -6316,6 +6316,25 @@ public class ConnectivityService extends IConnectivityManager.Stub
return newNc;
}
+ private void updateNetworkInfoForRoamingAndSuspended(NetworkAgentInfo nai,
+ NetworkCapabilities prevNc, NetworkCapabilities newNc) {
+ final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ if (prevSuspended != suspended) {
+ // TODO (b/73132094) : remove this call once the few users of onSuspended and
+ // onResumed have been removed.
+ notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+ : ConnectivityManager.CALLBACK_RESUMED);
+ }
+ if (prevSuspended != suspended || prevRoaming != roaming) {
+ // updateNetworkInfo will mix in the suspended info from the capabilities and
+ // take appropriate action for the network having possibly changed state.
+ updateNetworkInfo(nai, nai.networkInfo);
+ }
+ }
+
/**
* Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically:
*
@@ -6347,25 +6366,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
// on this network. We might have been called by rematchNetworkAndRequests when a
// network changed foreground state.
processListenRequests(nai);
- final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
- final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
- final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
- final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
- if (prevSuspended != suspended || prevRoaming != roaming) {
- // TODO (b/73132094) : remove this call once the few users of onSuspended and
- // onResumed have been removed.
- notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
- : ConnectivityManager.CALLBACK_RESUMED);
- // updateNetworkInfo will mix in the suspended info from the capabilities and
- // take appropriate action for the network having possibly changed state.
- updateNetworkInfo(nai, nai.networkInfo);
- }
} else {
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
rematchAllNetworksAndRequests();
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
+ updateNetworkInfoForRoamingAndSuspended(nai, prevNc, newNc);
// TODO : static analysis indicates that prevNc can't be null here (getAndSetNetworkCaps
// never returns null), so mark the relevant members and functions in nai as @NonNull and
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 3d4ff41d65fbc..a5554c740e7f7 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5488,23 +5488,18 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesThat(mMockVpn,
nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
&& nc.hasTransport(TRANSPORT_WIFI));
-
- // BUG: the VPN is no longer suspended, so a RESUMED callback should have been sent.
- // callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+ callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
callback.assertNoCallback();
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
- assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
- // BUG: the device has connectivity, so this should return true.
- assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
- // Unsuspend cellular and then switch back to it.
- // The same bug happens in the opposite direction: the VPN's capabilities correctly have
- // NOT_SUSPENDED, but the VPN's NetworkInfo is in state SUSPENDED.
+ // Unsuspend cellular and then switch back to it. The VPN remains not suspended.
mCellNetworkAgent.resume();
mWiFiNetworkAgent.disconnect();
callback.expectCapabilitiesThat(mMockVpn,
@@ -5520,12 +5515,11 @@ public class ConnectivityServiceTest {
.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
- assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
- // BUG: the device has connectivity, so this should return true.
- assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
- // Re-suspending the current network fixes the problem.
+ // Suspend cellular and expect no connectivity.
mCellNetworkAgent.suspend();
callback.expectCapabilitiesThat(mMockVpn,
nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -5541,6 +5535,7 @@ public class ConnectivityServiceTest {
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ // Resume cellular and expect that connectivity comes back.
mCellNetworkAgent.resume();
callback.expectCapabilitiesThat(mMockVpn,
nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -5926,10 +5921,7 @@ public class ConnectivityServiceTest {
&& caps.hasTransport(TRANSPORT_CELLULAR)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- // While the SUSPENDED callback should in theory be sent here, it is not. This is
- // a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never
- // been public and are deprecated and slated for removal, there is no sense in spending
- // resources fixing this bug now.
+ vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Use both again.
@@ -5941,8 +5933,7 @@ public class ConnectivityServiceTest {
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- // As above, the RESUMED callback not being sent here is a bug, but not a bug that's
- // worth anybody's time to fix.
+ vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Disconnect cell. Receive update without even removing the dead network from the
From 19f28d7894d4c518b4343e601d745846aaff6562 Mon Sep 17 00:00:00 2001
From: Miranda Kephart
Date: Mon, 16 Nov 2020 11:22:13 -0500
Subject: [PATCH 105/288] [DO NOT MERGE] Close screenshot process on user
switched
Currently, we keep the process up even if the user switches,
meaning that in some cases (if the user is switched while the
screenshot UI is up) we will save images to the wrong profile.
This change makes ScreenshotHelper listen for user switches and
close the screenshot service, so that a new screenshot is
guaranteed to be constructed with the correct user's context.
Bug: 170474245
Fix: 170474245
Test: manual -- verified bad state occurs if user switches within
the timeout period, ensured that screenshots work immediately
after switching with this change.
Change-Id: I9d32d0928e6c2bda161d04555438d0dd7afef0ba
(cherry picked from commit 7ef1a5dd1506075507412626f2533283d9520144)
---
.../internal/util/ScreenshotHelper.java | 37 ++++++++++++++++---
1 file changed, 31 insertions(+), 6 deletions(-)
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 7fd94c6859fb2..b92204236e97f 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -1,9 +1,13 @@
package com.android.internal.util;
+import static android.content.Intent.ACTION_USER_SWITCHED;
+
import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
@@ -29,8 +33,21 @@ public class ScreenshotHelper {
private ServiceConnection mScreenshotConnection = null;
private final Context mContext;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mScreenshotLock) {
+ if (ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ resetConnection();
+ }
+ }
+ }
+ };
+
public ScreenshotHelper(Context context) {
mContext = context;
+ IntentFilter filter = new IntentFilter(ACTION_USER_SWITCHED);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
}
/**
@@ -57,8 +74,8 @@ public class ScreenshotHelper {
@Override public void run() {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
+ Log.e(TAG, "Timed out before getting screenshot capture response");
+ resetConnection();
notifyScreenshotError();
}
}
@@ -81,8 +98,7 @@ public class ScreenshotHelper {
public void handleMessage(Message msg) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection == myConn) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
+ resetConnection();
handler.removeCallbacks(mScreenshotTimeout);
}
}
@@ -103,8 +119,7 @@ public class ScreenshotHelper {
public void onServiceDisconnected(ComponentName name) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
+ resetConnection();
handler.removeCallbacks(mScreenshotTimeout);
notifyScreenshotError();
}
@@ -120,6 +135,16 @@ public class ScreenshotHelper {
}
}
+ /**
+ * Unbinds the current screenshot connection (if any).
+ */
+ private void resetConnection() {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ }
+ }
+
/**
* Notifies the screenshot service to show an error.
*/
From 41a97a3fd773176a0c262250dc87b2f26cbbdf31 Mon Sep 17 00:00:00 2001
From: Miranda Kephart
Date: Mon, 16 Nov 2020 11:22:13 -0500
Subject: [PATCH 106/288] [DO NOT MERGE] Close screenshot process on user
switched
Currently, we keep the process up even if the user switches,
meaning that in some cases (if the user is switched while the
screenshot UI is up) we will save images to the wrong profile.
This change makes ScreenshotHelper listen for user switches and
close the screenshot service, so that a new screenshot is
guaranteed to be constructed with the correct user's context.
Bug: 170474245
Fix: 170474245
Test: manual -- verified bad state occurs if user switches within
the timeout period, ensured that screenshots work immediately
after switching with this change.
Change-Id: I9d32d0928e6c2bda161d04555438d0dd7afef0ba
(cherry picked from commit 7ef1a5dd1506075507412626f2533283d9520144)
---
.../internal/util/ScreenshotHelper.java | 37 ++++++++++++++++---
1 file changed, 31 insertions(+), 6 deletions(-)
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index d24d78c6f3dab..14afe9958a409 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -1,10 +1,14 @@
package com.android.internal.util;
+import static android.content.Intent.ACTION_USER_SWITCHED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.Handler;
@@ -33,8 +37,21 @@ public class ScreenshotHelper {
private ServiceConnection mScreenshotConnection = null;
private final Context mContext;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mScreenshotLock) {
+ if (ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ resetConnection();
+ }
+ }
+ }
+ };
+
public ScreenshotHelper(Context context) {
mContext = context;
+ IntentFilter filter = new IntentFilter(ACTION_USER_SWITCHED);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
}
/**
@@ -103,8 +120,8 @@ public class ScreenshotHelper {
public void run() {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
+ Log.e(TAG, "Timed out before getting screenshot capture response");
+ resetConnection();
notifyScreenshotError();
}
}
@@ -130,8 +147,7 @@ public class ScreenshotHelper {
public void handleMessage(Message msg) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection == myConn) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
+ resetConnection();
handler.removeCallbacks(mScreenshotTimeout);
}
}
@@ -160,8 +176,7 @@ public class ScreenshotHelper {
public void onServiceDisconnected(ComponentName name) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
+ resetConnection();
handler.removeCallbacks(mScreenshotTimeout);
notifyScreenshotError();
}
@@ -177,6 +192,16 @@ public class ScreenshotHelper {
}
}
+ /**
+ * Unbinds the current screenshot connection (if any).
+ */
+ private void resetConnection() {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ }
+ }
+
/**
* Notifies the screenshot service to show an error.
*/
From 9908e1a14575b5543fef15032f9fe495382fce87 Mon Sep 17 00:00:00 2001
From: Miranda Kephart
Date: Mon, 16 Nov 2020 11:22:13 -0500
Subject: [PATCH 107/288] [DO NOT MERGE] Close screenshot process on user
switched
Currently, we keep the process up even if the user switches,
meaning that in some cases (if the user is switched while the
screenshot UI is up) we will save images to the wrong profile.
This change makes ScreenshotHelper listen for user switches and
close the screenshot service, so that a new screenshot is
guaranteed to be constructed with the correct user's context.
Bug: 170474245
Fix: 170474245
Test: manual -- verified bad state occurs if user switches within
the timeout period, ensured that screenshots work immediately
after switching with this change.
Change-Id: I9d32d0928e6c2bda161d04555438d0dd7afef0ba
(cherry picked from commit 7ef1a5dd1506075507412626f2533283d9520144)
---
.../internal/util/ScreenshotHelper.java | 47 ++++++++++++++-----
1 file changed, 35 insertions(+), 12 deletions(-)
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 7fd94c6859fb2..9ab89328a075c 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -1,9 +1,13 @@
package com.android.internal.util;
+import static android.content.Intent.ACTION_USER_SWITCHED;
+
import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
@@ -29,8 +33,21 @@ public class ScreenshotHelper {
private ServiceConnection mScreenshotConnection = null;
private final Context mContext;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mScreenshotLock) {
+ if (ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ resetConnection();
+ }
+ }
+ }
+ };
+
public ScreenshotHelper(Context context) {
mContext = context;
+ IntentFilter filter = new IntentFilter(ACTION_USER_SWITCHED);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
}
/**
@@ -53,14 +70,12 @@ public class ScreenshotHelper {
SYSUI_SCREENSHOT_SERVICE);
final Intent serviceIntent = new Intent();
- final Runnable mScreenshotTimeout = new Runnable() {
- @Override public void run() {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- notifyScreenshotError();
- }
+ final Runnable mScreenshotTimeout = () -> {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ Log.e(TAG, "Timed out before getting screenshot capture response");
+ resetConnection();
+ notifyScreenshotError();
}
}
};
@@ -81,8 +96,7 @@ public class ScreenshotHelper {
public void handleMessage(Message msg) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection == myConn) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
+ resetConnection();
handler.removeCallbacks(mScreenshotTimeout);
}
}
@@ -103,8 +117,7 @@ public class ScreenshotHelper {
public void onServiceDisconnected(ComponentName name) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
+ resetConnection();
handler.removeCallbacks(mScreenshotTimeout);
notifyScreenshotError();
}
@@ -120,6 +133,16 @@ public class ScreenshotHelper {
}
}
+ /**
+ * Unbinds the current screenshot connection (if any).
+ */
+ private void resetConnection() {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ }
+ }
+
/**
* Notifies the screenshot service to show an error.
*/
From 3a62e9ffaad2d81bf176decb7b60266bd6005301 Mon Sep 17 00:00:00 2001
From: Beth Thibodeau
Date: Wed, 27 Jan 2021 13:33:34 -0500
Subject: [PATCH 108/288] Limit maximum allowed size for a status bar icon
Bug: 169255797
Test: atest StatusBarIconViewTest
Test: verified that app crashes instead of SysUI following repro steps
Change-Id: I66e3bb873841b5babfd522c82cea7bed361fc14c
(cherry picked from commit 4394595debfd3f625b45a8b2280cdbec61f8dc71)
---
.../android/systemui/statusbar/StatusBarIconView.java | 10 ++++++++++
.../systemui/statusbar/StatusBarIconViewTest.java | 10 ++++++++++
2 files changed, 20 insertions(+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 8cf8a22999220..25ae5c2dadef1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -83,6 +83,9 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
public static final int STATE_DOT = 1;
public static final int STATE_HIDDEN = 2;
+ /** Maximum allowed width or height for an icon drawable */
+ private static final int MAX_IMAGE_SIZE = 500;
+
private static final String TAG = "StatusBarIconView";
private static final Property ICON_APPEAR_AMOUNT
= new FloatProperty("iconAppearAmount") {
@@ -378,6 +381,13 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
Log.w(TAG, "No icon for slot " + mSlot + "; " + mIcon.icon);
return false;
}
+
+ if (drawable.getIntrinsicWidth() > MAX_IMAGE_SIZE
+ || drawable.getIntrinsicHeight() > MAX_IMAGE_SIZE) {
+ Log.w(TAG, "Drawable is too large " + mIcon);
+ return false;
+ }
+
if (withClear) {
setImageDrawable(null);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 9971e0cf81a32..daa805a8f6e6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -35,6 +35,7 @@ import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
@@ -123,4 +124,13 @@ public class StatusBarIconViewTest extends SysuiTestCase {
assertEquals("Transparent backgrounds should fallback to drawable color",
color, mIconView.getStaticDrawableColor());
}
+
+ @Test
+ public void testGiantImageNotAllowed() {
+ Bitmap largeBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
+ Icon icon = Icon.createWithBitmap(largeBitmap);
+ StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
+ icon, 0, 0, "");
+ assertFalse(mIconView.set(largeIcon));
+ }
}
\ No newline at end of file
From def75584a3f1a375685f1520fe5df506e8dcfa77 Mon Sep 17 00:00:00 2001
From: Beth Thibodeau
Date: Wed, 27 Jan 2021 13:33:34 -0500
Subject: [PATCH 109/288] Limit maximum allowed size for a status bar icon
Bug: 169255797
Test: atest StatusBarIconViewTest
Test: verified that app crashes instead of SysUI following repro steps
Change-Id: I66e3bb873841b5babfd522c82cea7bed361fc14c
(cherry picked from commit 4394595debfd3f625b45a8b2280cdbec61f8dc71)
---
.../android/systemui/statusbar/StatusBarIconView.java | 10 ++++++++++
.../systemui/statusbar/StatusBarIconViewTest.java | 10 ++++++++++
2 files changed, 20 insertions(+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 8cf8a22999220..25ae5c2dadef1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -83,6 +83,9 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
public static final int STATE_DOT = 1;
public static final int STATE_HIDDEN = 2;
+ /** Maximum allowed width or height for an icon drawable */
+ private static final int MAX_IMAGE_SIZE = 500;
+
private static final String TAG = "StatusBarIconView";
private static final Property ICON_APPEAR_AMOUNT
= new FloatProperty("iconAppearAmount") {
@@ -378,6 +381,13 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
Log.w(TAG, "No icon for slot " + mSlot + "; " + mIcon.icon);
return false;
}
+
+ if (drawable.getIntrinsicWidth() > MAX_IMAGE_SIZE
+ || drawable.getIntrinsicHeight() > MAX_IMAGE_SIZE) {
+ Log.w(TAG, "Drawable is too large " + mIcon);
+ return false;
+ }
+
if (withClear) {
setImageDrawable(null);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 9971e0cf81a32..daa805a8f6e6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -35,6 +35,7 @@ import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
@@ -123,4 +124,13 @@ public class StatusBarIconViewTest extends SysuiTestCase {
assertEquals("Transparent backgrounds should fallback to drawable color",
color, mIconView.getStaticDrawableColor());
}
+
+ @Test
+ public void testGiantImageNotAllowed() {
+ Bitmap largeBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
+ Icon icon = Icon.createWithBitmap(largeBitmap);
+ StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
+ icon, 0, 0, "");
+ assertFalse(mIconView.set(largeIcon));
+ }
}
\ No newline at end of file
From a21b2252e0b0eccd482b3b69e67b8911b94dba73 Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Mon, 16 Nov 2020 10:11:12 +0900
Subject: [PATCH 110/288] Test a VPN with an underlying network that does not
yet exist.
This test checks that if a VPN declares an underlying network
that does not exist, the capabilities of that network are applied
to the VPN as soon as the network starts to exist.
Bug: 172870110
Test: test-only change
Change-Id: Icc0701cb4cea7d91f7738c1e426e94cd26686b74
Merged-In: Icc0701cb4cea7d91f7738c1e426e94cd26686b74
---
.../com/android/server/TestNetIdManager.kt | 1 +
.../server/ConnectivityServiceTest.java | 57 ++++++++++++++++++-
2 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
index eb290dc7d24ad..938a694e8ba98 100644
--- a/tests/net/integration/util/com/android/server/TestNetIdManager.kt
+++ b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
@@ -35,4 +35,5 @@ class TestNetIdManager : NetIdManager() {
private val nextId = AtomicInteger(MAX_NET_ID)
override fun reserveNetId() = nextId.decrementAndGet()
override fun releaseNetId(id: Int) = Unit
+ fun peekNextNetId() = nextId.get() - 1
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 385005f90c3be..0c1bf548db6dc 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -336,6 +336,7 @@ public class ConnectivityServiceTest {
private INetworkPolicyListener mPolicyListener;
private WrappedMultinetworkPolicyTracker mPolicyTracker;
private HandlerThread mAlarmManagerThread;
+ private TestNetIdManager mNetIdManager;
@Mock IIpConnectivityMetrics mIpConnectivityMetrics;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@@ -1194,6 +1195,8 @@ public class ConnectivityServiceTest {
@Before
public void setUp() throws Exception {
+ mNetIdManager = new TestNetIdManager();
+
mContext = InstrumentationRegistry.getContext();
MockitoAnnotations.initMocks(this);
@@ -1264,7 +1267,7 @@ public class ConnectivityServiceTest {
final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
- doReturn(new TestNetIdManager()).when(deps).makeNetIdManager();
+ doReturn(mNetIdManager).when(deps).makeNetIdManager();
doReturn(mNetworkStack).when(deps).getNetworkStack();
doReturn(systemProperties).when(deps).getSystemProperties();
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
@@ -5186,6 +5189,58 @@ public class ConnectivityServiceTest {
assertTrue(lp.getDnsServers().containsAll(dnsServers));
}
+ @Test
+ public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN).build();
+
+ mCm.registerNetworkCallback(request, callback);
+
+ // Bring up a VPN that specifies an underlying network that does not exist yet.
+ // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet,
+ // (and doing so is difficult without using reflection) but it's good to test that the code
+ // behaves approximately correctly.
+ final int uid = Process.myUid();
+ final TestNetworkAgentWrapper
+ vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
+ final ArraySet ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+
+ final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
+ mMockVpn.setNetworkAgent(vpnNetworkAgent);
+ mMockVpn.setUids(ranges);
+ mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
+ vpnNetworkAgent.connect(false);
+ mMockVpn.connect();
+ callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertFalse(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
+
+ // Make that underlying network connect, and expect to see its capabilities immediately
+ // reflected in the VPN's capabilities.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
+ mWiFiNetworkAgent.connect(false);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
+
+ // Disconnect the network, and expect to see the VPN capabilities change accordingly.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ callback.expectCapabilitiesThat(vpnNetworkAgent, (nc) ->
+ nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
+
+ vpnNetworkAgent.disconnect();
+ mCm.unregisterNetworkCallback(callback);
+ }
+
@Test
public void testVpnNetworkActive() throws Exception {
final int uid = Process.myUid();
From 9ededd8476680d40b0b0d5b21e3a4bc0d743adae Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Mon, 16 Nov 2020 16:05:44 +0900
Subject: [PATCH 111/288] Simplify MockVpn.
This CL removes four methods in MockVpn by slightly changing the
test code to leverage the actual methods implemented by the
(production) Vpn superclass.
This works because setting mInterface results in
isRunningLocked() returning true, which makes a number of methods
behave as if the VPN is connected (which is what the test
expects).
The more realistic behaviour exposes a minor bug in the treatment
of underlying networks. Add a TODO to fix it.
Bug: 172870110
Test: test-only change
Change-Id: I49421183538ba61ca790af71e309ece36b653bf9
Merged-In: I49421183538ba61ca790af71e309ece36b653bf9
---
.../com/android/server/connectivity/Vpn.java | 3 +-
.../server/ConnectivityServiceTest.java | 53 ++++++++-----------
2 files changed, 25 insertions(+), 31 deletions(-)
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index bcb17bc9e2dc9..9edb0e440baac 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -202,7 +202,8 @@ public class Vpn {
@VisibleForTesting protected String mPackage;
private int mOwnerUID;
private boolean mIsPackageTargetingAtLeastQ;
- private String mInterface;
+ @VisibleForTesting
+ protected String mInterface;
private Connection mConnection;
/** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 0c1bf548db6dc..572ea12358ec1 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -319,6 +319,7 @@ public class ConnectivityServiceTest {
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
+ private static final String VPN_IFNAME = "tun10042";
private static final String TEST_PACKAGE_NAME = "com.android.test.package";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
@@ -1033,12 +1034,14 @@ public class ConnectivityServiceTest {
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
userId, mock(KeyStore.class));
+ mConfig = new VpnConfig();
}
public void setNetworkAgent(TestNetworkAgentWrapper agent) {
agent.waitForIdle(TIMEOUT_MS);
mMockNetworkAgent = agent;
mNetworkAgent = agent.getNetworkAgent();
+ mInterface = VPN_IFNAME;
mNetworkCapabilities.set(agent.getNetworkCapabilities());
}
@@ -1059,16 +1062,6 @@ public class ConnectivityServiceTest {
return mMockNetworkAgent.getNetwork().netId;
}
- @Override
- public boolean appliesToUid(int uid) {
- return mConnected; // Trickery to simplify testing.
- }
-
- @Override
- protected boolean isCallerEstablishedOwnerLocked() {
- return mConnected; // Similar trickery
- }
-
@Override
public int getActiveAppVpnType() {
return mVpnType;
@@ -1077,7 +1070,6 @@ public class ConnectivityServiceTest {
private void connect(boolean isAlwaysMetered) {
mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
mConnected = true;
- mConfig = new VpnConfig();
mConfig.isMetered = isAlwaysMetered;
}
@@ -1108,7 +1100,6 @@ public class ConnectivityServiceTest {
public void disconnect() {
mConnected = false;
- mConfig = null;
}
@Override
@@ -1121,18 +1112,6 @@ public class ConnectivityServiceTest {
private synchronized void setVpnInfo(VpnInfo vpnInfo) {
mVpnInfo = vpnInfo;
}
-
- @Override
- public synchronized Network[] getUnderlyingNetworks() {
- if (mUnderlyingNetworks != null) return mUnderlyingNetworks;
-
- return super.getUnderlyingNetworks();
- }
-
- /** Don't override behavior for {@link Vpn#setUnderlyingNetworks}. */
- private synchronized void overrideUnderlyingNetworks(Network[] underlyingNetworks) {
- mUnderlyingNetworks = underlyingNetworks;
- }
}
private void mockVpn(int uid) {
@@ -5210,7 +5189,7 @@ public class ConnectivityServiceTest {
final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
- mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
+ mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
vpnNetworkAgent.connect(false);
mMockVpn.connect();
callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
@@ -5224,8 +5203,17 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
mWiFiNetworkAgent.connect(false);
- callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ // TODO: the callback for the VPN happens before any callbacks are called for the wifi
+ // network that has just connected. There appear to be two issues here:
+ // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for
+ // it returns non-null (which happens very early, during handleRegisterNetworkAgent).
+ // This is not correct because that that point the network is not connected and cannot
+ // pass any traffic.
+ // 2. When a network connects, updateNetworkInfo propagates underlying network capabilities
+ // before rematching networks.
+ // Given that this scenario can't really happen, this is probably fine for now.
callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
.hasTransport(TRANSPORT_VPN));
assertTrue(mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork())
@@ -5289,7 +5277,7 @@ public class ConnectivityServiceTest {
vpnNetworkAgent.connect(false);
mMockVpn.connect();
- mMockVpn.setUnderlyingNetworks(new Network[0]);
+ mService.setUnderlyingNetworksForVpn(new Network[0]);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
@@ -7249,16 +7237,21 @@ public class ConnectivityServiceTest {
// active
final VpnInfo info = new VpnInfo();
info.ownerUid = Process.myUid();
- info.vpnIface = "interface";
+ info.vpnIface = VPN_IFNAME;
mMockVpn.setVpnInfo(info);
- mMockVpn.overrideUnderlyingNetworks(new Network[] {network});
+
+ final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
+ mMockVpn.setNetworkAgent(vpnNetworkAgent);
+ mMockVpn.connect();
+
+ assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network}));
assertTrue(
"Active VPN permission not applied",
mService.checkConnectivityDiagnosticsPermissions(
Process.myPid(), Process.myUid(), naiWithoutUid,
mContext.getOpPackageName()));
- mMockVpn.overrideUnderlyingNetworks(null);
+ assertTrue(mService.setUnderlyingNetworksForVpn(null));
assertFalse(
"VPN shouldn't receive callback on non-underlying network",
mService.checkConnectivityDiagnosticsPermissions(
From 04314f147d00660fc5d32613bc06ede3d9ec3b9d Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti
Date: Tue, 17 Nov 2020 23:23:58 +0900
Subject: [PATCH 112/288] Increase test coverage for VPN info sent to
NetworkStatsService.
Bug: 172870110
Test: test-only change
Change-Id: I3711b362f31cb92b759e9f5c9d244fb88d9bd5e7
Merged-In: I3711b362f31cb92b759e9f5c9d244fb88d9bd5e7
---
.../server/ConnectivityServiceTest.java | 169 +++++++++++++++---
1 file changed, 149 insertions(+), 20 deletions(-)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 572ea12358ec1..c0293c157c277 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4745,13 +4745,52 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(networkCallback);
}
+ private void assertSameElementsNoDuplicates(T[] expected, T[] actual) {
+ // Easier to implement than a proper "assertSameElements" method that also correctly deals
+ // with duplicates.
+ final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual);
+ assertEquals(msg, expected.length, actual.length);
+ Set expectedSet = new ArraySet<>(Arrays.asList(expected));
+ assertEquals("expected contains duplicates", expectedSet.size(), expected.length);
+ // actual cannot have duplicates because it's the same length and has the same elements.
+ Set actualSet = new ArraySet<>(Arrays.asList(actual));
+ assertEquals(expectedSet, actualSet);
+ }
+
+ private void expectForceUpdateIfaces(Network[] networks, String defaultIface,
+ Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception {
+ ArgumentCaptor networksCaptor = ArgumentCaptor.forClass(Network[].class);
+ ArgumentCaptor vpnInfosCaptor = ArgumentCaptor.forClass(VpnInfo[].class);
+
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(),
+ any(NetworkState[].class), eq(defaultIface), vpnInfosCaptor.capture());
+
+ assertSameElementsNoDuplicates(networksCaptor.getValue(), networks);
+
+ VpnInfo[] infos = vpnInfosCaptor.getValue();
+ if (vpnUid != null) {
+ assertEquals("Should have exactly one VPN:", 1, infos.length);
+ VpnInfo info = infos[0];
+ assertEquals("Unexpected VPN owner:", (int) vpnUid, info.ownerUid);
+ assertEquals("Unexpected VPN interface:", vpnIfname, info.vpnIface);
+ assertSameElementsNoDuplicates(underlyingIfaces, info.underlyingIfaces);
+ } else {
+ assertEquals(0, infos.length);
+ return;
+ }
+ }
+
+ private void expectForceUpdateIfaces(Network[] networks, String defaultIface) throws Exception {
+ expectForceUpdateIfaces(networks, defaultIface, null, null, new String[0]);
+ }
+
@Test
public void testStatsIfacesChanged() throws Exception {
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
- Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
+ final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
+ final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -4762,9 +4801,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(false);
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Default network switch should update ifaces.
@@ -4772,32 +4809,24 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
waitForIdle();
assertEquals(wifiLp, mService.getActiveLinkProperties());
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyWifi), any(NetworkState[].class), eq(WIFI_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyWifi, WIFI_IFNAME);
reset(mStatsService);
// Disconnect should update ifaces.
mWiFiNetworkAgent.disconnect();
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class),
- eq(MOBILE_IFNAME), eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Metered change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Captive portal change shouldn't update ifaces
@@ -4811,9 +4840,101 @@ public class ConnectivityServiceTest {
// Roaming change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
+ reset(mStatsService);
+
+ // Test VPNs.
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(VPN_IFNAME);
+
+ final NetworkAgentWrapper vpnNetworkAgent = establishVpnForMyUid(lp);
+ final Network[] cellAndVpn = new Network[] {
+ mCellNetworkAgent.getNetwork(), vpnNetworkAgent.getNetwork()};
+ Network[] cellAndWifi = new Network[] {
+ mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
+
+ // A VPN with default (null) underlying networks sets the underlying network's interfaces...
+ expectForceUpdateIfaces(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME});
+
+ // ...and updates them as the default network switches.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+ final Network[] wifiAndVpn = new Network[] {
+ mWiFiNetworkAgent.getNetwork(), vpnNetworkAgent.getNetwork()};
+ cellAndWifi = new Network[] {
+ mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
+
+ waitForIdle();
+ assertEquals(wifiLp, mService.getActiveLinkProperties());
+ expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{WIFI_IFNAME});
+ reset(mStatsService);
+
+ // A VPN that sets its underlying networks passes the underlying interfaces, and influences
+ // the default interface sent to NetworkStatsService by virtue of applying to the system
+ // server UID (or, in this test, to the test's UID). This is the reason for sending
+ // MOBILE_IFNAME even though the default network is wifi.
+ // TODO: fix this to pass in the actual default network interface. Whether or not the VPN
+ // applies to the system server UID should not have any bearing on network stats.
+ mService.setUnderlyingNetworksForVpn(onlyCell);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME});
+ reset(mStatsService);
+
+ mService.setUnderlyingNetworksForVpn(cellAndWifi);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+ reset(mStatsService);
+
+ // If an underlying network disconnects, that interface should no longer be underlying.
+ // This doesn't actually work because disconnectAndDestroyNetwork only notifies
+ // NetworkStatsService before the underlying network is actually removed. So the underlying
+ // network will only be removed if notifyIfacesChangedForNetworkStats is called again. This
+ // could result in incorrect data usage measurements if the interface used by the
+ // disconnected network is reused by a system component that does not register an agent for
+ // it (e.g., tethering).
+ mCellNetworkAgent.disconnect();
+ waitForIdle();
+ assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork()));
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+
+ // Confirm that we never tell NetworkStatsService that cell is no longer the underlying
+ // network for the VPN...
+ verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class),
+ any(NetworkState[].class), any() /* anyString() doesn't match null */,
+ argThat(infos -> infos[0].underlyingIfaces.length == 1
+ && WIFI_IFNAME.equals(infos[0].underlyingIfaces[0])));
+ verifyNoMoreInteractions(mStatsService);
+ reset(mStatsService);
+
+ // ... but if something else happens that causes notifyIfacesChangedForNetworkStats to be
+ // called again, it does. For example, connect Ethernet, but with a low score, such that it
+ // does not become the default network.
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent.adjustScore(-40);
+ mEthernetNetworkAgent.connect(false);
+ waitForIdle();
+ verify(mStatsService).forceUpdateIfaces(any(Network[].class),
+ any(NetworkState[].class), any() /* anyString() doesn't match null */,
+ argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1
+ && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0])));
+ mEthernetNetworkAgent.disconnect();
+ reset(mStatsService);
+
+ // When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo
+ // does not return the VPN, so CS does not pass it to NetworkStatsService. This causes
+ // NetworkStatsFactory#adjustForTunAnd464Xlat not to attempt any VPN data migration, which
+ // is probably a performance improvement (though it's very unlikely that a VPN would declare
+ // no underlying networks).
+ // Also, for the same reason as above, the active interface passed in is null.
+ mService.setUnderlyingNetworksForVpn(new Network[0]);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
}
@@ -7052,6 +7173,14 @@ public class ConnectivityServiceTest {
return vpnNetworkAgent;
}
+ private TestNetworkAgentWrapper establishVpnForMyUid(LinkProperties lp)
+ throws Exception {
+ final int uid = Process.myUid();
+ final ArraySet ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ return establishVpn(lp, uid, ranges);
+ }
+
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
final PackageInfo packageInfo = new PackageInfo();
if (hasSystemPermission) {
From a083cb1ffed7a851f2748463a31904fc4c6b625d Mon Sep 17 00:00:00 2001
From: Lorenzo Colitti