summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java10
-rw-r--r--core/api/current.txt3
-rw-r--r--core/api/system-current.txt19
-rw-r--r--core/api/test-current.txt22
-rw-r--r--core/java/android/app/AppOpsManager.java64
-rw-r--r--core/java/android/app/time/LocationTimeZoneManager.java2
-rw-r--r--core/java/android/companion/DeviceNotAssociatedException.java2
-rw-r--r--core/java/android/content/pm/PackageManager.java17
-rw-r--r--core/java/android/hardware/SensorPrivacyManager.java114
-rw-r--r--core/java/android/os/BatteryConsumer.java4
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java18
-rw-r--r--core/java/com/android/internal/os/UsageBasedPowerEstimator.java4
-rw-r--r--core/java/com/android/internal/os/WifiPowerCalculator.java288
-rw-r--r--core/java/com/android/internal/os/WifiPowerEstimator.java126
-rw-r--r--core/java/com/android/internal/power/MeasuredEnergyStats.java26
-rw-r--r--core/java/com/android/server/SystemConfig.java9
-rw-r--r--core/proto/android/server/powerstatsservice.proto6
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java1
-rw-r--r--core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java128
-rw-r--r--core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java110
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java18
-rw-r--r--location/java/android/location/ILocationManager.aidl3
-rw-r--r--location/java/android/location/LocationManager.java25
-rw-r--r--location/java/android/location/LocationManagerInternal.java75
-rw-r--r--media/jni/android_media_tv_Tuner.cpp171
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/res/layout/qs_tile_label.xml1
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadButton.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadKey.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java6
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java9
-rw-r--r--services/core/java/com/android/server/SensorPrivacyService.java46
-rw-r--r--services/core/java/com/android/server/ServiceWatcher.java109
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java24
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java140
-rw-r--r--services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java4
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java34
-rw-r--r--services/core/java/com/android/server/location/LocationShellCommand.java4
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java6
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java3
-rw-r--r--services/core/java/com/android/server/location/provider/AbstractLocationProvider.java44
-rw-r--r--services/core/java/com/android/server/location/provider/DelegateLocationProvider.java2
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java41
-rw-r--r--services/core/java/com/android/server/location/provider/MockLocationProvider.java6
-rw-r--r--services/core/java/com/android/server/location/provider/MockableLocationProvider.java2
-rw-r--r--services/core/java/com/android/server/location/provider/PassiveLocationProvider.java3
-rw-r--r--services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java24
-rw-r--r--services/core/java/com/android/server/policy/AppOpsPolicy.java151
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsLogger.java22
-rw-r--r--services/core/java/com/android/server/powerstats/ProtoStreamUtils.java26
-rw-r--r--services/core/java/com/android/server/timedetector/DeviceConfig.java126
-rw-r--r--services/core/java/com/android/server/timedetector/ServerFlags.java238
-rw-r--r--services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java5
-rw-r--r--services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java51
-rw-r--r--services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java83
-rw-r--r--services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java249
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java13
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java32
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java10
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java7
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java31
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java166
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java3
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java4
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java21
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java4
105 files changed, 2471 insertions, 1138 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index fc2a409be2c2..040a1164fc73 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -887,8 +887,10 @@ public class JobSchedulerService extends com.android.server.SystemService
}
@Override
- public void onUserStarting(@NonNull TargetUser user) {
+ public void onUserUnlocked(@NonNull TargetUser user) {
synchronized (mLock) {
+ // Note that the user has started after its unlocked instead of when the user
+ // actually starts because the storage won't be decrypted until unlock.
mStartedUsers = ArrayUtils.appendInt(mStartedUsers, user.getUserIdentifier());
}
// Let's kick any outstanding jobs for this user.
@@ -896,12 +898,6 @@ public class JobSchedulerService extends com.android.server.SystemService
}
@Override
- public void onUserUnlocking(@NonNull TargetUser user) {
- // Let's kick any outstanding jobs for this user.
- mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
- }
-
- @Override
public void onUserStopping(@NonNull TargetUser user) {
synchronized (mLock) {
mStartedUsers = ArrayUtils.removeInt(mStartedUsers, user.getUserIdentifier());
diff --git a/core/api/current.txt b/core/api/current.txt
index 78c7f939d556..24f9aa03ff08 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9741,7 +9741,7 @@ package android.companion {
public interface DeviceFilter<D extends android.os.Parcelable> extends android.os.Parcelable {
}
- public class DeviceNotAssociatedException extends java.lang.Exception {
+ public class DeviceNotAssociatedException extends java.lang.RuntimeException {
}
public final class WifiDeviceFilter implements android.companion.DeviceFilter<android.net.wifi.ScanResult> {
@@ -19662,6 +19662,7 @@ package android.location {
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void addProximityAlert(double, double, float, long, @NonNull android.app.PendingIntent);
method public void addTestProvider(@NonNull String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
method public void addTestProvider(@NonNull String, @NonNull android.location.provider.ProviderProperties);
+ method public void addTestProvider(@NonNull String, @NonNull android.location.provider.ProviderProperties, @NonNull java.util.Set<java.lang.String>);
method @Deprecated public void clearTestProviderEnabled(@NonNull String);
method @Deprecated public void clearTestProviderLocation(@NonNull String);
method @Deprecated public void clearTestProviderStatus(@NonNull String);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 05360141d9dd..5734cbd8d84f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -176,6 +176,7 @@ package android {
field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
field public static final String OBSERVE_NETWORK_POLICY = "android.permission.OBSERVE_NETWORK_POLICY";
field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
+ field public static final String OBSERVE_SENSOR_PRIVACY = "android.permission.OBSERVE_SENSOR_PRIVACY";
field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS";
field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
@@ -2550,10 +2551,12 @@ package android.content.pm {
field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
+ field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
field public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur";
field @Deprecated public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
field public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION = "android.software.incremental_delivery_version";
+ field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
@@ -2908,6 +2911,22 @@ package android.hardware {
method public boolean injectSensorData(android.hardware.Sensor, float[], int, long);
}
+ public final class SensorPrivacyManager {
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+ }
+
+ public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
+ method public void onSensorPrivacyChanged(boolean);
+ }
+
+ public static class SensorPrivacyManager.Sensors {
+ field public static final int CAMERA = 2; // 0x2
+ field public static final int MICROPHONE = 1; // 0x1
+ }
+
}
package android.hardware.biometrics {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 835c49fccf7d..6231b958ae9a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -718,8 +718,10 @@ package android.content.pm {
method public void holdLock(android.os.IBinder, int);
method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>);
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
+ field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
+ field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
@@ -880,11 +882,21 @@ package android.graphics.fonts {
package android.hardware {
public final class SensorPrivacyManager {
- method public boolean isIndividualSensorPrivacyEnabled(int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacy(int, boolean);
- method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacyForProfileGroup(int, boolean);
- field public static final int INDIVIDUAL_SENSOR_CAMERA = 2; // 0x2
- field public static final int INDIVIDUAL_SENSOR_MICROPHONE = 1; // 0x1
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyForProfileGroup(int, boolean);
+ }
+
+ public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
+ method public void onSensorPrivacyChanged(boolean);
+ }
+
+ public static class SensorPrivacyManager.Sensors {
+ field public static final int CAMERA = 2; // 0x2
+ field public static final int MICROPHONE = 1; // 0x1
}
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2f3b50b17d51..160844aacc46 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1141,23 +1141,20 @@ public class AppOpsManager {
*
* @hide
*/
- // TODO: Add as AppProtoEnums
- public static final int OP_PHONE_CALL_MICROPHONE = 100;
+ public static final int OP_PHONE_CALL_MICROPHONE = AppProtoEnums.APP_OP_PHONE_CALL_MICROPHONE;
/**
* Phone call is using camera
*
* @hide
*/
- // TODO: Add as AppProtoEnums
- public static final int OP_PHONE_CALL_CAMERA = 101;
+ public static final int OP_PHONE_CALL_CAMERA = AppProtoEnums.APP_OP_PHONE_CALL_CAMERA;
/**
* Audio is being recorded for hotword detection.
*
* @hide
*/
- // TODO: Add as AppProtoEnums
- public static final int OP_RECORD_AUDIO_HOTWORD = 102;
+ public static final int OP_RECORD_AUDIO_HOTWORD = AppProtoEnums.APP_OP_RECORD_AUDIO_HOTWORD;
/**
* Manage credentials in the system KeyChain.
@@ -1184,10 +1181,29 @@ public class AppOpsManager {
*/
public static final int OP_SCHEDULE_EXACT_ALARM = AppProtoEnums.APP_OP_SCHEDULE_EXACT_ALARM;
+ /**
+ * Fine location being accessed by a location source, which is
+ * a component that already has location data since it is the one
+ * that produces location, which is it is a data source for
+ * location data.
+ *
+ * @hide
+ */
+ public static final int OP_FINE_LOCATION_SOURCE = AppProtoEnums.APP_OP_FINE_LOCATION_SOURCE;
+
+ /**
+ * Coarse location being accessed by a location source, which is
+ * a component that already has location data since it is the one
+ * that produces location, which is it is a data source for
+ * location data.
+ *
+ * @hide
+ */
+ public static final int OP_COARSE_LOCATION_SOURCE = AppProtoEnums.APP_OP_COARSE_LOCATION_SOURCE;
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 108;
+ public static final int _NUM_OP = 110;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1567,6 +1583,24 @@ public class AppOpsManager {
*/
public static final String OPSTR_SCHEDULE_EXACT_ALARM = "android:schedule_exact_alarm";
+ /**
+ * Fine location being accessed by a location source, which is
+ * a component that already has location since it is the one that
+ * produces location.
+ *
+ * @hide
+ */
+ public static final String OPSTR_FINE_LOCATION_SOURCE = "android:fine_location_source";
+
+ /**
+ * Coarse location being accessed by a location source, which is
+ * a component that already has location since it is the one that
+ * produces location.
+ *
+ * @hide
+ */
+ public static final String OPSTR_COARSE_LOCATION_SOURCE = "android:coarse_location_source";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1767,6 +1801,8 @@ public class AppOpsManager {
OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
OP_RECORD_AUDIO_OUTPUT, // RECORD_AUDIO_OUTPUT
OP_SCHEDULE_EXACT_ALARM, // SCHEDULE_EXACT_ALARM
+ OP_FINE_LOCATION, // OP_FINE_LOCATION_SOURCE
+ OP_COARSE_LOCATION, // OP_COARSE_LOCATION_SOURCE
};
/**
@@ -1881,6 +1917,8 @@ public class AppOpsManager {
OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
OPSTR_RECORD_AUDIO_OUTPUT,
OPSTR_SCHEDULE_EXACT_ALARM,
+ OPSTR_FINE_LOCATION_SOURCE,
+ OPSTR_COARSE_LOCATION_SOURCE,
};
/**
@@ -1996,6 +2034,8 @@ public class AppOpsManager {
"USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER",
"RECORD_AUDIO_OUTPUT",
"SCHEDULE_EXACT_ALARM",
+ "FINE_LOCATION_SOURCE",
+ "COARSE_LOCATION_SOURCE",
};
/**
@@ -2112,6 +2152,8 @@ public class AppOpsManager {
Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
null, // no permission for OP_RECORD_AUDIO_OUTPUT
Manifest.permission.SCHEDULE_EXACT_ALARM,
+ null, // no permission for OP_ACCESS_FINE_LOCATION_SOURCE,
+ null, // no permission for OP_ACCESS_COARSE_LOCATION_SOURCE,
};
/**
@@ -2228,6 +2270,8 @@ public class AppOpsManager {
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
null, // RECORD_AUDIO_OUTPUT
null, // SCHEDULE_EXACT_ALARM
+ null, // ACCESS_FINE_LOCATION_SOURCE
+ null, // ACCESS_COARSE_LOCATION_SOURCE
};
/**
@@ -2343,6 +2387,8 @@ public class AppOpsManager {
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
null, // RECORD_AUDIO_OUTPUT
null, // SCHEDULE_EXACT_ALARM
+ null, // ACCESS_FINE_LOCATION_SOURCE
+ null, // ACCESS_COARSE_LOCATION_SOURCE
};
/**
@@ -2457,6 +2503,8 @@ public class AppOpsManager {
AppOpsManager.MODE_DEFAULT, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_OUTPUT
AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM
+ AppOpsManager.MODE_ALLOWED, // ACCESS_FINE_LOCATION_SOURCE
+ AppOpsManager.MODE_ALLOWED, // ACCESS_COARSE_LOCATION_SOURCE
};
/**
@@ -2575,6 +2623,8 @@ public class AppOpsManager {
true, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
false, // RECORD_AUDIO_OUTPUT
false, // SCHEDULE_EXACT_ALARM
+ false, // ACCESS_FINE_LOCATION_SOURCE
+ false, // ACCESS_COARSE_LOCATION_SOURCE
};
/**
diff --git a/core/java/android/app/time/LocationTimeZoneManager.java b/core/java/android/app/time/LocationTimeZoneManager.java
index 71a800f2085e..066aadae1476 100644
--- a/core/java/android/app/time/LocationTimeZoneManager.java
+++ b/core/java/android/app/time/LocationTimeZoneManager.java
@@ -37,7 +37,7 @@ public final class LocationTimeZoneManager {
/**
* The name of the service for shell commands
*/
- public static final String SHELL_COMMAND_SERVICE_NAME = "location_time_zone_manager";
+ public static final String SERVICE_NAME = "location_time_zone_manager";
/**
* A shell command that starts the service (after stop).
diff --git a/core/java/android/companion/DeviceNotAssociatedException.java b/core/java/android/companion/DeviceNotAssociatedException.java
index bebb6c9ff7eb..f8a7a7cdeffe 100644
--- a/core/java/android/companion/DeviceNotAssociatedException.java
+++ b/core/java/android/companion/DeviceNotAssociatedException.java
@@ -23,7 +23,7 @@ import android.annotation.Nullable;
* An exception for a case when a given device was not
* {@link CompanionDeviceManager#associate associated} to the calling app.
*/
-public class DeviceNotAssociatedException extends Exception {
+public class DeviceNotAssociatedException extends RuntimeException {
/** @hide */
public DeviceNotAssociatedException(@Nullable String deviceName) {
super("Device not associated with the current app: " + deviceName);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9a3fbdcea9ff..ca882417394e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3623,11 +3623,26 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for
- * camera. When sensory privacy for the camera is enabled no camera data is send to clients,
+ * microphone. When sensory privacy for the microphone is enabled no microphone data is sent to
+ * clients, e.g. all audio data is silent.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for
+ * camera. When sensory privacy for the camera is enabled no camera data is sent to clients,
* e.g. the view finder in a camera app would appear blank.
*
* @hide
*/
+ @SystemApi
+ @TestApi
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle";
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index f4f9e1775d1a..e03c1f48773a 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -19,6 +19,7 @@ package android.hardware;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
@@ -33,6 +34,7 @@ import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
/**
* This class provides access to the sensor privacy services; sensor privacy allows the
@@ -42,9 +44,21 @@ import java.lang.annotation.RetentionPolicy;
*
* @hide
*/
+@SystemApi
@TestApi
@SystemService(Context.SENSOR_PRIVACY_SERVICE)
public final class SensorPrivacyManager {
+
+ /**
+ * @hide
+ */
+ public static final boolean USE_MICROPHONE_TOGGLE = true;
+
+ /**
+ * @hide
+ */
+ public static final boolean USE_CAMERA_TOGGLE = true;
+
/**
* Unique Id of this manager to identify to the service
* @hide
@@ -58,28 +72,39 @@ public final class SensorPrivacyManager {
public static final String EXTRA_SENSOR = SensorPrivacyManager.class.getName()
+ ".extra.sensor";
- /** Microphone
- * @hide */
- @TestApi
- public static final int INDIVIDUAL_SENSOR_MICROPHONE =
- SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
-
- /** Camera
- * @hide */
- @TestApi
- public static final int INDIVIDUAL_SENSOR_CAMERA =
- SensorPrivacyIndividualEnabledSensorProto.CAMERA;
-
/**
- * Individual sensors not listed in {@link Sensor}
+ * Individual sensors not listed in {@link Sensors}
* @hide
*/
- @IntDef(prefix = "INDIVIDUAL_SENSOR_", value = {
- INDIVIDUAL_SENSOR_MICROPHONE,
- INDIVIDUAL_SENSOR_CAMERA
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface IndividualSensor {}
+ @SystemApi
+ @TestApi
+ public static class Sensors {
+
+ private Sensors() {}
+
+ /** Microphone
+ * @hide */
+ @SystemApi
+ @TestApi
+ public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
+ /** Camera
+ * @hide */
+ @SystemApi
+ @TestApi
+ public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA;
+
+ /**
+ * Individual sensors not listed in {@link Sensors}
+ *
+ * @hide
+ */
+ @IntDef(value = {
+ MICROPHONE,
+ CAMERA
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Sensor {}
+ }
/**
* A class implementing this interface can register with the {@link
@@ -88,6 +113,8 @@ public final class SensorPrivacyManager {
*
* @hide
*/
+ @SystemApi
+ @TestApi
public interface OnSensorPrivacyChangedListener {
/**
* Callback invoked when the sensor privacy state changes.
@@ -165,7 +192,8 @@ public final class SensorPrivacyManager {
*
* @hide
*/
- public void addSensorPrivacyListener(final OnSensorPrivacyChangedListener listener) {
+ @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public void addSensorPrivacyListener(@NonNull final OnSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
ISensorPrivacyListener iListener = mListeners.get(listener);
if (iListener == null) {
@@ -196,15 +224,37 @@ public final class SensorPrivacyManager {
*
* @hide
*/
- public void addSensorPrivacyListener(@IndividualSensor int sensor,
- final OnSensorPrivacyChangedListener listener) {
+ @SystemApi
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public void addSensorPrivacyListener(@Sensors.Sensor int sensor,
+ @NonNull OnSensorPrivacyChangedListener listener) {
+ addSensorPrivacyListener(sensor, mContext.getMainExecutor(), listener);
+ }
+
+ /**
+ * Registers a new listener to receive notification when the state of sensor privacy
+ * changes.
+ *
+ * @param sensor the sensor to listen to changes to
+ * @param executor the executor to dispatch the callback on
+ * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
+ * privacy changes.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor,
+ @NonNull OnSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
ISensorPrivacyListener iListener = mListeners.get(listener);
if (iListener == null) {
iListener = new ISensorPrivacyListener.Stub() {
@Override
public void onSensorPrivacyChanged(boolean enabled) {
- listener.onSensorPrivacyChanged(enabled);
+ executor.execute(() -> listener.onSensorPrivacyChanged(enabled));
}
};
mListeners.put(listener, iListener);
@@ -228,7 +278,10 @@ public final class SensorPrivacyManager {
*
* @hide
*/
- public void removeSensorPrivacyListener(OnSensorPrivacyChangedListener listener) {
+ @SystemApi
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public void removeSensorPrivacyListener(@NonNull OnSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
ISensorPrivacyListener iListener = mListeners.get(listener);
if (iListener != null) {
@@ -249,6 +302,7 @@ public final class SensorPrivacyManager {
*
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public boolean isSensorPrivacyEnabled() {
try {
return mService.isSensorPrivacyEnabled();
@@ -264,8 +318,10 @@ public final class SensorPrivacyManager {
*
* @hide
*/
+ @SystemApi
@TestApi
- public boolean isIndividualSensorPrivacyEnabled(@IndividualSensor int sensor) {
+ @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
try {
return mService.isIndividualSensorPrivacyEnabled(mContext.getUserId(), sensor);
} catch (RemoteException e) {
@@ -283,8 +339,7 @@ public final class SensorPrivacyManager {
*/
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
- public void setIndividualSensorPrivacy(@IndividualSensor int sensor,
- boolean enable) {
+ public void setSensorPrivacy(@Sensors.Sensor int sensor, boolean enable) {
try {
mService.setIndividualSensorPrivacy(mContext.getUserId(), sensor, enable);
} catch (RemoteException e) {
@@ -303,7 +358,7 @@ public final class SensorPrivacyManager {
*/
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
- public void setIndividualSensorPrivacyForProfileGroup(@IndividualSensor int sensor,
+ public void setSensorPrivacyForProfileGroup(@Sensors.Sensor int sensor,
boolean enable) {
try {
mService.setIndividualSensorPrivacyForProfileGroup(mContext.getUserId(), sensor,
@@ -321,7 +376,8 @@ public final class SensorPrivacyManager {
*
* @hide
*/
- public void suppressIndividualSensorPrivacyReminders(@NonNull String packageName,
+ @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ public void suppressSensorPrivacyReminders(@NonNull String packageName,
boolean suppress) {
try {
mService.suppressIndividualSensorPrivacyReminders(mContext.getUserId(), packageName,
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index c6efaace76d7..2b6f336848c3 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -47,6 +47,7 @@ public abstract class BatteryConsumer {
POWER_COMPONENT_SYSTEM_SERVICES,
POWER_COMPONENT_SENSORS,
POWER_COMPONENT_GNSS,
+ POWER_COMPONENT_WIFI,
POWER_COMPONENT_WAKELOCK,
POWER_COMPONENT_SCREEN,
POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
@@ -66,6 +67,7 @@ public abstract class BatteryConsumer {
public static final int POWER_COMPONENT_MOBILE_RADIO = 8;
public static final int POWER_COMPONENT_SENSORS = 9;
public static final int POWER_COMPONENT_GNSS = 10;
+ public static final int POWER_COMPONENT_WIFI = 11;
public static final int POWER_COMPONENT_WAKELOCK = 12;
public static final int POWER_COMPONENT_SCREEN = 13;
// Power that is re-attributed to other battery consumers. For example, for System Server
@@ -94,6 +96,7 @@ public abstract class BatteryConsumer {
TIME_COMPONENT_MOBILE_RADIO,
TIME_COMPONENT_SENSORS,
TIME_COMPONENT_GNSS,
+ TIME_COMPONENT_WIFI,
TIME_COMPONENT_WAKELOCK,
TIME_COMPONENT_SCREEN,
})
@@ -112,6 +115,7 @@ public abstract class BatteryConsumer {
public static final int TIME_COMPONENT_MOBILE_RADIO = 8;
public static final int TIME_COMPONENT_SENSORS = 9;
public static final int TIME_COMPONENT_GNSS = 10;
+ public static final int TIME_COMPONENT_WIFI = 11;
public static final int TIME_COMPONENT_WAKELOCK = 12;
public static final int TIME_COMPONENT_SCREEN = 13;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 54095bd86887..62e1ee19b25e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -7965,16 +7965,14 @@ public class BatteryStatsImpl extends BatteryStats {
/** Adds the given energy to the given standard energy bucket for this uid. */
private void addEnergyToStandardBucketLocked(long energyDeltaUJ,
- @StandardEnergyBucket int energyBucket, boolean accumulate) {
+ @StandardEnergyBucket int energyBucket) {
getOrCreateMeasuredEnergyStatsLocked()
- .updateStandardBucket(energyBucket, energyDeltaUJ, accumulate);
+ .updateStandardBucket(energyBucket, energyDeltaUJ);
}
/** Adds the given energy to the given custom energy bucket for this uid. */
- private void addEnergyToCustomBucketLocked(long energyDeltaUJ, int energyBucket,
- boolean accumulate) {
- getOrCreateMeasuredEnergyStatsLocked()
- .updateCustomBucket(energyBucket, energyDeltaUJ, accumulate);
+ private void addEnergyToCustomBucketLocked(long energyDeltaUJ, int energyBucket) {
+ getOrCreateMeasuredEnergyStatsLocked().updateCustomBucket(energyBucket, energyDeltaUJ);
}
/**
@@ -12468,7 +12466,7 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
- mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ, true);
+ mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ);
// Now we blame individual apps, but only if the display was ON.
if (energyBucket != MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON) {
@@ -12506,7 +12504,7 @@ public class BatteryStatsImpl extends BatteryStats {
final long appDisplayEnergyMJ =
(totalDisplayEnergyMJ * fgTimeMs + (totalFgTimeMs / 2))
/ totalFgTimeMs;
- uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true);
+ uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket);
// To mitigate round-off errors, remove this app from numerator & denominator totals
totalDisplayEnergyMJ -= appDisplayEnergyMJ;
@@ -12533,7 +12531,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (mGlobalMeasuredEnergyStats == null) return;
if (!mOnBatteryInternal || mIgnoreNextExternalStats || totalEnergyUJ <= 0) return;
- mGlobalMeasuredEnergyStats.updateCustomBucket(customEnergyBucket, totalEnergyUJ, true);
+ mGlobalMeasuredEnergyStats.updateCustomBucket(customEnergyBucket, totalEnergyUJ);
if (uidEnergies == null) return;
final int numUids = uidEnergies.size();
@@ -12543,7 +12541,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (uidEnergyUJ == 0) continue;
final Uid uidObj = getAvailableUidStatsLocked(uidInt);
if (uidObj != null) {
- uidObj.addEnergyToCustomBucketLocked(uidEnergyUJ, customEnergyBucket, true);
+ uidObj.addEnergyToCustomBucketLocked(uidEnergyUJ, customEnergyBucket);
} else {
// Ignore any uid not already known to BatteryStats, rather than creating a new Uid.
// Otherwise we could end up reviving dead Uids. Note that the CPU data is updated
diff --git a/core/java/com/android/internal/os/UsageBasedPowerEstimator.java b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java
index 5910b61f0f6f..fd5201431eab 100644
--- a/core/java/com/android/internal/os/UsageBasedPowerEstimator.java
+++ b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java
@@ -32,6 +32,10 @@ public class UsageBasedPowerEstimator {
mAveragePowerMahPerMs = averagePowerMilliAmp / MILLIS_IN_HOUR;
}
+ public boolean isSupported() {
+ return mAveragePowerMahPerMs != 0;
+ }
+
/**
* Given a {@link BatteryStats.Timer}, returns the accumulated duration.
*/
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index 63763f76c6ed..98f613fc1c40 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -15,8 +15,13 @@
*/
package com.android.internal.os;
+import android.os.BatteryConsumer;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
@@ -30,25 +35,93 @@ import java.util.List;
public class WifiPowerCalculator extends PowerCalculator {
private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
private static final String TAG = "WifiPowerCalculator";
- private final double mIdleCurrentMa;
- private final double mTxCurrentMa;
- private final double mRxCurrentMa;
- private final PowerProfile mPowerProfile;
+ private final UsageBasedPowerEstimator mIdlePowerEstimator;
+ private final UsageBasedPowerEstimator mTxPowerEstimator;
+ private final UsageBasedPowerEstimator mRxPowerEstimator;
+ private final UsageBasedPowerEstimator mPowerOnPowerEstimator;
+ private final UsageBasedPowerEstimator mScanPowerEstimator;
+ private final UsageBasedPowerEstimator mBatchScanPowerEstimator;
private final boolean mHasWifiPowerController;
- private double mTotalAppPowerDrain = 0;
- private long mTotalAppRunningTime = 0;
- private WifiPowerEstimator mWifiPowerEstimator;
- private boolean mHasWifiPowerReporting;
+ private final double mWifiPowerPerPacket;
+
+ private static class PowerDurationAndTraffic {
+ public double powerMah;
+ public long durationMs;
+
+ public long wifiRxPackets;
+ public long wifiTxPackets;
+ public long wifiRxBytes;
+ public long wifiTxBytes;
+ }
public WifiPowerCalculator(PowerProfile profile) {
- mPowerProfile = profile;
+ mPowerOnPowerEstimator = new UsageBasedPowerEstimator(
+ profile.getAveragePower(PowerProfile.POWER_WIFI_ON));
+ mScanPowerEstimator = new UsageBasedPowerEstimator(
+ profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN));
+ mBatchScanPowerEstimator = new UsageBasedPowerEstimator(
+ profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN));
+ mIdlePowerEstimator = new UsageBasedPowerEstimator(
+ profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE));
+ mTxPowerEstimator = new UsageBasedPowerEstimator(
+ profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX));
+ mRxPowerEstimator = new UsageBasedPowerEstimator(
+ profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX));
+
+ mWifiPowerPerPacket = getWifiPowerPerPacket(profile);
+
+ mHasWifiPowerController =
+ mIdlePowerEstimator.isSupported() && mTxPowerEstimator.isSupported()
+ && mRxPowerEstimator.isSupported();
+ }
+
+ @Override
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+
+ // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point,
+ // so always check this field.
+ final boolean hasWifiPowerReporting =
+ mHasWifiPowerController && batteryStats.hasWifiActivityReporting();
+
+ final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
+ builder.getOrCreateSystemBatteryConsumerBuilder(
+ SystemBatteryConsumer.DRAIN_TYPE_WIFI);
- mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
- mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);
- mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);
+ long totalAppDurationMs = 0;
+ double totalAppPowerMah = 0;
+ final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic();
+ final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+ calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), rawRealtimeUs,
+ BatteryStats.STATS_SINCE_CHARGED,
+ hasWifiPowerReporting);
- mHasWifiPowerController = mIdleCurrentMa != 0 && mTxCurrentMa != 0 && mRxCurrentMa != 0;
- mWifiPowerEstimator = new WifiPowerEstimator(mPowerProfile);
+ totalAppDurationMs += powerDurationAndTraffic.durationMs;
+ totalAppPowerMah += powerDurationAndTraffic.powerMah;
+
+ app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI,
+ powerDurationAndTraffic.durationMs);
+ app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
+ powerDurationAndTraffic.powerMah);
+
+ if (app.getUid() == Process.WIFI_UID) {
+ systemBatteryConsumerBuilder.addUidBatteryConsumer(app);
+ app.excludeFromBatteryUsageStats();
+ }
+ }
+
+ calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs,
+ BatteryStats.STATS_SINCE_CHARGED,
+ hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah);
+
+ systemBatteryConsumerBuilder
+ .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI,
+ powerDurationAndTraffic.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
+ powerDurationAndTraffic.powerMah);
}
/**
@@ -64,100 +137,151 @@ public class WifiPowerCalculator extends PowerCalculator {
// batteryStats.hasWifiActivityReporting can change if we get energy data at a later point,
// so always check this field.
- mHasWifiPowerReporting = mHasWifiPowerController && batteryStats.hasWifiActivityReporting();
-
- super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
+ final boolean hasWifiPowerReporting =
+ mHasWifiPowerController && batteryStats.hasWifiActivityReporting();
- BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0);
- calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType);
+ final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0);
+ long totalAppDurationMs = 0;
+ double totalAppPowerMah = 0;
+ final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic();
for (int i = sippers.size() - 1; i >= 0; i--) {
- BatterySipper app = sippers.get(i);
- if (app.getUid() == Process.WIFI_UID) {
- if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs);
- app.isAggregated = true;
- bs.add(app);
+ final BatterySipper app = sippers.get(i);
+ if (app.drainType == BatterySipper.DrainType.APP) {
+ calculateApp(powerDurationAndTraffic, app.uidObj, rawRealtimeUs, statsType,
+ hasWifiPowerReporting);
+
+ totalAppDurationMs += powerDurationAndTraffic.durationMs;
+ totalAppPowerMah += powerDurationAndTraffic.powerMah;
+
+ app.wifiPowerMah = powerDurationAndTraffic.powerMah;
+ app.wifiRunningTimeMs = powerDurationAndTraffic.durationMs;
+ app.wifiRxBytes = powerDurationAndTraffic.wifiRxBytes;
+ app.wifiRxPackets = powerDurationAndTraffic.wifiRxPackets;
+ app.wifiTxBytes = powerDurationAndTraffic.wifiTxBytes;
+ app.wifiTxPackets = powerDurationAndTraffic.wifiTxPackets;
+ if (app.getUid() == Process.WIFI_UID) {
+ if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs);
+ app.isAggregated = true;
+ bs.add(app);
+ }
}
}
+
+ calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, statsType,
+ hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah);
+
+ bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs;
+ bs.wifiPowerMah += powerDurationAndTraffic.powerMah;
+
if (bs.sumPower() > 0) {
sippers.add(bs);
}
}
- @Override
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- if (!mHasWifiPowerReporting) {
- mWifiPowerEstimator.calculateApp(app, u, rawRealtimeUs, rawUptimeUs, statsType);
- return;
- }
-
- final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
- if (counter == null) {
- return;
- }
-
- final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
- final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType);
- final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);
- app.wifiRunningTimeMs = idleTime + rxTime + txTime;
- mTotalAppRunningTime += app.wifiRunningTimeMs;
-
- app.wifiPowerMah =
- ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa))
- / (1000 * 60 * 60);
- mTotalAppPowerDrain += app.wifiPowerMah;
-
- app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
+ private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u,
+ long rawRealtimeUs,
+ int statsType, boolean hasWifiPowerReporting) {
+ powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets(
+ BatteryStats.NETWORK_WIFI_RX_DATA,
statsType);
- app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
+ powerDurationAndTraffic.wifiTxPackets = u.getNetworkActivityPackets(
+ BatteryStats.NETWORK_WIFI_TX_DATA,
statsType);
- app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
+ powerDurationAndTraffic.wifiRxBytes = u.getNetworkActivityBytes(
+ BatteryStats.NETWORK_WIFI_RX_DATA,
statsType);
- app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
+ powerDurationAndTraffic.wifiTxBytes = u.getNetworkActivityBytes(
+ BatteryStats.NETWORK_WIFI_TX_DATA,
statsType);
- if (DEBUG && app.wifiPowerMah != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime + "ms tx=" +
- txTime + "ms power=" + formatCharge(app.wifiPowerMah));
- }
- }
+ if (hasWifiPowerReporting) {
+ final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
+ if (counter != null) {
+ final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
+ final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType);
+ final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);
+
+ powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime;
+ powerDurationAndTraffic.powerMah = mIdlePowerEstimator.calculatePower(idleTime)
+ + mTxPowerEstimator.calculatePower(txTime)
+ + mRxPowerEstimator.calculatePower(rxTime);
- private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- if (!mHasWifiPowerReporting) {
- mWifiPowerEstimator.calculateRemaining(app, stats, rawRealtimeUs, rawUptimeUs,
- statsType);
- return;
+ if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
+ Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime
+ + "ms tx=" + txTime + "ms power=" + formatCharge(
+ powerDurationAndTraffic.powerMah));
+ }
+ }
+ } else {
+ final double wifiPacketPower = (
+ powerDurationAndTraffic.wifiRxPackets + powerDurationAndTraffic.wifiTxPackets)
+ * mWifiPowerPerPacket;
+ final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
+ final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
+ long batchScanTimeMs = 0;
+ for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
+ batchScanTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
+ }
+
+ powerDurationAndTraffic.durationMs = wifiRunningTime;
+ powerDurationAndTraffic.powerMah = wifiPacketPower
+ + mPowerOnPowerEstimator.calculatePower(wifiRunningTime)
+ + mScanPowerEstimator.calculatePower(wifiScanTimeMs)
+ + mBatchScanPowerEstimator.calculatePower(batchScanTimeMs);
+
+ if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
+ Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(
+ powerDurationAndTraffic.powerMah));
+ }
}
+ }
- final BatteryStats.ControllerActivityCounter counter = stats.getWifiControllerActivity();
+ private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic,
+ BatteryStats stats, long rawRealtimeUs,
+ int statsType, boolean hasWifiPowerReporting, long totalAppDurationMs,
+ double totalAppPowerMah) {
+ long totalDurationMs;
+ double totalPowerMah;
+ if (hasWifiPowerReporting) {
+ final BatteryStats.ControllerActivityCounter counter =
+ stats.getWifiControllerActivity();
- final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
- final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
- final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
+ final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
+ final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
+ final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
- app.wifiRunningTimeMs = Math.max(0,
- (idleTimeMs + rxTimeMs + txTimeMs) - mTotalAppRunningTime);
+ totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs;
- double powerDrainMah = counter.getPowerCounter().getCountLocked(statsType)
- / (double) (1000 * 60 * 60);
- if (powerDrainMah == 0) {
- // Some controllers do not report power drain, so we can calculate it here.
- powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
- + (rxTimeMs * mRxCurrentMa)) / (1000 * 60 * 60);
+ totalPowerMah =
+ counter.getPowerCounter().getCountLocked(statsType) / (double) (1000 * 60 * 60);
+ if (totalPowerMah == 0) {
+ // Some controllers do not report power drain, so we can calculate it here.
+ totalPowerMah = mIdlePowerEstimator.calculatePower(idleTimeMs)
+ + mTxPowerEstimator.calculatePower(txTimeMs)
+ + mRxPowerEstimator.calculatePower(rxTimeMs);
+ }
+ } else {
+ totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000;
+ totalPowerMah = mPowerOnPowerEstimator.calculatePower(totalDurationMs);
}
- app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain);
+
+ powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs);
+ powerDurationAndTraffic.powerMah = Math.max(0, totalPowerMah - totalAppPowerMah);
if (DEBUG) {
- Log.d(TAG, "left over WiFi power: " + formatCharge(app.wifiPowerMah));
+ Log.d(TAG, "left over WiFi power: " + formatCharge(powerDurationAndTraffic.powerMah));
}
}
- @Override
- public void reset() {
- mTotalAppPowerDrain = 0;
- mTotalAppRunningTime = 0;
- mWifiPowerEstimator.reset();
+ /**
+ * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB.
+ */
+ private static double getWifiPowerPerPacket(PowerProfile profile) {
+ // TODO(b/179392913): Extract average bit rates from system
+ final long wifiBps = 1000000;
+ final double averageWifiActivePower =
+ profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) / 3600;
+ return averageWifiActivePower / (((double) wifiBps) / 8 / 2048);
}
}
diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java
deleted file mode 100644
index d0a105cfd958..000000000000
--- a/core/java/com/android/internal/os/WifiPowerEstimator.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import android.os.BatteryStats;
-import android.os.Process;
-import android.os.UserHandle;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.util.List;
-
-/**
- * Estimates WiFi power usage based on timers in BatteryStats.
- */
-public class WifiPowerEstimator extends PowerCalculator {
- private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
- private static final String TAG = "WifiPowerEstimator";
- private final double mWifiPowerPerPacket;
- private final double mWifiPowerOn;
- private final double mWifiPowerScan;
- private final double mWifiPowerBatchScan;
- private long mTotalAppWifiRunningTimeMs = 0;
-
- public WifiPowerEstimator(PowerProfile profile) {
- mWifiPowerPerPacket = getWifiPowerPerPacket(profile);
- mWifiPowerOn = profile.getAveragePower(PowerProfile.POWER_WIFI_ON);
- mWifiPowerScan = profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN);
- mWifiPowerBatchScan = profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN);
- }
-
- @Override
- public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
- long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
-
- BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0);
- calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType);
-
- for (int i = sippers.size() - 1; i >= 0; i--) {
- BatterySipper app = sippers.get(i);
- if (app.getUid() == Process.WIFI_UID) {
- if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs);
- app.isAggregated = true;
- bs.add(app);
- }
- }
- if (bs.sumPower() > 0) {
- sippers.add(bs);
- }
- }
-
- /**
- * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB.
- */
- private static double getWifiPowerPerPacket(PowerProfile profile) {
- final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
- final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
- / 3600;
- return WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048);
- }
-
- @Override
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
- statsType);
- app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
- statsType);
- app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
- statsType);
- app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
- statsType);
-
- final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets)
- * mWifiPowerPerPacket;
-
- app.wifiRunningTimeMs = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
- mTotalAppWifiRunningTimeMs += app.wifiRunningTimeMs;
- final double wifiLockPower = (app.wifiRunningTimeMs * mWifiPowerOn) / (1000*60*60);
-
- final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
- final double wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000*60*60);
-
- double wifiBatchScanPower = 0;
- for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
- final long batchScanTimeMs =
- u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
- final double batchScanPower = (batchScanTimeMs * mWifiPowerBatchScan) / (1000*60*60);
- wifiBatchScanPower += batchScanPower;
- }
-
- app.wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
- if (DEBUG && app.wifiPowerMah != 0) {
- Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(app.wifiPowerMah));
- }
- }
-
- void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- final long totalRunningTimeMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType)
- / 1000;
- final double powerDrain = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn)
- / (1000*60*60);
- app.wifiRunningTimeMs = totalRunningTimeMs;
- app.wifiPowerMah = Math.max(0, powerDrain);
- }
-
- @Override
- public void reset() {
- mTotalAppWifiRunningTimeMs = 0;
- }
-}
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index d7b4d78c56cf..d49203c731e9 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -193,34 +193,30 @@ public class MeasuredEnergyStats {
return mAccumulatedEnergiesMicroJoules.length;
}
- // TODO: Get rid of the 'accumulate' boolean. It's always true.
/** Updates the given standard energy bucket with the given energy if accumulate is true. */
- public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ,
- boolean accumulate) {
+ public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ) {
checkValidStandardBucket(bucket);
- updateEntry(bucket, energyDeltaUJ, accumulate);
+ updateEntry(bucket, energyDeltaUJ);
}
/** Updates the given custom energy bucket with the given energy if accumulate is true. */
- public void updateCustomBucket(int customBucket, long energyDeltaUJ, boolean accumulate) {
+ public void updateCustomBucket(int customBucket, long energyDeltaUJ) {
if (!isValidCustomBucket(customBucket)) {
Slog.e(TAG, "Attempted to update invalid custom bucket " + customBucket);
return;
}
final int index = customBucketToIndex(customBucket);
- updateEntry(index, energyDeltaUJ, accumulate);
+ updateEntry(index, energyDeltaUJ);
}
/** Updates the given index with the given energy if accumulate is true. */
- private void updateEntry(int index, long energyDeltaUJ, boolean accumulate) {
- if (accumulate) {
- if (mAccumulatedEnergiesMicroJoules[index] >= 0L) {
- mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ;
- } else {
- Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket "
- + getBucketName(index) + " whose value was "
- + mAccumulatedEnergiesMicroJoules[index]);
- }
+ private void updateEntry(int index, long energyDeltaUJ) {
+ if (mAccumulatedEnergiesMicroJoules[index] >= 0L) {
+ mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ;
+ } else {
+ Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket "
+ + getBucketName(index) + " whose value was "
+ + mAccumulatedEnergiesMicroJoules[index]);
}
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 671cb8481dd9..b40ffb0136f2 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -24,6 +24,7 @@ import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
+import android.hardware.SensorPrivacyManager;
import android.os.Build;
import android.os.CarrierAssociatedAppEntry;
import android.os.Environment;
@@ -1252,6 +1253,14 @@ public class SystemConfig {
addFeature(PackageManager.FEATURE_CROSS_LAYER_BLUR, 0);
}
+ if (SensorPrivacyManager.USE_MICROPHONE_TOGGLE) {
+ addFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE, 0);
+ }
+
+ if (SensorPrivacyManager.USE_CAMERA_TOGGLE) {
+ addFeature(PackageManager.FEATURE_CAMERA_TOGGLE, 0);
+ }
+
for (String featureName : mUnavailableFeatures) {
removeFeature(featureName);
}
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
index 4b1ee02a6c30..f64e146f87b7 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -144,7 +144,7 @@ message StateResidencyProto {
*/
optional int64 total_state_entry_count = 3;
/**
- * Last time this state was entered. Time in milliseconds since boot
+ * Last time this state was entered. Walltime in milliseconds since Unix epoch.
*/
optional int64 last_entry_timestamp_ms = 4;
}
@@ -208,7 +208,7 @@ message EnergyConsumerResultProto {
/** Unique index identifying the energy consumer. */
optional int32 id = 1;
- /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
+ /** Walltime in milliseconds since Unix epoch */
optional int64 timestamp_ms = 2;
/** Accumulated energy since device boot in microwatt-seconds (uWs) */
@@ -247,7 +247,7 @@ message EnergyMeasurementProto {
*/
optional int32 id = 1;
- /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
+ /** Walltime in milliseconds since Unix epoch */
optional int64 timestamp_ms = 2;
/** Accumulated energy since device boot in microwatt-seconds (uWs) */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7ad016c8ce47..a89043d813f5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5431,6 +5431,12 @@
@hide -->
<permission android:name="android.permission.MANAGE_SENSOR_PRIVACY"
android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows sensor privacy changes to be observed.
+ @hide -->
+ <permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY"
+ android:protectionLevel="signature|installer" />
+
<!-- @SystemApi Permission that protects the {@link Intent#ACTION_REVIEW_ACCESSIBILITY_SERVICES}
intent.
@hide -->
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index b819d9edb2a8..a1beecc83f43 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -71,6 +71,7 @@ import org.junit.runners.Suite;
UserPowerCalculatorTest.class,
VideoPowerCalculatorTest.class,
WakelockPowerCalculatorTest.class,
+ WifiPowerCalculatorTest.class,
com.android.internal.power.MeasuredEnergyStatsTest.class
})
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
new file mode 100644
index 000000000000..e1005457c289
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.NetworkCapabilities;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.Process;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
+import android.os.WorkSource;
+import android.os.connectivity.WifiActivityEnergyInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WifiPowerCalculatorTest {
+ private static final double PRECISION = 0.00001;
+
+ private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+
+ @Rule
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0)
+ .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX, 480.0)
+ .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX, 720.0)
+ .setAveragePower(PowerProfile.POWER_WIFI_ON, 360.0)
+ .setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0)
+ .setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0)
+ .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0);
+
+ @Test
+ public void testPowerControllerBasedModel() {
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+ batteryStats.noteNetworkInterfaceForTransports("wifi",
+ new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+
+ NetworkStats networkStats = new NetworkStats(10000, 1)
+ .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)
+ .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
+ mStatsRule.setNetworkStats(networkStats);
+
+ WifiActivityEnergyInfo energyInfo = new WifiActivityEnergyInfo(10000,
+ WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000);
+
+ batteryStats.updateWifiState(energyInfo, 1000, 1000);
+
+ WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+ mStatsRule.apply(calculator);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+ assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+ .isEqualTo(1423);
+ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.2214666);
+
+ SystemBatteryConsumer systemConsumer =
+ mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
+ assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+ .isEqualTo(5577);
+ assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.645200);
+ }
+
+ @Test
+ public void testTimerBasedModel() {
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+ batteryStats.noteNetworkInterfaceForTransports("wifi",
+ new int[]{NetworkCapabilities.TRANSPORT_WIFI});
+
+ NetworkStats networkStats = new NetworkStats(10000, 1)
+ .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)
+ .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
+ mStatsRule.setNetworkStats(networkStats);
+
+ batteryStats.noteWifiScanStartedLocked(APP_UID, 1000, 1000);
+ batteryStats.noteWifiScanStoppedLocked(APP_UID, 2000, 2000);
+ batteryStats.noteWifiRunningLocked(new WorkSource(APP_UID), 3000, 3000);
+ batteryStats.noteWifiStoppedLocked(new WorkSource(APP_UID), 4000, 4000);
+ batteryStats.noteWifiRunningLocked(new WorkSource(Process.WIFI_UID), 1111, 2222);
+ batteryStats.noteWifiStoppedLocked(new WorkSource(Process.WIFI_UID), 3333, 4444);
+
+ // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
+ // on the packet counts.
+ batteryStats.updateWifiState(/* energyInfo */ null, 1000, 1000);
+
+ WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
+ mStatsRule.apply(calculator);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+ assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+ .isEqualTo(1000);
+ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.8231573);
+
+ SystemBatteryConsumer systemConsumer =
+ mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
+ assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI))
+ .isEqualTo(2222);
+ assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.8759216);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index 5fd5a7838c3a..d217bce24b9c 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -81,11 +81,11 @@ public class MeasuredEnergyStatsTest {
final MeasuredEnergyStats stats
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
- stats.updateCustomBucket(0, 50, true);
- stats.updateCustomBucket(1, 60, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+ stats.updateCustomBucket(0, 50);
+ stats.updateCustomBucket(1, 60);
final MeasuredEnergyStats newStats = MeasuredEnergyStats.createFromTemplate(stats);
@@ -114,11 +114,11 @@ public class MeasuredEnergyStatsTest {
final MeasuredEnergyStats stats
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
- stats.updateCustomBucket(0, 50, true);
- stats.updateCustomBucket(1, 60, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+ stats.updateCustomBucket(0, 50);
+ stats.updateCustomBucket(1, 60);
final Parcel parcel = Parcel.obtain();
stats.writeToParcel(parcel);
@@ -149,11 +149,11 @@ public class MeasuredEnergyStatsTest {
final MeasuredEnergyStats stats
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
- stats.updateCustomBucket(0, 50, true);
- stats.updateCustomBucket(1, 60, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+ stats.updateCustomBucket(0, 50);
+ stats.updateCustomBucket(1, 60);
final Parcel parcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
@@ -185,17 +185,17 @@ public class MeasuredEnergyStatsTest {
final MeasuredEnergyStats template
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
- template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
- template.updateCustomBucket(0, 50, true);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+ template.updateCustomBucket(0, 50);
final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true);
- stats.updateCustomBucket(0, 315, true);
- stats.updateCustomBucket(1, 316, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63);
+ stats.updateCustomBucket(0, 315);
+ stats.updateCustomBucket(1, 316);
final Parcel parcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
@@ -243,8 +243,8 @@ public class MeasuredEnergyStatsTest {
final MeasuredEnergyStats stats
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
// Accumulate energy in one bucket and one custom bucket, the rest should be zero
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
- stats.updateCustomBucket(1, 60, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200);
+ stats.updateCustomBucket(1, 60);
// Let's try parcelling with including zeros
final Parcel includeZerosParcel = Parcel.obtain();
@@ -305,11 +305,11 @@ public class MeasuredEnergyStatsTest {
final MeasuredEnergyStats stats
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
- stats.updateCustomBucket(0, 50, true);
- stats.updateCustomBucket(1, 60, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+ stats.updateCustomBucket(0, 50);
+ stats.updateCustomBucket(1, 60);
final Parcel parcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
@@ -331,14 +331,14 @@ public class MeasuredEnergyStatsTest {
final MeasuredEnergyStats template
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
- template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
- template.updateCustomBucket(0, 50, true);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+ template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+ template.updateCustomBucket(0, 50);
final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L);
final Parcel parcel = Parcel.obtain();
MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
@@ -369,14 +369,14 @@ public class MeasuredEnergyStatsTest {
final MeasuredEnergyStats stats
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
- stats.updateCustomBucket(0, 50, true);
- stats.updateCustomBucket(1, 60, true);
- stats.updateCustomBucket(0, 3, true);
+ stats.updateCustomBucket(0, 50);
+ stats.updateCustomBucket(1, 60);
+ stats.updateCustomBucket(0, 3);
assertEquals(15, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
assertEquals(ENERGY_DATA_UNAVAILABLE,
@@ -409,10 +409,10 @@ public class MeasuredEnergyStatsTest {
final MeasuredEnergyStats stats
= new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 3);
- stats.updateCustomBucket(0, 50, true);
- stats.updateCustomBucket(1, 60, true);
- stats.updateCustomBucket(2, 13, true);
- stats.updateCustomBucket(1, 70, true);
+ stats.updateCustomBucket(0, 50);
+ stats.updateCustomBucket(1, 60);
+ stats.updateCustomBucket(2, 13);
+ stats.updateCustomBucket(1, 70);
final long[] output = stats.getAccumulatedCustomBucketEnergies();
assertEquals(3, output.length);
@@ -449,11 +449,11 @@ public class MeasuredEnergyStatsTest {
final MeasuredEnergyStats stats
= new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true);
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
- stats.updateCustomBucket(0, 50, true);
- stats.updateCustomBucket(1, 60, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40);
+ stats.updateCustomBucket(0, 50);
+ stats.updateCustomBucket(1, 60);
MeasuredEnergyStats.resetIfNotNull(stats);
// All energy should be reset to 0
@@ -471,10 +471,10 @@ public class MeasuredEnergyStatsTest {
}
// Values should increase as usual.
- stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70, true);
+ stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70);
assertEquals(70L, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON));
- stats.updateCustomBucket(1, 12, true);
+ stats.updateCustomBucket(1, 12);
assertEquals(12L, stats.getAccumulatedCustomBucketEnergy(1));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 4564af438f32..f118b1e0b7a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -50,6 +50,7 @@ import com.android.internal.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Contains information about the layout-properties of a display. This refers to internal layout
@@ -82,6 +83,31 @@ public class DisplayLayout {
private boolean mHasStatusBar = false;
private int mNavBarFrameHeight = 0;
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DisplayLayout)) return false;
+ final DisplayLayout other = (DisplayLayout) o;
+ return mUiMode == other.mUiMode
+ && mWidth == other.mWidth
+ && mHeight == other.mHeight
+ && Objects.equals(mCutout, other.mCutout)
+ && mRotation == other.mRotation
+ && mDensityDpi == other.mDensityDpi
+ && Objects.equals(mNonDecorInsets, other.mNonDecorInsets)
+ && Objects.equals(mStableInsets, other.mStableInsets)
+ && mHasNavigationBar == other.mHasNavigationBar
+ && mHasStatusBar == other.mHasStatusBar
+ && mNavBarFrameHeight == other.mNavBarFrameHeight;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi,
+ mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar,
+ mNavBarFrameHeight);
+ }
+
/**
* Create empty layout.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 66560d339c5a..a52db24aa184 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -90,15 +90,15 @@ public class PipAnimationController {
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
- private PipTransitionAnimator mCurrentAnimator;
-
- private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
+ private final ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
ThreadLocal.withInitial(() -> {
AnimationHandler handler = new AnimationHandler();
handler.setProvider(new SfVsyncFrameCallbackProvider());
return handler;
});
+ private PipTransitionAnimator mCurrentAnimator;
+
public PipAnimationController(PipSurfaceTransactionHelper helper) {
mSurfaceTransactionHelper = helper;
}
@@ -268,6 +268,7 @@ public class PipAnimationController {
if (mPipAnimationCallback != null) {
mPipAnimationCallback.onPipAnimationEnd(mTaskInfo, tx, this);
}
+ mTransitionDirection = TRANSITION_DIRECTION_NONE;
}
@Override
@@ -275,6 +276,7 @@ public class PipAnimationController {
if (mPipAnimationCallback != null) {
mPipAnimationCallback.onPipAnimationCancel(mTaskInfo, this);
}
+ mTransitionDirection = TRANSITION_DIRECTION_NONE;
}
@Override public void onAnimationRepeat(Animator animation) {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 7fff37d082b1..9a584c67f97c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -65,6 +65,7 @@ import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipUtils;
import java.io.PrintWriter;
+import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -101,9 +102,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb
*/
private final DisplayChangeController.OnDisplayChangingListener mRotationController = (
int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> {
- if (!mPipTaskOrganizer.isInPip() || mPipTaskOrganizer.isDeferringEnterPipAnimation()) {
- // Skip if we aren't in PIP or haven't actually entered PIP yet. We still need to update
- // the display layout in the bounds handler in this case.
+ if (!mPipTaskOrganizer.isInPip()
+ || mPipBoundsState.getDisplayLayout().rotation() == toRotation
+ || mPipTaskOrganizer.isDeferringEnterPipAnimation()) {
+ // Skip if the same rotation has been set or we aren't in PIP or haven't actually
+ // entered PIP yet. We still need to update the display layout in the bounds handler
+ // in this case.
onDisplayRotationChangedNotInPip(mContext, toRotation);
// do not forget to update the movement bounds as well.
updateMovementBounds(mPipBoundsState.getNormalBounds(), true /* fromRotation */,
@@ -378,6 +382,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) {
+ if (Objects.equals(layout, mPipBoundsState.getDisplayLayout())) {
+ return;
+ }
Runnable updateDisplayLayout = () -> {
mPipBoundsState.setDisplayLayout(layout);
updateMovementBounds(null /* toBounds */,
@@ -476,8 +483,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb
int launcherRotation, int shelfHeight) {
setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight);
onDisplayRotationChangedNotInPip(mContext, launcherRotation);
- return mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
+ final Rect entryBounds = mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
pictureInPictureParams);
+ // sync mPipBoundsState with the newly calculated bounds.
+ mPipBoundsState.setNormalBounds(entryBounds);
+ return entryBounds;
}
private void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 5e3966032a11..38b48e97771a 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -117,7 +117,8 @@ interface ILocationManager
boolean isLocationEnabledForUser(int userId);
void setLocationEnabledForUser(boolean enabled, int userId);
- void addTestProvider(String name, in ProviderProperties properties, String packageName, @nullable String attributionTag);
+ void addTestProvider(String name, in ProviderProperties properties,
+ in List<String> locationTags, String packageName, @nullable String attributionTag);
void removeTestProvider(String provider, String packageName, @nullable String attributionTag);
void setTestProviderLocation(String provider, in Location location, String packageName, @nullable String attributionTag);
void setTestProviderEnabled(String provider, boolean enabled, String packageName, @nullable String attributionTag);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index dd5b6e6b4222..95bae5ae7aab 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -76,6 +76,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -1957,12 +1958,32 @@ public class LocationManager {
* allowed} for your app.
*/
public void addTestProvider(@NonNull String provider, @NonNull ProviderProperties properties) {
+ addTestProvider(provider, properties, Collections.emptySet());
+ }
+
+ /**
+ * Creates a test location provider and adds it to the set of active providers. This provider
+ * will replace any provider with the same name that exists prior to this call.
+ *
+ * @param provider the provider name
+ * @param properties the provider properties
+ * @param locationTags the attribution tags for accessing location from the provider
+ *
+ * @throws IllegalArgumentException if provider is null
+ * @throws IllegalArgumentException if properties is null
+ * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
+ * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
+ * allowed} for your app.
+ */
+ public void addTestProvider(@NonNull String provider, @NonNull ProviderProperties properties,
+ @NonNull Set<String> locationTags) {
Preconditions.checkArgument(provider != null, "invalid null provider");
Preconditions.checkArgument(properties != null, "invalid null properties");
+ Preconditions.checkArgument(locationTags != null, "invalid null location tags");
try {
- mService.addTestProvider(provider, properties, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mService.addTestProvider(provider, properties, new ArrayList<>(locationTags),
+ mContext.getOpPackageName(), mContext.getFeatureId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
index a6a0e7aa24ff..763835c9cbe2 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -21,6 +21,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.location.util.identity.CallerIdentity;
+import com.android.internal.annotations.Immutable;
+
+import java.util.Set;
+
/**
* Location manager local system service interface.
*
@@ -39,6 +43,21 @@ public abstract class LocationManagerInternal {
}
/**
+ * Interface for getting callbacks when a location provider's location tags change.
+ *
+ * @see LocationTagInfo
+ */
+ public interface OnProviderLocationTagsChangeListener {
+
+ /**
+ * Called when the location tags for a provider change.
+ *
+ * @param providerLocationTagInfo The tag info for a provider.
+ */
+ void onLocationTagsChanged(@NonNull LocationTagInfo providerLocationTagInfo);
+ }
+
+ /**
* Returns true if the given provider is enabled for the given user.
*
* @param provider A location provider as listed by {@link LocationManager#getAllProviders()}
@@ -88,4 +107,60 @@ public abstract class LocationManagerInternal {
* provider, and the elapsed nanos since boot the current time was computed at.
*/
public abstract @Nullable LocationTime getGnssTimeMillis();
+
+ /**
+ * Sets a listener for changes in the location providers' tags. Passing
+ * {@code null} clears the current listener.
+ *
+ * @param listener The listener.
+ */
+ public abstract void setOnProviderLocationTagsChangeListener(
+ @Nullable OnProviderLocationTagsChangeListener listener);
+
+ /**
+ * This class represents the location permission tags used by the location provider
+ * packages in a given UID. These tags are strictly used for accessing state guarded
+ * by the location permission(s) by a location provider which are required for the
+ * provider to fulfill its function as being a location provider.
+ */
+ @Immutable
+ public static class LocationTagInfo {
+ private final int mUid;
+
+ @NonNull
+ private final String mPackageName;
+
+ @Nullable
+ private final Set<String> mLocationTags;
+
+ public LocationTagInfo(int uid, @NonNull String packageName,
+ @Nullable Set<String> locationTags) {
+ mUid = uid;
+ mPackageName = packageName;
+ mLocationTags = locationTags;
+ }
+
+ /**
+ * @return The UID for which tags are related.
+ */
+ public int getUid() {
+ return mUid;
+ }
+
+ /**
+ * @return The package for which tags are related.
+ */
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * @return The tags for the package used for location related accesses.
+ */
+ @Nullable
+ public Set<String> getTags() {
+ return mLocationTags;
+ }
+ }
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 3669bf44c42a..4ae68762c08e 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -3571,26 +3571,28 @@ static DemuxFilterSettings getFilterConfiguration(
.tpid = tpid,
};
- DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype);
- switch (tsType) {
- case DemuxTsFilterType::SECTION:
- tsFilterSettings.filterSettings.section(
- getFilterSectionSettings(env, settingsObj));
- break;
- case DemuxTsFilterType::AUDIO:
- case DemuxTsFilterType::VIDEO:
- tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
- break;
- case DemuxTsFilterType::PES:
- tsFilterSettings.filterSettings.pesData(
- getFilterPesDataSettings(env, settingsObj));
- break;
- case DemuxTsFilterType::RECORD:
- tsFilterSettings.filterSettings.record(
- getFilterRecordSettings(env, settingsObj));
- break;
- default:
- break;
+ if (settingsObj != NULL) {
+ DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype);
+ switch (tsType) {
+ case DemuxTsFilterType::SECTION:
+ tsFilterSettings.filterSettings.section(
+ getFilterSectionSettings(env, settingsObj));
+ break;
+ case DemuxTsFilterType::AUDIO:
+ case DemuxTsFilterType::VIDEO:
+ tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
+ break;
+ case DemuxTsFilterType::PES:
+ tsFilterSettings.filterSettings.pesData(
+ getFilterPesDataSettings(env, settingsObj));
+ break;
+ case DemuxTsFilterType::RECORD:
+ tsFilterSettings.filterSettings.record(
+ getFilterRecordSettings(env, settingsObj));
+ break;
+ default:
+ break;
+ }
}
filterSettings.ts(tsFilterSettings);
break;
@@ -3602,60 +3604,55 @@ static DemuxFilterSettings getFilterConfiguration(
DemuxMmtpFilterSettings mmtpFilterSettings {
.mmtpPid = mmtpPid,
};
- DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype);
- switch (mmtpType) {
- case DemuxMmtpFilterType::SECTION:
- mmtpFilterSettings.filterSettings.section(
- getFilterSectionSettings(env, settingsObj));
- break;
- case DemuxMmtpFilterType::AUDIO:
- case DemuxMmtpFilterType::VIDEO:
- mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
- break;
- case DemuxMmtpFilterType::PES:
- mmtpFilterSettings.filterSettings.pesData(
- getFilterPesDataSettings(env, settingsObj));
- break;
- case DemuxMmtpFilterType::RECORD:
- mmtpFilterSettings.filterSettings.record(
- getFilterRecordSettings(env, settingsObj));
- break;
- case DemuxMmtpFilterType::DOWNLOAD:
- mmtpFilterSettings.filterSettings.download(
- getFilterDownloadSettings(env, settingsObj));
- break;
- default:
- break;
+
+ if (settingsObj != NULL) {
+ DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype);
+ switch (mmtpType) {
+ case DemuxMmtpFilterType::SECTION:
+ mmtpFilterSettings.filterSettings.section(
+ getFilterSectionSettings(env, settingsObj));
+ break;
+ case DemuxMmtpFilterType::AUDIO:
+ case DemuxMmtpFilterType::VIDEO:
+ mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
+ break;
+ case DemuxMmtpFilterType::PES:
+ mmtpFilterSettings.filterSettings.pesData(
+ getFilterPesDataSettings(env, settingsObj));
+ break;
+ case DemuxMmtpFilterType::RECORD:
+ mmtpFilterSettings.filterSettings.record(
+ getFilterRecordSettings(env, settingsObj));
+ break;
+ case DemuxMmtpFilterType::DOWNLOAD:
+ mmtpFilterSettings.filterSettings.download(
+ getFilterDownloadSettings(env, settingsObj));
+ break;
+ default:
+ break;
+ }
}
filterSettings.mmtp(mmtpFilterSettings);
break;
}
case DemuxFilterMainType::IP: {
DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj);
-
DemuxIpFilterSettings ipFilterSettings {
.ipAddr = ipAddr,
};
+
DemuxIpFilterType ipType = static_cast<DemuxIpFilterType>(subtype);
- switch (ipType) {
- case DemuxIpFilterType::SECTION: {
- ipFilterSettings.filterSettings.section(
- getFilterSectionSettings(env, settingsObj));
- break;
- }
- case DemuxIpFilterType::IP: {
- jclass clazz = env->FindClass(
- "android/media/tv/tuner/filter/IpFilterConfiguration");
- bool bPassthrough = static_cast<bool>(
- env->GetBooleanField(
- filterConfigObj, env->GetFieldID(
- clazz, "mPassthrough", "Z")));
- ipFilterSettings.filterSettings.bPassthrough(bPassthrough);
- break;
- }
- default: {
- break;
- }
+ if (ipType == DemuxIpFilterType::SECTION && settingsObj != NULL) {
+ ipFilterSettings.filterSettings.section(
+ getFilterSectionSettings(env, settingsObj));
+ } else if (ipType == DemuxIpFilterType::IP) {
+ jclass clazz = env->FindClass(
+ "android/media/tv/tuner/filter/IpFilterConfiguration");
+ bool bPassthrough = static_cast<bool>(
+ env->GetBooleanField(
+ filterConfigObj, env->GetFieldID(
+ clazz, "mPassthrough", "Z")));
+ ipFilterSettings.filterSettings.bPassthrough(bPassthrough);
}
filterSettings.ip(ipFilterSettings);
break;
@@ -3672,24 +3669,17 @@ static DemuxFilterSettings getFilterConfiguration(
.packetType = packetType,
.isCompressedIpPacket = isCompressedIpPacket,
};
+
DemuxTlvFilterType tlvType = static_cast<DemuxTlvFilterType>(subtype);
- switch (tlvType) {
- case DemuxTlvFilterType::SECTION: {
- tlvFilterSettings.filterSettings.section(
- getFilterSectionSettings(env, settingsObj));
- break;
- }
- case DemuxTlvFilterType::TLV: {
- bool bPassthrough = static_cast<bool>(
- env->GetBooleanField(
- filterConfigObj, env->GetFieldID(
- clazz, "mPassthrough", "Z")));
- tlvFilterSettings.filterSettings.bPassthrough(bPassthrough);
- break;
- }
- default: {
- break;
- }
+ if (tlvType == DemuxTlvFilterType::SECTION && settingsObj != NULL) {
+ tlvFilterSettings.filterSettings.section(
+ getFilterSectionSettings(env, settingsObj));
+ } else if (tlvType == DemuxTlvFilterType::TLV) {
+ bool bPassthrough = static_cast<bool>(
+ env->GetBooleanField(
+ filterConfigObj, env->GetFieldID(
+ clazz, "mPassthrough", "Z")));
+ tlvFilterSettings.filterSettings.bPassthrough(bPassthrough);
}
filterSettings.tlv(tlvFilterSettings);
break;
@@ -3704,14 +3694,17 @@ static DemuxFilterSettings getFilterConfiguration(
.packetType = packetType,
.lengthType = lengthType,
};
- DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype);
- switch (alpType) {
- case DemuxAlpFilterType::SECTION:
- alpFilterSettings.filterSettings.section(
- getFilterSectionSettings(env, settingsObj));
- break;
- default:
- break;
+
+ if (settingsObj != NULL) {
+ DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype);
+ switch (alpType) {
+ case DemuxAlpFilterType::SECTION:
+ alpFilterSettings.filterSettings.section(
+ getFilterSectionSettings(env, settingsObj));
+ break;
+ default:
+ break;
+ }
}
filterSettings.alp(alpFilterSettings);
break;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7ced202c5e44..f67e039c5ec3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -400,6 +400,7 @@
<!-- Permission required for CTS to test sensor privacy behavior -->
<uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
+ <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" />
<!-- Permission needed for CTS test - CallLogTest -->
<uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index fbe58c505662..044216e5423d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -87,6 +87,7 @@
<uses-permission android:name="android.permission.MASTER_CLEAR" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
+ <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" />
<!-- ActivityManager -->
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 571cbbcc77db..1ce87c1c0236 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -20,7 +20,6 @@
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
- android:minHeight="48dp"
android:paddingTop="12dp">
<LinearLayout
android:id="@+id/label_group"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f13fdd34e4f3..0893c1488005 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -91,6 +91,9 @@
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">3</integer>
+ <!-- The number of columns in the Quick Settings customizer -->
+ <integer name="quick_settings_edit_num_columns">@integer/quick_settings_num_columns</integer>
+
<!-- The number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">3</integer>
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 8cb1bc4878a5..5db3349b646a 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -69,7 +69,9 @@ public class NumPadButton extends AlphaOptimizedImageButton {
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (mAnimator != null) mAnimator.start();
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ if (mAnimator != null) mAnimator.start();
+ }
return super.onTouchEvent(event);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index a4a781dc6ff5..e6a9d4fdd547 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -160,10 +160,9 @@ public class NumPadKey extends ViewGroup {
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
doHapticKeyClick();
+ if (mAnimator != null) mAnimator.start();
}
- if (mAnimator != null) mAnimator.start();
-
return super.onTouchEvent(event);
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 9686c91f2c31..79f0688db869 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,8 +16,8 @@
package com.android.systemui.appops;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
import android.Manifest;
@@ -137,8 +137,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
mAudioManager = audioManager;
mSensorPrivacyController = sensorPrivacyController;
mMicMuted = audioManager.isMicrophoneMute()
- || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
- mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA);
+ || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
+ mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
mLocationManager = context.getSystemService(LocationManager.class);
mPackageManager = context.getPackageManager();
dumpManager.registerDumpable(TAG, this);
@@ -159,8 +159,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
mSensorPrivacyController.addCallback(this);
mMicMuted = mAudioManager.isMicrophoneMute()
- || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
- mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA);
+ || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
+ mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
mAudioManager.getActiveRecordingConfigurations()));
@@ -583,16 +583,16 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
@Override
public void onReceive(Context context, Intent intent) {
mMicMuted = mAudioManager.isMicrophoneMute()
- || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
+ || mSensorPrivacyController.isSensorBlocked(MICROPHONE);
updateSensorDisabledStatus();
}
@Override
public void onSensorBlockedChanged(int sensor, boolean blocked) {
mBGHandler.post(() -> {
- if (sensor == INDIVIDUAL_SENSOR_CAMERA) {
+ if (sensor == CAMERA) {
mCameraDisabled = blocked;
- } else if (sensor == INDIVIDUAL_SENSOR_MICROPHONE) {
+ } else if (sensor == MICROPHONE) {
mMicMuted = mAudioManager.isMicrophoneMute() || blocked;
}
updateSensorDisabledStatus();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index d4678f39e404..127128dda112 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -103,6 +103,11 @@ public class KeyguardIndicationRotateTextViewController extends
*/
public void updateIndication(@IndicationType int type, KeyguardIndication newIndication,
boolean showImmediately) {
+ if (type == INDICATION_TYPE_NOW_PLAYING
+ || type == INDICATION_TYPE_REVERSE_CHARGING) {
+ // temporarily don't show here, instead use AmbientContainer b/181049781
+ return;
+ }
final boolean hasPreviousIndication = mIndicationMessages.get(type) != null;
final boolean hasNewIndication = newIndication != null;
if (!hasNewIndication) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index c8edaec98ee4..fe76668ab68b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -29,7 +29,6 @@ import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.qs.dagger.QSScope;
-import com.android.systemui.qs.tileimpl.QSTileBaseView;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -56,7 +55,8 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
private final ArrayList<View> mAllViews = new ArrayList<>();
/**
* List of {@link View}s representing Quick Settings that are being animated from the quick QS
- * position to the normal QS panel.
+ * position to the normal QS panel. These views will only show once the animation is complete,
+ * to prevent overlapping of semi transparent views
*/
private final ArrayList<View> mQuickQsViews = new ArrayList<>();
private final QuickQSPanel mQuickQsPanel;
@@ -233,7 +233,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
// Quick tiles.
QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
if (quickTileView == null) continue;
- View qqsBgCircle = ((QSTileBaseView) quickTileView).getBgCircle();
getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
getRelativePosition(loc2, tileIcon, view);
@@ -255,11 +254,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
- if (mFeatureFlags.isQSLabelsEnabled()) {
- firstPageBuilder.addFloat(qqsBgCircle, "alpha", 1, 1, 0);
- mAllViews.add(qqsBgCircle);
- }
-
} else { // These tiles disappear when expanding
firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
@@ -271,7 +265,11 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
translationX);
}
- mQuickQsViews.add(tileView.getIconWithBackground());
+ if (mFeatureFlags.isQSLabelsEnabled()) {
+ mQuickQsViews.add(tileView);
+ } else {
+ mQuickQsViews.add(tileView.getIconWithBackground());
+ }
mAllViews.add(tileView.getIcon());
mAllViews.add(quickTileView);
} else if (mFullRows && isIconInAnimatedRow(count)) {
@@ -362,7 +360,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
if(view == parent || view == null) return;
// Ignore tile pages as they can have some offset we don't want to take into account in
// RTL.
- if (!(view instanceof PagedTileLayout.TilePage || view instanceof SideLabelTileLayout)) {
+ if (!isAPage(view)) {
loc1[0] += view.getLeft();
loc1[1] += view.getTop();
}
@@ -374,6 +372,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
getRelativePositionInt(loc1, (View) view.getParent(), parent);
}
+ // Returns true if the view is a possible page in PagedTileLayout
+ private boolean isAPage(View view) {
+ if (view instanceof PagedTileLayout.TilePage) {
+ return true;
+ } else if (view instanceof SideLabelTileLayout) {
+ return !(view instanceof QuickQSPanel.QQSSideLabelTileLayout);
+ }
+ return false;
+ }
+
public void setPosition(float position) {
if (mNeedsAnimatorUpdate) {
updateAnimators();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 91ae571d1cfb..05633071be86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -112,7 +112,7 @@ public class QSPanel extends LinearLayout implements Tunable {
private int mMediaTotalBottomMargin;
private int mFooterMarginStartHorizontal;
private Consumer<Boolean> mMediaVisibilityChangedListener;
- private boolean mSideLabels;
+ protected boolean mSideLabels;
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -201,16 +201,20 @@ public class QSPanel extends LinearLayout implements Tunable {
mFooterPageIndicator.setNumPages(((PagedTileLayout) mTileLayout).getNumPages());
}
- // Allow the UI to be as big as it want's to, we're in a scroll view
- int newHeight = 10000;
- int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
- int excessHeight = newHeight - availableHeight;
- // Measure with EXACTLY. That way, The content will only use excess height and will
- // be measured last, after other views and padding is accounted for. This only
- // works because our Layouts in here remeasure themselves with the exact content
- // height.
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
- ((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight);
+ // In landscape, mTileLayout's parent is not the panel but a view that contains the
+ // tile layout and the media controls.
+ if (((View) mTileLayout).getParent() == this) {
+ // Allow the UI to be as big as it want's to, we're in a scroll view
+ int newHeight = 10000;
+ int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
+ int excessHeight = newHeight - availableHeight;
+ // Measure with EXACTLY. That way, The content will only use excess height and will
+ // be measured last, after other views and padding is accounted for. This only
+ // works because our Layouts in here remeasure themselves with the exact content
+ // height.
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
+ ((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight);
+ }
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -758,7 +762,7 @@ public class QSPanel extends LinearLayout implements Tunable {
// Let's use 3 columns to match the current layout
int columns;
if (mSideLabels) {
- columns = horizontal ? 1 : 2;
+ columns = horizontal ? 2 : 4;
} else {
columns = horizontal ? 3 : TileLayout.NO_MAX_COLUMNS;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index fcb35e2040ea..d60801ef2d03 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -18,7 +18,6 @@ package com.android.systemui.qs;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
-import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
import android.annotation.NonNull;
@@ -66,7 +65,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
private BrightnessMirrorController mBrightnessMirrorController;
private boolean mGridContentVisible = true;
- private boolean mQsLabelsFlag;
private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
new QSPanel.OnConfigurationChangedListener() {
@@ -93,7 +91,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSlider.Factory brightnessSliderFactory,
- @Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
FeatureFlags featureFlags) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags);
@@ -108,9 +105,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
mView.setBrightnessView(mBrightnessSlider.getRootView());
mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider);
-
- mQsLabelsFlag = qsLabelsFlag;
- mSideLabels = qsLabelsFlag;
}
@Override
@@ -329,7 +323,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
@Override
public void onTuningChanged(String key, String newValue) {
if (QS_REMOVE_LABELS.equals(key)) {
- if (!mQsLabelsFlag) return;
+ if (!mQSLabelFlag) return;
boolean newShowLabels = newValue == null || "0".equals(newValue);
if (mShowLabels == newShowLabels) return;
mShowLabels = newShowLabels;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 9426e7122c1c..f1174fbe7aef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -75,7 +75,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
private final QSHost.Callback mQSHostCallback = this::setTiles;
protected boolean mShowLabels = true;
- protected boolean mSideLabels;
+ protected boolean mQSLabelFlag;
private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
new QSPanel.OnConfigurationChangedListener() {
@@ -118,11 +118,12 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
mQSLogger = qsLogger;
mDumpManager = dumpManager;
mFeatureFlags = featureFlags;
+ mQSLabelFlag = featureFlags.isQSLabelsEnabled();
}
@Override
protected void onInit() {
- mView.initialize(mSideLabels);
+ mView.initialize(mQSLabelFlag);
mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), "");
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index a29ac3bb77e9..9b66b59c06df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -69,12 +69,22 @@ public class QuickQSPanel extends QSPanel {
@Override
public TileLayout createRegularTileLayout() {
- return new QuickQSPanel.HeaderTileLayout(mContext);
+ if (mSideLabels) {
+ return new QQSSideLabelTileLayout(mContext);
+ } else {
+ return new QuickQSPanel.HeaderTileLayout(mContext);
+ }
}
@Override
protected QSTileLayout createHorizontalTileLayout() {
- return new DoubleLineTileLayout(mContext);
+ if (mSideLabels) {
+ TileLayout t = createRegularTileLayout();
+ t.setMaxColumns(2);
+ return t;
+ } else {
+ return new DoubleLineTileLayout(mContext);
+ }
}
@Override
@@ -331,4 +341,38 @@ public class QuickQSPanel extends QSPanel {
}
}
}
+
+ static class QQSSideLabelTileLayout extends SideLabelTileLayout {
+ QQSSideLabelTileLayout(Context context) {
+ super(context, null);
+ setClipChildren(false);
+ setClipToPadding(false);
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.gravity = Gravity.CENTER_HORIZONTAL;
+ setLayoutParams(lp);
+ setMaxColumns(4);
+ }
+
+ @Override
+ public boolean updateResources() {
+ boolean b = super.updateResources();
+ mMaxAllowedRows = 2;
+ return b;
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateResources();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Make sure to always use the correct number of rows. As it's determined by the
+ // columns, just use as many as needed.
+ updateMaxRows(10000, mRecords.size());
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 383e932a6955..671f8f7dd2d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,7 +17,6 @@
package com.android.systemui.qs;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
-import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
import com.android.internal.logging.MetricsLogger;
@@ -42,8 +41,6 @@ import javax.inject.Named;
@QSScope
public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> {
- private boolean mUseSideLabels;
-
private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
newConfig -> {
int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
@@ -58,12 +55,10 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
@Named(QUICK_QS_PANEL) MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag,
- FeatureFlags featureFlags
+ DumpManager dumpManager, FeatureFlags featureFlags
) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
uiEventLogger, qsLogger, dumpManager, featureFlags);
- mUseSideLabels = qsLabelsFlag;
}
@Override
@@ -104,19 +99,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
break;
}
}
- if (mUseSideLabels) {
- List<QSTile> newTiles = new ArrayList<>();
- for (int i = 0; i < tiles.size(); i += 2) {
- newTiles.add(tiles.get(i));
- }
- for (int i = 1; i < tiles.size(); i += 2) {
- newTiles.add(tiles.get(i));
- }
- super.setTiles(newTiles, true);
-
- } else {
- super.setTiles(tiles, true);
- }
+ super.setTiles(tiles, !mQSLabelFlag);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 74a7ac1cb6dd..4de4a78e9e8a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -20,11 +20,13 @@ import android.content.Context
import android.util.AttributeSet
import com.android.systemui.R
-open class SideLabelTileLayout(context: Context, attrs: AttributeSet) : TileLayout(context, attrs) {
+open class SideLabelTileLayout(
+ context: Context,
+ attrs: AttributeSet?
+) : TileLayout(context, attrs) {
override fun updateResources(): Boolean {
return super.updateResources().also {
- mResourceColumns = 2
mMaxAllowedRows = 4
mCellMarginHorizontal = (mCellMarginHorizontal * 1.2).toInt()
mCellMarginVertical = mCellMarginHorizontal
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index d559e07f3ff6..14cbf980df23 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -124,6 +124,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
public boolean updateResources() {
final Resources res = mContext.getResources();
mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
+ updateColumns();
mMaxCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 048fdc3a0e5a..7a91421b00a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -75,6 +75,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
private static final int ACTION_ADD = 1;
private static final int ACTION_MOVE = 2;
+ private static final int NUM_COLUMNS_ID = R.integer.quick_settings_edit_num_columns;
+
private final Context mContext;
private final Handler mHandler = new Handler();
@@ -87,6 +89,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
private int mEditIndex;
private int mTileDividerIndex;
private int mFocusIndex;
+
private boolean mNeedsFocus;
private List<String> mCurrentSpecs;
private List<TileInfo> mOtherTiles;
@@ -109,7 +112,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
mDecoration = new TileItemDecoration(context);
mMarginDecoration = new MarginTileDecoration();
mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
- mNumColumns = context.getResources().getInteger(R.integer.quick_settings_num_columns);
+ mNumColumns = context.getResources().getInteger(NUM_COLUMNS_ID);
mAccessibilityDelegate = new TileAdapterDelegate();
}
@@ -129,7 +132,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
* @return {@code true} if the number of columns changed, {@code false} otherwise
*/
public boolean updateNumColumns() {
- int numColumns = mContext.getResources().getInteger(R.integer.quick_settings_num_columns);
+ int numColumns = mContext.getResources().getInteger(NUM_COLUMNS_ID);
if (numColumns != mNumColumns) {
mNumColumns = numColumns;
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index a699e2ec7cfc..424aafac1f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -105,22 +105,25 @@ public class QSTileView extends QSTileBaseView {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mLabel.setSingleLine(false);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- // Remeasure view if the primary label requires more then 2 lines or the secondary label
- // text will be cut off.
- if (mLabel.getLineCount() > mMaxLabelLines || !TextUtils.isEmpty(mSecondLine.getText())
- && mSecondLine.getLineHeight() > mSecondLine.getHeight()) {
- if (!mLabel.isSingleLine()) {
- mLabel.setSingleLine();
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- } else {
- if (mLabel.isSingleLine()) {
- mLabel.setSingleLine(false);
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
+ // Remeasure view if the primary label requires more than mMaxLabelLines lines or the
+ // secondary label text will be cut off.
+ if (shouldLabelBeSingleLine()) {
+ mLabel.setSingleLine();
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ private boolean shouldLabelBeSingleLine() {
+ if (mLabel.getLineCount() > mMaxLabelLines) {
+ return true;
+ } else if (!TextUtils.isEmpty(mSecondLine.getText())
+ && mLabel.getLineCount() > mMaxLabelLines - 1) {
+ return true;
}
+ return false;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index dc81b702021f..c98de8cb8fab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -24,8 +24,8 @@ import android.graphics.drawable.PaintDrawable
import android.graphics.drawable.RippleDrawable
import android.service.quicksettings.Tile.STATE_ACTIVE
import android.view.Gravity
-import android.view.View
import android.widget.LinearLayout
+import android.widget.RelativeLayout
import com.android.systemui.R
import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
@@ -37,7 +37,6 @@ class QSTileViewHorizontal(
) : QSTileView(context, icon, false) {
private var paintDrawable: PaintDrawable? = null
- private var divider: View? = null
init {
orientation = HORIZONTAL
@@ -49,7 +48,12 @@ class QSTileViewHorizontal(
override fun createLabel() {
super.createLabel()
- findViewById<LinearLayout>(R.id.label_group)?.gravity = Gravity.START
+ findViewById<LinearLayout>(R.id.label_group)?.apply {
+ gravity = Gravity.START
+ (layoutParams as? RelativeLayout.LayoutParams)?.apply {
+ removeRule(RelativeLayout.ALIGN_PARENT_TOP)
+ }
+ }
mLabel.gravity = Gravity.START
mLabel.textDirection = TEXT_DIRECTION_LOCALE
mSecondLine.gravity = Gravity.START
@@ -57,7 +61,7 @@ class QSTileViewHorizontal(
val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
mLabelContainer.setPaddingRelative(0, padding, padding, padding)
(mLabelContainer.layoutParams as LayoutParams).gravity =
- Gravity.CENTER_VERTICAL or Gravity.START
+ Gravity.CENTER_VERTICAL or Gravity.START
}
override fun updateRippleSize() {
@@ -93,7 +97,6 @@ class QSTileViewHorizontal(
paintDrawable?.setTint(getCircleColor(state.state))
mSecondLine.setTextColor(mLabel.textColors)
mLabelContainer.background = null
- divider?.backgroundTintList = mLabel.textColors
}
override fun handleExpand(dualTarget: Boolean) {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index 3841daca7ebe..70287cd37d01 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -16,10 +16,13 @@
package com.android.systemui.qs.tiles;
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
+import static android.content.pm.PackageManager.FEATURE_CAMERA_TOGGLE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfig;
@@ -58,8 +61,8 @@ public class CameraToggleTile extends SensorPrivacyToggleTile {
@Override
public boolean isAvailable() {
- return /*getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE)
- && */whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ return getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE)
+ && whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
"camera_toggle_enabled",
false));
}
@@ -75,7 +78,7 @@ public class CameraToggleTile extends SensorPrivacyToggleTile {
}
@Override
- public int getSensorId() {
+ public @Sensor int getSensorId() {
return CAMERA;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index 2f0071a1f198..e9b712df2154 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -16,10 +16,13 @@
package com.android.systemui.qs.tiles;
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
+import static android.content.pm.PackageManager.FEATURE_MICROPHONE_TOGGLE;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfig;
@@ -58,7 +61,9 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile {
@Override
public boolean isAvailable() {
- return whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ return getHost().getContext().getPackageManager()
+ .hasSystemFeature(FEATURE_MICROPHONE_TOGGLE)
+ && whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
"mic_toggle_enabled",
false));
}
@@ -74,7 +79,7 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile {
}
@Override
- public int getSensorId() {
+ public @Sensor int getSensorId() {
return MICROPHONE;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 00703e7f8403..0c582bdbe12f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -17,7 +17,7 @@
package com.android.systemui.qs.tiles;
import android.content.Intent;
-import android.hardware.SensorPrivacyManager.IndividualSensor;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
import android.os.Handler;
import android.os.Looper;
import android.service.quicksettings.Tile;
@@ -49,7 +49,7 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS
/**
* @return Id of the sensor that will be toggled
*/
- public abstract @IndividualSensor int getSensorId();
+ public abstract @Sensor int getSensorId();
/**
* @return icon for the QS tile
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 9f182e19efaf..658613796498 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -25,8 +25,8 @@ import android.content.pm.PackageManager
import android.content.res.Resources
import android.hardware.SensorPrivacyManager
import android.hardware.SensorPrivacyManager.EXTRA_SENSOR
-import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA
-import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE
+import android.hardware.SensorPrivacyManager.Sensors.CAMERA
+import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE
import android.os.Bundle
import android.os.Handler
import android.text.Html
@@ -81,7 +81,7 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
dismiss()
}
}
- if (!sensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor)) {
+ if (!sensorPrivacyManager.isSensorPrivacyEnabled(sensor)) {
finish()
return
}
@@ -89,9 +89,9 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
mAlertParams.apply {
try {
mMessage = Html.fromHtml(getString(when (sensor) {
- INDIVIDUAL_SENSOR_MICROPHONE ->
+ MICROPHONE ->
R.string.sensor_privacy_start_use_mic_dialog_content
- INDIVIDUAL_SENSOR_CAMERA ->
+ CAMERA ->
R.string.sensor_privacy_start_use_camera_dialog_content
else -> Resources.ID_NULL
}, packageManager.getApplicationInfo(sensorUsePackageName, 0)
@@ -102,9 +102,9 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
}
mIconId = when (sensor) {
- INDIVIDUAL_SENSOR_MICROPHONE ->
+ MICROPHONE ->
com.android.internal.R.drawable.perm_group_microphone
- INDIVIDUAL_SENSOR_CAMERA -> com.android.internal.R.drawable.perm_group_camera
+ CAMERA -> com.android.internal.R.drawable.perm_group_camera
else -> Resources.ID_NULL
}
@@ -121,7 +121,7 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
override fun onStart() {
super.onStart()
- sensorPrivacyManager.suppressIndividualSensorPrivacyReminders(sensorUsePackageName, true)
+ sensorPrivacyManager.suppressSensorPrivacyReminders(sensorUsePackageName, true)
unsuppressImmediately = false
}
@@ -156,11 +156,11 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
if (unsuppressImmediately) {
sensorPrivacyManager
- .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
+ .suppressSensorPrivacyReminders(sensorUsePackageName, false)
} else {
Handler(mainLooper).postDelayed({
sensorPrivacyManager
- .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false)
+ .suppressSensorPrivacyReminders(sensorUsePackageName, false)
}, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS)
}
}
@@ -170,7 +170,7 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene
}
private fun disableSensorPrivacy() {
- sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false)
+ sensorPrivacyManager.setSensorPrivacyForProfileGroup(sensor, false)
unsuppressImmediately = true
setResult(RESULT_OK)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
index a76d08a438f2..7f935d28285f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
@@ -16,17 +16,17 @@
package com.android.systemui.statusbar.policy;
-import android.hardware.SensorPrivacyManager.IndividualSensor;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
public interface IndividualSensorPrivacyController extends
CallbackController<IndividualSensorPrivacyController.Callback> {
void init();
- boolean isSensorBlocked(@IndividualSensor int sensor);
+ boolean isSensorBlocked(@Sensor int sensor);
- void setSensorBlocked(@IndividualSensor int sensor, boolean blocked);
+ void setSensorBlocked(@Sensor int sensor, boolean blocked);
interface Callback {
- void onSensorBlockedChanged(@IndividualSensor int sensor, boolean blocked);
+ void onSensorBlockedChanged(@Sensor int sensor, boolean blocked);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index 32d15ed41648..295df05797ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.policy;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import android.hardware.SensorPrivacyManager;
-import android.hardware.SensorPrivacyManager.IndividualSensor;
+import android.hardware.SensorPrivacyManager.Sensors.Sensor;
import android.util.ArraySet;
import android.util.SparseBooleanArray;
@@ -30,8 +30,7 @@ import java.util.Set;
public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController {
- private static final int[] SENSORS = new int[] {INDIVIDUAL_SENSOR_CAMERA,
- INDIVIDUAL_SENSOR_MICROPHONE};
+ private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE};
private final @NonNull SensorPrivacyManager mSensorPrivacyManager;
private final SparseBooleanArray mState = new SparseBooleanArray();
@@ -48,18 +47,18 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr
mSensorPrivacyManager.addSensorPrivacyListener(sensor,
(enabled) -> onSensorPrivacyChanged(sensor, enabled));
- mState.put(sensor, mSensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor));
+ mState.put(sensor, mSensorPrivacyManager.isSensorPrivacyEnabled(sensor));
}
}
@Override
- public boolean isSensorBlocked(@IndividualSensor int sensor) {
+ public boolean isSensorBlocked(@Sensor int sensor) {
return mState.get(sensor, false);
}
@Override
- public void setSensorBlocked(@IndividualSensor int sensor, boolean blocked) {
- mSensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, blocked);
+ public void setSensorBlocked(@Sensor int sensor, boolean blocked) {
+ mSensorPrivacyManager.setSensorPrivacyForProfileGroup(sensor, blocked);
}
@Override
@@ -72,7 +71,7 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr
mCallbacks.remove(listener);
}
- private void onSensorPrivacyChanged(@IndividualSensor int sensor, boolean blocked) {
+ private void onSensorPrivacyChanged(@Sensor int sensor, boolean blocked) {
mState.put(sensor, blocked);
for (Callback callback : mCallbacks) {
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index 9effc6728bab..bbb2f1a5259a 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -161,10 +161,10 @@ public class ThemeOverlayApplier implements Dumpable {
void applyCurrentUserOverlays(
Map<String, OverlayIdentifier> categoryToPackage,
FabricatedOverlay[] pendingCreation,
- Set<UserHandle> userHandles) {
+ int currentUser,
+ Set<UserHandle> managedProfiles) {
// Disable all overlays that have not been specified in the user setting.
final Set<String> overlayCategoriesToDisable = new HashSet<>(THEME_CATEGORIES);
- overlayCategoriesToDisable.removeAll(categoryToPackage.keySet());
final Set<String> targetPackagesToQuery = overlayCategoriesToDisable.stream()
.map(category -> mCategoryToTargetPackage.get(category))
.collect(Collectors.toSet());
@@ -175,6 +175,7 @@ public class ThemeOverlayApplier implements Dumpable {
.filter(o ->
mTargetPackageToCategories.get(o.targetPackageName).contains(o.category))
.filter(o -> overlayCategoriesToDisable.contains(o.category))
+ .filter(o -> !categoryToPackage.containsValue(new OverlayIdentifier(o.packageName)))
.filter(o -> o.isEnabled())
.map(o -> new Pair<>(o.category, o.packageName))
.collect(Collectors.toList());
@@ -186,17 +187,18 @@ public class ThemeOverlayApplier implements Dumpable {
}
}
- // Toggle overlays in the order of THEME_CATEGORIES.
+ for (Pair<String, String> packageToDisable : overlaysToDisable) {
+ OverlayIdentifier overlayInfo = new OverlayIdentifier(packageToDisable.second);
+ setEnabled(transaction, overlayInfo, packageToDisable.first, currentUser,
+ managedProfiles, false);
+ }
+
for (String category : THEME_CATEGORIES) {
if (categoryToPackage.containsKey(category)) {
OverlayIdentifier overlayInfo = categoryToPackage.get(category);
- setEnabled(transaction, overlayInfo, category, userHandles, true);
+ setEnabled(transaction, overlayInfo, category, currentUser, managedProfiles, true);
}
}
- for (Pair<String, String> packageToDisable : overlaysToDisable) {
- OverlayIdentifier overlayInfo = new OverlayIdentifier(packageToDisable.second);
- setEnabled(transaction, overlayInfo, packageToDisable.first, userHandles, false);
- }
mExecutor.execute(() -> {
try {
@@ -213,18 +215,30 @@ public class ThemeOverlayApplier implements Dumpable {
}
private void setEnabled(OverlayManagerTransaction.Builder transaction,
- OverlayIdentifier identifier, String category, Set<UserHandle> handles,
- boolean enabled) {
+ OverlayIdentifier identifier, String category, int currentUser,
+ Set<UserHandle> managedProfiles, boolean enabled) {
if (DEBUG) {
Log.d(TAG, "setEnabled: " + identifier.getPackageName() + " category: "
+ category + ": " + enabled);
}
- for (UserHandle userHandle : handles) {
- transaction.setEnabled(identifier, enabled, userHandle.getIdentifier());
- }
- if (!handles.contains(UserHandle.SYSTEM) && SYSTEM_USER_CATEGORIES.contains(category)) {
+
+ transaction.setEnabled(identifier, enabled, currentUser);
+ if (currentUser != UserHandle.SYSTEM.getIdentifier()
+ && SYSTEM_USER_CATEGORIES.contains(category)) {
transaction.setEnabled(identifier, enabled, UserHandle.SYSTEM.getIdentifier());
}
+
+ // Do not apply Launcher or Theme picker overlays to managed users. Apps are not
+ // installed in there.
+ OverlayInfo overlayInfo = mOverlayManager.getOverlayInfo(identifier, UserHandle.SYSTEM);
+ if (overlayInfo == null || overlayInfo.targetPackageName.equals(mLauncherPackage)
+ || overlayInfo.targetPackageName.equals(mThemePickerPackage)) {
+ return;
+ }
+
+ for (UserHandle userHandle : managedProfiles) {
+ transaction.setEnabled(identifier, enabled, userHandle.getIdentifier());
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 028cbd084677..5d028454a417 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -55,14 +55,13 @@ import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.settings.SecureSettings;
-import com.google.android.collect.Sets;
-
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collection;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -151,7 +150,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
- updateThemeOverlays();
+ reevaluateSystemTheme(true /* forceReload */);
}
}, filter, mBgExecutor, UserHandle.ALL);
mSecureSettings.registerContentObserverForUser(
@@ -163,7 +162,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
int userId) {
if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId);
if (ActivityManager.getCurrentUser() == userId) {
- updateThemeOverlays();
+ reevaluateSystemTheme(true /* forceReload */);
}
}
},
@@ -180,7 +179,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
mLockColors = lockColors;
}
mSystemColors = systemColor;
- reevaluateSystemTheme();
+ reevaluateSystemTheme(false /* forceReload */);
});
});
if (USE_LOCK_SCREEN_WALLPAPER) {
@@ -192,7 +191,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
}
// It's possible that the user has a lock screen wallpaper. On this case we'll
// end up with different colors after unlocking.
- reevaluateSystemTheme();
+ reevaluateSystemTheme(false /* forceReload */);
}
});
}
@@ -209,11 +208,11 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
Log.d(TAG, "got new lock colors: " + wallpaperColors + " where: " + which);
}
}
- reevaluateSystemTheme();
+ reevaluateSystemTheme(false /* forceReload */);
}, null, UserHandle.USER_ALL);
}
- private void reevaluateSystemTheme() {
+ private void reevaluateSystemTheme(boolean forceReload) {
WallpaperColors currentColors =
mKeyguardStateController.isShowing() && mLockColors != null
? mLockColors : mSystemColors;
@@ -228,7 +227,8 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
accentCandidate = getAccentColor(currentColors);
}
- if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate) {
+ if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate
+ && !forceReload) {
return;
}
@@ -309,6 +309,16 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
} catch (NumberFormatException e) {
Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName());
}
+ } else if (!mIsMonetEnabled && systemPalette != null) {
+ try {
+ // It's possible that we flipped the flag off and still have a @ColorInt in the
+ // setting. We need to sanitize the input, otherwise the overlay transaction will
+ // fail.
+ Integer.parseInt(systemPalette.getPackageName().toLowerCase(), 16);
+ categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+ } catch (NumberFormatException e) {
+ // This is a package name. All good, let's continue
+ }
}
// Same for accent color.
@@ -322,6 +332,13 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
} catch (NumberFormatException e) {
Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName());
}
+ } else if (!mIsMonetEnabled && accentPalette != null) {
+ try {
+ Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16);
+ categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
+ } catch (NumberFormatException e) {
+ // This is a package name. All good, let's continue
+ }
}
// Compatibility with legacy themes, where full packages were defined, instead of just
@@ -337,10 +354,10 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, mSecondaryOverlay.getIdentifier());
}
- Set<UserHandle> userHandles = Sets.newHashSet(UserHandle.of(currentUser));
+ Set<UserHandle> managedProfiles = new HashSet<>();
for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) {
if (userInfo.isManagedProfile()) {
- userHandles.add(userInfo.getUserHandle());
+ managedProfiles.add(userInfo.getUserHandle());
}
}
if (DEBUG) {
@@ -352,9 +369,10 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
mNeedsOverlayCreation = false;
mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
mPrimaryOverlay, mSecondaryOverlay, mNeutralOverlay
- }, userHandles);
+ }, currentUser, managedProfiles);
} else {
- mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, userHandles);
+ mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
+ managedProfiles);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 97cb8736f01c..2526990dfd40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -16,8 +16,8 @@
package com.android.systemui.appops;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
-import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static junit.framework.TestCase.assertFalse;
@@ -125,9 +125,9 @@ public class AppOpsControllerTest extends SysuiTestCase {
when(mAudioManager.getActiveRecordingConfigurations())
.thenReturn(List.of(mPausedMockRecording));
- when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA))
+ when(mSensorPrivacyController.isSensorBlocked(CAMERA))
.thenReturn(false);
- when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA))
+ when(mSensorPrivacyController.isSensorBlocked(CAMERA))
.thenReturn(false);
mController = new AppOpsControllerImpl(
@@ -505,7 +505,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
assertFalse(list.get(0).isDisabled());
// Add a camera op, and disable the microphone. The camera op should be the only op returned
- mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, true);
+ mController.onSensorBlockedChanged(MICROPHONE, true);
mController.onOpActiveChanged(
AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
@@ -515,7 +515,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
// Re enable the microphone, and verify the op returns
- mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, false);
+ mController.onSensorBlockedChanged(MICROPHONE, false);
mTestableLooper.processAllMessages();
list = mController.getActiveAppOps();
@@ -538,7 +538,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
assertFalse(list.get(0).isDisabled());
// Add an audio op, and disable the camera. The audio op should be the only op returned
- mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, true);
+ mController.onSensorBlockedChanged(CAMERA, true);
mController.onOpActiveChanged(
AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
mTestableLooper.processAllMessages();
@@ -547,7 +547,7 @@ public class AppOpsControllerTest extends SysuiTestCase {
assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode());
// Re enable the camera, and verify the op returns
- mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, false);
+ mController.onSensorBlockedChanged(CAMERA, false);
mTestableLooper.processAllMessages();
list = mController.getActiveAppOps();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index 0dfebab59feb..a2a179ea29df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -121,7 +121,6 @@ public class QSPanelControllerTest extends SysuiTestCase {
mQSTileHost, mQSCustomizerController, true, mMediaHost,
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
- /* labelsFlag */ false,
mFeatureFlags
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 587020090433..cb380d51bd79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -87,7 +87,6 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
uiEventLogger,
qsLogger,
dumpManager,
- false,
featureFlags
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index e798207c6947..6067b42e0ef8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -32,7 +32,6 @@ import static com.android.systemui.theme.ThemeOverlayApplier.SYSUI_PACKAGE;
import static com.android.systemui.theme.ThemeOverlayApplier.THEME_CATEGORIES;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -88,7 +87,9 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
private static final String THEMEPICKER_PACKAGE = "com.android.wallpaper";
private static final String LAUNCHER_PACKAGE = "com.android.launcher3";
private static final UserHandle TEST_USER = UserHandle.of(5);
- private static final Set<UserHandle> TEST_USER_HANDLES = Sets.newHashSet(TEST_USER);
+ private static final UserHandle TEST_USER_MANAGED_PROFILE = UserHandle.of(6);
+ private static final Set<UserHandle> TEST_USER_HANDLES =
+ Sets.newHashSet(TEST_USER_MANAGED_PROFILE);
@Mock
OverlayManager mOverlayManager;
@@ -159,13 +160,19 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, false),
createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER,
THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, true)));
+
+ OverlayInfo launcherTargetInfo = new OverlayInfo("packageName", LAUNCHER_PACKAGE,
+ null, null, "/", 0, 0, 0, false);
+ when(mOverlayManager.getOverlayInfo(any(OverlayIdentifier.class), any()))
+ .thenReturn(launcherTargetInfo);
clearInvocations(mOverlayManager);
verify(mDumpManager).registerDumpable(any(), any());
}
@Test
public void allCategoriesSpecified_allEnabledExclusively() {
- mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
+ TEST_USER_HANDLES);
verify(mOverlayManager).commit(any());
for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
@@ -176,7 +183,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
@Test
public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() {
- mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
+ TEST_USER_HANDLES);
for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) {
if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) {
@@ -192,27 +200,25 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
@Test
public void allCategoriesSpecified_enabledForAllUserHandles() {
Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
- UserHandle newUserHandle = UserHandle.of(10);
- userHandles.add(newUserHandle);
- mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, userHandles);
+ mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
+ userHandles);
for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
eq(TEST_USER.getIdentifier()));
- verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
- eq(newUserHandle.getIdentifier()));
+ // Not enabled for work profile because the target package is LAUNCHER_PACKAGE
+ verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true),
+ eq(TEST_USER_MANAGED_PROFILE.getIdentifier()));
}
}
@Test
public void applyCurrentUserOverlays_createsPendingOverlays() {
- Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
- UserHandle newUserHandle = UserHandle.of(10);
- userHandles.add(newUserHandle);
- FabricatedOverlay[] pendingCreation = new FabricatedOverlay[] {
+ FabricatedOverlay[] pendingCreation = new FabricatedOverlay[]{
mock(FabricatedOverlay.class)
};
- mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation, userHandles);
+ mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation,
+ TEST_USER.getIdentifier(), TEST_USER_HANDLES);
for (FabricatedOverlay overlay : pendingCreation) {
verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay));
@@ -220,20 +226,13 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
}
@Test
- public void allCategoriesSpecified_overlayManagerNotQueried() {
- mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES);
-
- verify(mOverlayManager, never())
- .getOverlayInfosForTarget(anyString(), any(UserHandle.class));
- }
-
- @Test
public void someCategoriesSpecified_specifiedEnabled_unspecifiedDisabled() {
Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS);
categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID);
- mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
+ TEST_USER_HANDLES);
for (OverlayIdentifier overlayPackage : categoryToPackage.values()) {
verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
@@ -249,7 +248,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
@Test
public void zeroCategoriesSpecified_allDisabled() {
- mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(),
+ TEST_USER_HANDLES);
for (String category : THEME_CATEGORIES) {
verify(mTransactionBuilder).setEnabled(
@@ -263,7 +263,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category"));
- mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES);
+ mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
+ TEST_USER_HANDLES);
verify(mTransactionBuilder, never()).setEnabled(
eq(new OverlayIdentifier("com.example.blah.category")), eq(false),
@@ -273,23 +274,6 @@ public class ThemeOverlayApplierTest extends SysuiTestCase {
eq(TEST_USER.getIdentifier()));
}
- @Test
- public void overlayManagerOnlyQueriedForUnspecifiedPackages() {
- Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP);
- categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS);
-
- mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES);
-
- verify(mOverlayManager).getOverlayInfosForTarget(SETTINGS_PACKAGE, UserHandle.SYSTEM);
- verify(mOverlayManager, never()).getOverlayInfosForTarget(ANDROID_PACKAGE,
- UserHandle.SYSTEM);
- verify(mOverlayManager, never()).getOverlayInfosForTarget(SYSUI_PACKAGE, UserHandle.SYSTEM);
- verify(mOverlayManager, never()).getOverlayInfosForTarget(LAUNCHER_PACKAGE,
- UserHandle.SYSTEM);
- verify(mOverlayManager, never()).getOverlayInfosForTarget(THEMEPICKER_PACKAGE,
- UserHandle.SYSTEM);
- }
-
private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName,
String category, boolean enabled) {
return new OverlayInfo(packageName, null, targetPackageName, null, category, "",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index aa385effa931..d80c40fcd07b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -143,7 +143,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
ArgumentCaptor.forClass(Map.class);
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(themeOverlays.capture(), any(), any());
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
// Assert that we received the colors that we were expecting
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -175,7 +175,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
ArgumentCaptor.forClass(Map.class);
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(themeOverlays.capture(), any(), any());
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
// Assert that we received the colors that we were expecting
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -198,7 +198,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
ArgumentCaptor.forClass(Map.class);
verify(mThemeOverlayApplier)
- .applyCurrentUserOverlays(themeOverlays.capture(), any(), any());
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
// Assert that we received the colors that we were expecting
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index ebfa9171b2b9..21cae453d702 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -742,6 +742,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
}
}
+
+ if (association.isNotifyOnDeviceNearby()) {
+ ServiceConnector<ICompanionDeviceService> serviceConnector =
+ mDeviceListenerServiceConnectors.forUser(association.getUserId())
+ .get(association.getPackageName());
+ if (serviceConnector != null) {
+ serviceConnector.unbind();
+ }
+ }
}
private void updateSpecialAccessPermissionForAssociatedPackage(Association association) {
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 84e429dc83e4..f2782f64995a 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -25,9 +25,9 @@ import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static android.os.UserHandle.USER_SYSTEM;
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
import android.annotation.NonNull;
@@ -406,12 +406,12 @@ public final class SensorPrivacyService extends SystemService {
*/
@Override
public void setSensorPrivacy(boolean enable) {
+ enforceManageSensorPrivacyPermission();
// Keep the state consistent between all users to make it a single global state
forAllUsers(userId -> setSensorPrivacy(userId, enable));
}
private void setSensorPrivacy(@UserIdInt int userId, boolean enable) {
- enforceSensorPrivacyPermission();
synchronized (mLock) {
mEnabled.put(userId, enable);
persistSensorPrivacyStateLocked();
@@ -421,7 +421,7 @@ public final class SensorPrivacyService extends SystemService {
@Override
public void setIndividualSensorPrivacy(@UserIdInt int userId, int sensor, boolean enable) {
- enforceSensorPrivacyPermission();
+ enforceManageSensorPrivacyPermission();
synchronized (mLock) {
SparseBooleanArray userIndividualEnabled = mIndividualEnabled.get(userId,
new SparseBooleanArray());
@@ -448,6 +448,7 @@ public final class SensorPrivacyService extends SystemService {
@Override
public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId, int sensor,
boolean enable) {
+ enforceManageSensorPrivacyPermission();
int parentId = mUserManagerInternal.getProfileParentId(userId);
forAllUsers(userId2 -> {
if (parentId == mUserManagerInternal.getProfileParentId(userId2)) {
@@ -460,21 +461,35 @@ public final class SensorPrivacyService extends SystemService {
* Enforces the caller contains the necessary permission to change the state of sensor
* privacy.
*/
- private void enforceSensorPrivacyPermission() {
- if (mContext.checkCallingOrSelfPermission(
- MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) {
- return;
- }
- throw new SecurityException(
+ private void enforceManageSensorPrivacyPermission() {
+ enforcePermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY,
"Changing sensor privacy requires the following permission: "
+ MANAGE_SENSOR_PRIVACY);
}
/**
+ * Enforces the caller contains the necessary permission to observe changes to the sate of
+ * sensor privacy.
+ */
+ private void enforceObserveSensorPrivacyPermission() {
+ enforcePermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY,
+ "Observing sensor privacy changes requires the following permission: "
+ + android.Manifest.permission.OBSERVE_SENSOR_PRIVACY);
+ }
+
+ private void enforcePermission(String permission, String message) {
+ if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+ return;
+ }
+ throw new SecurityException(message);
+ }
+
+ /**
* Returns whether sensor privacy is enabled.
*/
@Override
public boolean isSensorPrivacyEnabled() {
+ enforceObserveSensorPrivacyPermission();
return isSensorPrivacyEnabled(USER_SYSTEM);
}
@@ -486,6 +501,7 @@ public final class SensorPrivacyService extends SystemService {
@Override
public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) {
+ enforceObserveSensorPrivacyPermission();
synchronized (mLock) {
SparseBooleanArray states = mIndividualEnabled.get(userId);
if (states == null) {
@@ -703,6 +719,7 @@ public final class SensorPrivacyService extends SystemService {
*/
@Override
public void addSensorPrivacyListener(ISensorPrivacyListener listener) {
+ enforceObserveSensorPrivacyPermission();
if (listener == null) {
throw new NullPointerException("listener cannot be null");
}
@@ -715,6 +732,7 @@ public final class SensorPrivacyService extends SystemService {
@Override
public void addIndividualSensorPrivacyListener(int userId, int sensor,
ISensorPrivacyListener listener) {
+ enforceObserveSensorPrivacyPermission();
if (listener == null) {
throw new NullPointerException("listener cannot be null");
}
@@ -726,6 +744,7 @@ public final class SensorPrivacyService extends SystemService {
*/
@Override
public void removeSensorPrivacyListener(ISensorPrivacyListener listener) {
+ enforceObserveSensorPrivacyPermission();
if (listener == null) {
throw new NullPointerException("listener cannot be null");
}
@@ -735,6 +754,7 @@ public final class SensorPrivacyService extends SystemService {
@Override
public void suppressIndividualSensorPrivacyReminders(int userId, String packageName,
IBinder token, boolean suppress) {
+ enforceManageSensorPrivacyPermission();
Objects.requireNonNull(packageName);
Objects.requireNonNull(token);
@@ -886,13 +906,13 @@ public final class SensorPrivacyService extends SystemService {
}
/**
- * Convert a string into a {@link SensorPrivacyManager.IndividualSensor id}.
+ * Convert a string into a {@link SensorPrivacyManager.Sensors.Sensor id}.
*
* @param sensor The name to convert
*
* @return The id corresponding to the name
*/
- private @SensorPrivacyManager.IndividualSensor int sensorStrToId(@Nullable String sensor) {
+ private @SensorPrivacyManager.Sensors.Sensor int sensorStrToId(@Nullable String sensor) {
if (sensor == null) {
return UNKNOWN;
}
@@ -950,7 +970,7 @@ public final class SensorPrivacyService extends SystemService {
return -1;
}
- enforceSensorPrivacyPermission();
+ enforceManageSensorPrivacyPermission();
synchronized (mLock) {
SparseBooleanArray individualEnabled =
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index c2d8fa24157a..8a2894c84cc4 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -45,14 +45,18 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.Immutable;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
@@ -87,26 +91,30 @@ public class ServiceWatcher implements ServiceConnection {
/** Function to run on binder interface when first bound. */
public interface OnBindRunner {
/** Called to run client code with the binder. */
- void run(IBinder binder, ComponentName service) throws RemoteException;
+ void run(IBinder binder, BoundService service) throws RemoteException;
}
/**
* Information on the service ServiceWatcher has selected as the best option for binding.
*/
- private static final class ServiceInfo implements Comparable<ServiceInfo> {
+ @Immutable
+ public static final class BoundService implements Comparable<BoundService> {
- public static final ServiceInfo NONE = new ServiceInfo(Integer.MIN_VALUE, null,
- UserHandle.USER_NULL, false);
+ public static final BoundService NONE = new BoundService(Integer.MIN_VALUE, null,
+ false, null, -1);
public final int version;
- @Nullable public final ComponentName component;
- @UserIdInt public final int userId;
+ @Nullable
+ public final ComponentName component;
public final boolean serviceIsMultiuser;
+ public final int uid;
+ @Nullable
+ public final Bundle metadata;
- ServiceInfo(ResolveInfo resolveInfo, int currentUserId) {
+ BoundService(ResolveInfo resolveInfo) {
Preconditions.checkArgument(resolveInfo.serviceInfo.getComponentName() != null);
- Bundle metadata = resolveInfo.serviceInfo.metaData;
+ metadata = resolveInfo.serviceInfo.metaData;
if (metadata != null) {
version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
serviceIsMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false);
@@ -116,16 +124,17 @@ public class ServiceWatcher implements ServiceConnection {
}
component = resolveInfo.serviceInfo.getComponentName();
- userId = serviceIsMultiuser ? UserHandle.USER_SYSTEM : currentUserId;
+ uid = resolveInfo.serviceInfo.applicationInfo.uid;
}
- private ServiceInfo(int version, @Nullable ComponentName component, int userId,
- boolean serviceIsMultiuser) {
+ private BoundService(int version, @Nullable ComponentName component,
+ boolean serviceIsMultiuser, @Nullable Bundle metadata, int uid) {
Preconditions.checkArgument(component != null || version == Integer.MIN_VALUE);
this.version = version;
this.component = component;
- this.userId = userId;
this.serviceIsMultiuser = serviceIsMultiuser;
+ this.metadata = metadata;
+ this.uid = uid;
}
public @Nullable String getPackageName() {
@@ -137,21 +146,21 @@ public class ServiceWatcher implements ServiceConnection {
if (this == o) {
return true;
}
- if (!(o instanceof ServiceInfo)) {
+ if (!(o instanceof BoundService)) {
return false;
}
- ServiceInfo that = (ServiceInfo) o;
- return version == that.version && userId == that.userId
+ BoundService that = (BoundService) o;
+ return version == that.version && uid == that.uid
&& Objects.equals(component, that.component);
}
@Override
public int hashCode() {
- return Objects.hash(version, component, userId);
+ return Objects.hash(version, component, uid);
}
@Override
- public int compareTo(ServiceInfo that) {
+ public int compareTo(BoundService that) {
// ServiceInfos with higher version numbers always win (having a version number >
// MIN_VALUE implies having a non-null component). if version numbers are equal, a
// non-null component wins over a null component. if the version numbers are equal and
@@ -164,10 +173,11 @@ public class ServiceWatcher implements ServiceConnection {
} else if (component != null && that.component == null) {
ret = 1;
} else {
- if (userId != UserHandle.USER_SYSTEM && that.userId == UserHandle.USER_SYSTEM) {
+ if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM
+ && UserHandle.getUserId(that.uid) == UserHandle.USER_SYSTEM) {
ret = -1;
- } else if (userId == UserHandle.USER_SYSTEM
- && that.userId != UserHandle.USER_SYSTEM) {
+ } else if (UserHandle.getUserId(uid) == UserHandle.USER_SYSTEM
+ && UserHandle.getUserId(that.uid) != UserHandle.USER_SYSTEM) {
ret = 1;
}
}
@@ -180,7 +190,8 @@ public class ServiceWatcher implements ServiceConnection {
if (component == null) {
return "none";
} else {
- return component.toShortString() + "@" + version + "[u" + userId + "]";
+ return component.toShortString() + "@" + version + "[u"
+ + UserHandle.getUserId(uid) + "]";
}
}
}
@@ -227,17 +238,23 @@ public class ServiceWatcher implements ServiceConnection {
}
};
- @Nullable private final OnBindRunner mOnBind;
- @Nullable private final Runnable mOnUnbind;
+ // read/write from handler thread only
+ private final Map<ComponentName, BoundService> mPendingBinds = new ArrayMap<>();
+
+ @Nullable
+ private final OnBindRunner mOnBind;
+
+ @Nullable
+ private final Runnable mOnUnbind;
- // write from caller thread only, read anywhere
- private volatile boolean mRegistered;
+ // read/write from handler thread only
+ private boolean mRegistered;
// read/write from handler thread only
private int mCurrentUserId;
// write from handler thread only, read anywhere
- private volatile ServiceInfo mTargetService;
+ private volatile BoundService mTargetService;
private volatile IBinder mBinder;
public ServiceWatcher(Context context, String action,
@@ -274,7 +291,7 @@ public class ServiceWatcher implements ServiceConnection {
mCurrentUserId = UserHandle.USER_NULL;
- mTargetService = ServiceInfo.NONE;
+ mTargetService = BoundService.NONE;
mBinder = null;
}
@@ -299,6 +316,11 @@ public class ServiceWatcher implements ServiceConnection {
* Starts the process of determining the best matching service and maintaining a binding to it.
*/
public void register() {
+ mHandler.sendMessage(PooledLambda.obtainMessage(ServiceWatcher::registerInternal,
+ ServiceWatcher.this));
+ }
+
+ private void registerInternal() {
Preconditions.checkState(!mRegistered);
mPackageMonitor.register(mContext, UserHandle.ALL, true, mHandler);
@@ -309,6 +331,8 @@ public class ServiceWatcher implements ServiceConnection {
mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, intentFilter, null,
mHandler);
+ // TODO: This makes the behavior of the class unpredictable as the caller needs
+ // to know the internal impl detail that calling register would pick the current user.
mCurrentUserId = ActivityManager.getCurrentUser();
mRegistered = true;
@@ -320,6 +344,11 @@ public class ServiceWatcher implements ServiceConnection {
* Stops the process of determining the best matching service and releases any binding.
*/
public void unregister() {
+ mHandler.sendMessage(PooledLambda.obtainMessage(ServiceWatcher::unregisterInternal,
+ ServiceWatcher.this));
+ }
+
+ private void unregisterInternal() {
Preconditions.checkState(mRegistered);
mRegistered = false;
@@ -333,7 +362,7 @@ public class ServiceWatcher implements ServiceConnection {
private void onBestServiceChanged(boolean forceRebind) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- ServiceInfo bestServiceInfo = ServiceInfo.NONE;
+ BoundService bestServiceInfo = BoundService.NONE;
if (mRegistered) {
List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser(
@@ -344,7 +373,7 @@ public class ServiceWatcher implements ServiceConnection {
if (!mServiceCheckPredicate.test(resolveInfo)) {
continue;
}
- ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId);
+ BoundService serviceInfo = new BoundService(resolveInfo);
if (serviceInfo.compareTo(bestServiceInfo) > 0) {
bestServiceInfo = serviceInfo;
}
@@ -356,21 +385,22 @@ public class ServiceWatcher implements ServiceConnection {
}
}
- private void rebind(ServiceInfo newServiceInfo) {
+ private void rebind(BoundService newServiceInfo) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- if (!mTargetService.equals(ServiceInfo.NONE)) {
+ if (!mTargetService.equals(BoundService.NONE)) {
if (D) {
Log.d(TAG, "[" + mIntent.getAction() + "] unbinding from " + mTargetService);
}
mContext.unbindService(this);
onServiceDisconnected(mTargetService.component);
- mTargetService = ServiceInfo.NONE;
+ mPendingBinds.remove(mTargetService.component);
+ mTargetService = BoundService.NONE;
}
mTargetService = newServiceInfo;
- if (mTargetService.equals(ServiceInfo.NONE)) {
+ if (mTargetService.equals(BoundService.NONE)) {
return;
}
@@ -381,10 +411,12 @@ public class ServiceWatcher implements ServiceConnection {
Intent bindIntent = new Intent(mIntent).setComponent(mTargetService.component);
if (!mContext.bindServiceAsUser(bindIntent, this,
BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE,
- mHandler, UserHandle.of(mTargetService.userId))) {
- mTargetService = ServiceInfo.NONE;
+ mHandler, UserHandle.of(UserHandle.getUserId(mTargetService.uid)))) {
+ mTargetService = BoundService.NONE;
Log.e(TAG, getLogPrefix() + " unexpected bind failure - retrying later");
mHandler.postDelayed(() -> onBestServiceChanged(false), RETRY_DELAY_MS);
+ } else {
+ mPendingBinds.put(mTargetService.component, mTargetService);
}
}
@@ -397,10 +429,15 @@ public class ServiceWatcher implements ServiceConnection {
Log.d(TAG, getLogPrefix() + " connected to " + component.toShortString());
}
+ final BoundService boundService = mPendingBinds.remove(component);
+ if (boundService == null) {
+ return;
+ }
+
mBinder = binder;
if (mOnBind != null) {
try {
- mOnBind.run(binder, component);
+ mOnBind.run(binder, boundService);
} catch (RuntimeException | RemoteException e) {
// binders may propagate some specific non-RemoteExceptions from the other side
// through the binder as well - we cannot allow those to crash the system server
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 33c0f9d628dc..a36d913ee669 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -307,6 +307,7 @@ import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.SystemUserHomeActivity;
+import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.content.PackageHelper;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -1820,6 +1821,15 @@ public class ActivityManagerService extends IActivityManager.Stub
ncl.start();
}
+ /**
+ * Sets a policy for handling app ops.
+ *
+ * @param appOpsPolicy The policy.
+ */
+ public void setAppOpsPolicy(@Nullable CheckOpsDelegate appOpsPolicy) {
+ mAppOpsService.setAppOpsPolicy(appOpsPolicy);
+ }
+
public IAppOpsService getAppOpsService() {
return mAppOpsService;
}
@@ -3779,10 +3789,11 @@ public class ActivityManagerService extends IActivityManager.Stub
mi.getTotalUss(), mi.getTotalRss(), false,
ProcessStats.ADD_PSS_EXTERNAL_SLOW, duration);
proc.getPkgList().forEachPackageProcessStats(holder -> {
+ final ProcessState state = holder.state;
FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
proc.info.uid,
- holder.state.getName(),
- holder.state.getPackage(),
+ state != null ? state.getName() : proc.processName,
+ state != null ? state.getPackage() : proc.info.packageName,
mi.getTotalPss(),
mi.getTotalUss(),
mi.getTotalRss(),
@@ -7214,6 +7225,9 @@ public class ActivityManagerService extends IActivityManager.Stub
final long memoryGrowthThreshold =
Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD);
mProcessList.forEachLruProcessesLOSP(false, proc -> {
+ if (proc.getThread() == null) {
+ return;
+ }
final ProcessProfileRecord pr = proc.mProfile;
final ProcessStateRecord state = proc.mState;
final int setProcState = state.getSetProcState();
@@ -13969,6 +13983,9 @@ public class ActivityManagerService extends IActivityManager.Stub
final long uptimeSince = curUptime - mLastPowerCheckUptime;
mLastPowerCheckUptime = curUptime;
mProcessList.forEachLruProcessesLOSP(false, app -> {
+ if (app.getThread() == null) {
+ return;
+ }
if (app.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) {
int cpuLimit;
long checkDur = curUptime - app.mState.getWhenUnimportant();
@@ -14070,11 +14087,12 @@ public class ActivityManagerService extends IActivityManager.Stub
mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName,
uptimeSince, cputimeUsed);
app.getPkgList().forEachPackageProcessStats(holder -> {
+ final ProcessState state = holder.state;
FrameworkStatsLog.write(
FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED,
app.info.uid,
processName,
- holder.state.getPackage(),
+ state != null ? state.getPackage() : app.info.packageName,
holder.appVersion);
});
return true;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index a776458d63c5..44dcc205a9d0 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -339,7 +339,10 @@ public class AppOpsService extends IAppOpsService.Stub {
SparseIntArray mProfileOwners;
@GuardedBy("this")
- private CheckOpsDelegate mCheckOpsDelegate;
+ private CheckOpsDelegate mAppOpsPolicy;
+
+ @GuardedBy("this")
+ private CheckOpsDelegateDispatcher mCheckOpsDelegateDispatcher;
/**
* Reverse lookup for {@link AppOpsManager#opToSwitch(int)}. Initialized once and never
@@ -1770,6 +1773,17 @@ public class AppOpsService extends IAppOpsService.Stub {
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
}
+ /**
+ * Sets a policy for handling app ops.
+ *
+ * @param appOpsPolicy The policy.
+ */
+ public void setAppOpsPolicy(@Nullable CheckOpsDelegate appOpsPolicy) {
+ synchronized (AppOpsService.this) {
+ mAppOpsPolicy = appOpsPolicy;
+ }
+ }
+
public void packageRemoved(int uid, String packageName) {
synchronized (this) {
UidState uidState = mUidStates.get(uid);
@@ -2868,13 +2882,19 @@ public class AppOpsService extends IAppOpsService.Stub {
public CheckOpsDelegate getAppOpsServiceDelegate() {
synchronized (this) {
- return mCheckOpsDelegate;
+ return (mCheckOpsDelegateDispatcher != null)
+ ? mCheckOpsDelegateDispatcher.getCheckOpsDelegate()
+ : null;
}
}
public void setAppOpsServiceDelegate(CheckOpsDelegate delegate) {
synchronized (this) {
- mCheckOpsDelegate = delegate;
+ if (delegate != null) {
+ mCheckOpsDelegateDispatcher = new CheckOpsDelegateDispatcher(delegate);
+ } else {
+ mCheckOpsDelegateDispatcher = null;
+ }
}
}
@@ -2889,19 +2909,28 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private int checkOperationInternal(int code, int uid, String packageName, boolean raw) {
- final CheckOpsDelegate delegate;
- synchronized (this) {
- delegate = mCheckOpsDelegate;
+ final CheckOpsDelegate policy;
+ final CheckOpsDelegateDispatcher delegateDispatcher;
+ synchronized (AppOpsService.this) {
+ policy = mAppOpsPolicy;
+ delegateDispatcher = mCheckOpsDelegateDispatcher;
}
- if (delegate == null) {
- return checkOperationImpl(code, uid, packageName, raw);
+ if (policy != null) {
+ if (delegateDispatcher != null) {
+ return policy.checkOperation(code, uid, packageName, raw,
+ delegateDispatcher::checkOperationImpl);
+ } else {
+ return policy.checkOperation(code, uid, packageName, raw,
+ AppOpsService.this::checkOperationImpl);
+ }
+ } else if (delegateDispatcher != null) {
+ delegateDispatcher.getCheckOpsDelegate().checkOperation(code, uid,
+ packageName, raw, AppOpsService.this::checkOperationImpl);
}
- return delegate.checkOperation(code, uid, packageName, raw,
- AppOpsService.this::checkOperationImpl);
+ return checkOperationImpl(code, uid, packageName, raw);
}
- private int checkOperationImpl(int code, int uid, String packageName,
- boolean raw) {
+ private int checkOperationImpl(int code, int uid, String packageName, boolean raw) {
verifyIncomingOp(code);
verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
@@ -2956,15 +2985,25 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public int checkAudioOperation(int code, int usage, int uid, String packageName) {
- final CheckOpsDelegate delegate;
- synchronized (this) {
- delegate = mCheckOpsDelegate;
+ final CheckOpsDelegate policy;
+ final CheckOpsDelegateDispatcher delegateDispatcher;
+ synchronized (AppOpsService.this) {
+ policy = mAppOpsPolicy;
+ delegateDispatcher = mCheckOpsDelegateDispatcher;
}
- if (delegate == null) {
- return checkAudioOperationImpl(code, usage, uid, packageName);
+ if (policy != null) {
+ if (delegateDispatcher != null) {
+ return policy.checkAudioOperation(code, usage, uid, packageName,
+ delegateDispatcher::checkAudioOperationImpl);
+ } else {
+ return policy.checkAudioOperation(code, usage, uid, packageName,
+ AppOpsService.this::checkAudioOperationImpl);
+ }
+ } else if (delegateDispatcher != null) {
+ delegateDispatcher.getCheckOpsDelegate().checkAudioOperation(code, usage,
+ uid, packageName, AppOpsService.this::checkAudioOperationImpl);
}
- return delegate.checkAudioOperation(code, usage, uid, packageName,
- AppOpsService.this::checkAudioOperationImpl);
+ return checkAudioOperationImpl(code, usage, uid, packageName);
}
private int checkAudioOperationImpl(int code, int usage, int uid, String packageName) {
@@ -3078,17 +3117,29 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public int noteOperation(int code, int uid, String packageName, String attributionTag,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage) {
- final CheckOpsDelegate delegate;
- synchronized (this) {
- delegate = mCheckOpsDelegate;
- }
- if (delegate == null) {
- return noteOperationImpl(code, uid, packageName, attributionTag,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ final CheckOpsDelegate policy;
+ final CheckOpsDelegateDispatcher delegateDispatcher;
+ synchronized (AppOpsService.this) {
+ policy = mAppOpsPolicy;
+ delegateDispatcher = mCheckOpsDelegateDispatcher;
+ }
+ if (policy != null) {
+ if (delegateDispatcher != null) {
+ return policy.noteOperation(code, uid, packageName, attributionTag,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ delegateDispatcher::noteOperationImpl);
+ } else {
+ return policy.noteOperation(code, uid, packageName, attributionTag,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ AppOpsService.this::noteOperationImpl);
+ }
+ } else if (delegateDispatcher != null) {
+ delegateDispatcher.getCheckOpsDelegate().noteOperation(code, uid, packageName,
+ attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ AppOpsService.this::noteOperationImpl);
}
- return delegate.noteOperation(code, uid, packageName, attributionTag,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- AppOpsService.this::noteOperationImpl);
+ return noteOperationImpl(code, uid, packageName, attributionTag,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
private int noteOperationImpl(int code, int uid, @Nullable String packageName,
@@ -6589,7 +6640,6 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
-
/**
* Async task for writing note op stack trace, op code, package name and version to file
* More specifically, writes all the collected ops from {@link #mNoteOpCallerStacktraces}
@@ -6726,4 +6776,34 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
}
+
+ private final class CheckOpsDelegateDispatcher {
+ private final @NonNull CheckOpsDelegate mCheckOpsDelegate;
+
+ CheckOpsDelegateDispatcher(@NonNull CheckOpsDelegate checkOpsDelegate) {
+ mCheckOpsDelegate = checkOpsDelegate;
+ }
+
+ public @NonNull CheckOpsDelegate getCheckOpsDelegate() {
+ return mCheckOpsDelegate;
+ }
+
+ public int checkOperationImpl(int code, int uid, String packageName, boolean raw) {
+ return mCheckOpsDelegate.checkOperation(code, uid, packageName, raw,
+ AppOpsService.this::checkOperationImpl);
+ }
+
+ public int checkAudioOperationImpl(int code, int usage, int uid, String packageName) {
+ return mCheckOpsDelegate.checkAudioOperation(code, usage, uid, packageName,
+ AppOpsService.this::checkAudioOperationImpl);
+ }
+
+ public int noteOperationImpl(int code, int uid, @Nullable String packageName,
+ @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
+ @Nullable String message, boolean shouldCollectMessage) {
+ return mCheckOpsDelegate.noteOperation(code, uid, packageName, featureId,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ AppOpsService.this::noteOperationImpl);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
index 7b400b6e0309..6ea4bd2b1d6d 100644
--- a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
@@ -17,7 +17,6 @@
package com.android.server.location;
import android.annotation.Nullable;
-import android.content.ComponentName;
import android.content.Context;
import android.hardware.location.ActivityRecognitionHardware;
import android.hardware.location.IActivityRecognitionHardwareClient;
@@ -27,6 +26,7 @@ import android.os.RemoteException;
import android.util.Log;
import com.android.server.ServiceWatcher;
+import com.android.server.ServiceWatcher.BoundService;
/**
* Proxy class to bind GmsCore to the ActivityRecognitionHardware.
@@ -82,7 +82,7 @@ public class HardwareActivityRecognitionProxy {
return resolves;
}
- private void onBind(IBinder binder, ComponentName service) throws RemoteException {
+ private void onBind(IBinder binder, BoundService service) throws RemoteException {
String descriptor = binder.getInterfaceDescriptor();
if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index cbbc981d9ed6..57e9fc9cb719 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -63,6 +63,7 @@ import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationManagerInternal;
+import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener;
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
@@ -82,6 +83,7 @@ import android.os.WorkSource.WorkChain;
import android.provider.Settings;
import android.stats.location.LocationStatsEnums;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -255,6 +257,9 @@ public class LocationManagerService extends ILocationManager.Stub {
final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
new CopyOnWriteArrayList<>();
+ @GuardedBy("mLock")
+ private @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener;
+
LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mInjector = injector;
@@ -319,8 +324,9 @@ public class LocationManagerService extends ILocationManager.Stub {
Preconditions.checkState(getLocationProviderManager(manager.getName()) == null);
manager.startManager();
+ manager.setOnProviderLocationTagsChangeListener(
+ mOnProviderLocationTagsChangeListener);
if (realProvider != null) {
-
// custom logic wrapping all non-passive providers
if (manager != mPassiveManager) {
boolean enableStationaryThrottling = Settings.Global.getInt(
@@ -331,7 +337,6 @@ public class LocationManagerService extends ILocationManager.Stub {
mInjector, realProvider, mEventLog);
}
}
-
manager.setRealProvider(realProvider);
}
mProviderManagers.add(manager);
@@ -456,8 +461,9 @@ public class LocationManagerService extends ILocationManager.Stub {
.setPowerUsage(Integer.parseInt(fragments[8]))
.setAccuracy(Integer.parseInt(fragments[9]))
.build();
- getOrAddLocationProviderManager(name).setMockProvider(
- new MockLocationProvider(properties, CallerIdentity.fromContext(mContext)));
+ final LocationProviderManager manager = getOrAddLocationProviderManager(name);
+ manager.setMockProvider(new MockLocationProvider(properties,
+ CallerIdentity.fromContext(mContext), /*locationTags*/ null));
}
}
@@ -1147,15 +1153,16 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void addTestProvider(String provider, ProviderProperties properties,
- String packageName, String attributionTag) {
+ List<String> locationTags, String packageName, String attributionTag) {
// unsafe is ok because app ops will verify the package name
CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag);
if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) {
return;
}
- getOrAddLocationProviderManager(provider).setMockProvider(
- new MockLocationProvider(properties, identity));
+ final LocationProviderManager manager = getOrAddLocationProviderManager(provider);
+ manager.setMockProvider(new MockLocationProvider(properties, identity,
+ (locationTags != null) ? new ArraySet<>(locationTags) : null));
}
@Override
@@ -1392,6 +1399,19 @@ public class LocationManagerService extends ILocationManager.Stub {
return new LocationTime(location.getTime(), location.getElapsedRealtimeNanos());
}
+
+ @Override
+ public void setOnProviderLocationTagsChangeListener(
+ @Nullable OnProviderLocationTagsChangeListener listener) {
+ synchronized (mLock) {
+ mOnProviderLocationTagsChangeListener = listener;
+ final int providerCount = mProviderManagers.size();
+ for (int i = 0; i < providerCount; i++) {
+ final LocationProviderManager manager = mProviderManagers.get(i);
+ manager.setOnProviderLocationTagsChangeListener(listener);
+ }
+ }
+ }
}
private static class SystemInjector implements Injector {
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index f0dd8b559d64..21a9b0442b74 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -75,8 +75,8 @@ class LocationShellCommand extends BasicShellCommandHandler {
case "add-test-provider": {
String provider = getNextArgRequired();
ProviderProperties properties = parseTestProviderProviderProperties();
- mService.addTestProvider(provider, properties, mContext.getOpPackageName(),
- mContext.getFeatureId());
+ mService.addTestProvider(provider, properties, /*locationTags*/ null,
+ mContext.getOpPackageName(), mContext.getFeatureId());
return 0;
}
case "remove-test-provider": {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 81c1e45504cb..dde45c4ab52a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -295,7 +295,7 @@ public class ContextHubService extends IContextHubService.Stub {
};
SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
manager.addSensorPrivacyListener(
- SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE, listener);
+ SensorPrivacyManager.Sensors.MICROPHONE, listener);
}
}
@@ -1079,8 +1079,8 @@ public class ContextHubService extends IContextHubService.Stub {
*/
private void sendMicrophoneDisableSettingUpdate() {
SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext);
- boolean disabled = manager.isIndividualSensorPrivacyEnabled(
- SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE);
+ boolean disabled = manager.isSensorPrivacyEnabled(
+ SensorPrivacyManager.Sensors.MICROPHONE);
Log.d(TAG, "Mic Disabled Setting: " + disabled);
mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled);
}
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 9216a6b245a6..29da177ee4a1 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -371,7 +371,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
public GnssLocationProvider(Context context, Injector injector, GnssNative gnssNative,
GnssMetrics gnssMetrics) {
- super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES);
+ super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES,
+ /*locationTags*/ null);
mContext = context;
mGnssNative = gnssNative;
diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
index 08deb8698608..9ff6e6bc8e32 100644
--- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
@@ -29,6 +29,7 @@ import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.UnaryOperator;
@@ -67,7 +68,7 @@ public abstract class AbstractLocationProvider {
* Default state value for a location provider that is disabled with no properties and an
* empty provider package list.
*/
- public static final State EMPTY_STATE = new State(false, null, null);
+ public static final State EMPTY_STATE = new State(false, null, null, null);
/**
* The provider's allowed state.
@@ -84,10 +85,14 @@ public abstract class AbstractLocationProvider {
*/
@Nullable public final CallerIdentity identity;
- private State(boolean allowed, ProviderProperties properties, CallerIdentity identity) {
+ @Nullable public final Set<String> locationTags;
+
+ private State(boolean allowed, ProviderProperties properties, CallerIdentity identity,
+ Set<String> locationTags) {
this.allowed = allowed;
this.properties = properties;
this.identity = identity;
+ this.locationTags = locationTags;
}
/**
@@ -97,7 +102,7 @@ public abstract class AbstractLocationProvider {
if (allowed == this.allowed) {
return this;
} else {
- return new State(allowed, properties, identity);
+ return new State(allowed, properties, identity, locationTags);
}
}
@@ -108,7 +113,7 @@ public abstract class AbstractLocationProvider {
if (Objects.equals(properties, this.properties)) {
return this;
} else {
- return new State(allowed, properties, identity);
+ return new State(allowed, properties, identity, locationTags);
}
}
@@ -119,10 +124,22 @@ public abstract class AbstractLocationProvider {
if (Objects.equals(identity, this.identity)) {
return this;
} else {
- return new State(allowed, properties, identity);
+ return new State(allowed, properties, identity, locationTags);
}
}
+ /**
+ * Returns a state the same as the current but with location tags set as specified.
+ */
+ public State withLocationTags(@Nullable Set<String> locationTags) {
+ if (Objects.equals(locationTags, this.locationTags)) {
+ return this;
+ } else {
+ return new State(allowed, properties, identity, locationTags);
+ }
+ }
+
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -133,12 +150,13 @@ public abstract class AbstractLocationProvider {
}
State state = (State) o;
return allowed == state.allowed && properties == state.properties
- && Objects.equals(identity, state.identity);
+ && Objects.equals(identity, state.identity)
+ && Objects.equals(locationTags, state.locationTags);
}
@Override
public int hashCode() {
- return Objects.hash(allowed, properties, identity);
+ return Objects.hash(allowed, properties, identity, locationTags);
}
}
@@ -195,13 +213,14 @@ public abstract class AbstractLocationProvider {
* An optional identity and properties may be provided to initialize the location provider.
*/
protected AbstractLocationProvider(Executor executor, @Nullable CallerIdentity identity,
- @Nullable ProviderProperties properties) {
+ @Nullable ProviderProperties properties, @Nullable Set<String> locationTags) {
Preconditions.checkArgument(identity == null || identity.getListenerId() == null);
mExecutor = executor;
mInternalState = new AtomicReference<>(new InternalState(null,
State.EMPTY_STATE
.withIdentity(identity)
- .withProperties(properties)));
+ .withProperties(properties).withLocationTags(locationTags))
+ );
mController = new Controller();
}
@@ -281,6 +300,13 @@ public abstract class AbstractLocationProvider {
}
/**
+ * Call this method to report a change in provider location tags.
+ */
+ protected void setLocationTags(@Nullable Set<String> locationTags) {
+ setState(state -> state.withLocationTags(locationTags));
+ }
+
+ /**
* Call this method to report a new location.
*/
protected void reportLocation(LocationResult locationResult) {
diff --git a/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java
index a3ec867220dd..49f6e64a1e0c 100644
--- a/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java
@@ -40,7 +40,7 @@ class DelegateLocationProvider extends AbstractLocationProvider
private boolean mInitialized = false;
DelegateLocationProvider(Executor executor, AbstractLocationProvider delegate) {
- super(executor, null, null);
+ super(executor, null, null, null);
mDelegate = delegate;
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 5a35d7f88ff3..1ecf3ee40a53 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -52,6 +52,8 @@ import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationManagerInternal;
+import android.location.LocationManagerInternal.LocationTagInfo;
+import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener;
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
@@ -85,6 +87,7 @@ import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.location.LocationPermissions;
@@ -117,6 +120,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
@@ -1288,6 +1292,9 @@ public class LocationProviderManager extends
@GuardedBy("mLock")
private @Nullable OnAlarmListener mDelayedRegister;
+ @GuardedBy("mLock")
+ private @Nullable OnProviderLocationTagsChangeListener mOnLocationTagsChangeListener;
+
public LocationProviderManager(Context context, Injector injector, LocationEventLog eventLog,
String name, @Nullable PassiveLocationProviderManager passiveManager) {
mContext = context;
@@ -1448,6 +1455,19 @@ public class LocationProviderManager extends
}
}
+ /**
+ * Registers a listener for the location tags of the provider.
+ *
+ * @param listener The listener
+ */
+ public void setOnProviderLocationTagsChangeListener(
+ @Nullable OnProviderLocationTagsChangeListener listener) {
+ Preconditions.checkArgument(mOnLocationTagsChangeListener == null || listener == null);
+ synchronized (mLock) {
+ mOnLocationTagsChangeListener = listener;
+ }
+ }
+
public void setMockProvider(@Nullable MockLocationProvider provider) {
synchronized (mLock) {
Preconditions.checkState(mState != STATE_STOPPED);
@@ -2244,6 +2264,27 @@ public class LocationProviderManager extends
if (oldState.allowed != newState.allowed) {
onEnabledChanged(UserHandle.USER_ALL);
}
+
+ if (!Objects.equals(oldState.locationTags, newState.locationTags)) {
+ if (mOnLocationTagsChangeListener != null) {
+ if (oldState.identity != null) {
+ FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
+ OnProviderLocationTagsChangeListener::onLocationTagsChanged,
+ mOnLocationTagsChangeListener, new LocationTagInfo(
+ oldState.identity.getUid(), oldState.identity.getPackageName(),
+ Collections.emptySet())
+ ));
+ }
+ if (newState.identity != null) {
+ FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
+ OnProviderLocationTagsChangeListener::onLocationTagsChanged,
+ mOnLocationTagsChangeListener, new LocationTagInfo(
+ newState.identity.getUid(), newState.identity.getPackageName(),
+ newState.locationTags)
+ ));
+ }
+ }
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
index 0d8f64377db0..7660f56f1580 100644
--- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
@@ -28,6 +28,7 @@ import android.os.Bundle;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Set;
/**
* A mock location provider used by LocationManagerService to implement test providers.
@@ -38,9 +39,10 @@ public class MockLocationProvider extends AbstractLocationProvider {
@Nullable private Location mLocation;
- public MockLocationProvider(ProviderProperties properties, CallerIdentity identity) {
+ public MockLocationProvider(ProviderProperties properties, CallerIdentity identity,
+ @Nullable Set<String> locationTags) {
// using a direct executor is ok because this class has no locks that could deadlock
- super(DIRECT_EXECUTOR, identity, properties);
+ super(DIRECT_EXECUTOR, identity, properties, locationTags);
}
/** Sets the allowed state of this mock provider. */
diff --git a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
index cb7264e55fa9..4ffa9a509a23 100644
--- a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
@@ -75,7 +75,7 @@ public class MockableLocationProvider extends AbstractLocationProvider {
public MockableLocationProvider(Object ownerLock) {
// using a direct executor is acceptable because all inbound calls are delegated to the
// actual provider implementations which will use their own executors
- super(DIRECT_EXECUTOR, null, null);
+ super(DIRECT_EXECUTOR, null, null, null);
mOwnerLock = ownerLock;
mRequest = ProviderRequest.EMPTY_REQUEST;
}
diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
index a5758a37b983..ee9d35d21798 100644
--- a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
@@ -47,7 +47,8 @@ public class PassiveLocationProvider extends AbstractLocationProvider {
public PassiveLocationProvider(Context context) {
// using a direct executor is ok because this class has no locks that could deadlock
- super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context), PROPERTIES);
+ super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context), PROPERTIES,
+ /*locationTags*/ null);
setAllowed(true);
}
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index 4c97f645aac9..f00478a3488a 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -18,6 +18,7 @@ package com.android.server.location.provider.proxy;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
@@ -32,10 +33,12 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.ArraySet;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.server.ServiceWatcher;
+import com.android.server.ServiceWatcher.BoundService;
import com.android.server.location.provider.AbstractLocationProvider;
import java.io.FileDescriptor;
@@ -49,6 +52,9 @@ import java.util.Objects;
*/
public class ProxyLocationProvider extends AbstractLocationProvider {
+ private static final String KEY_LOCATION_TAGS = "android:location_allow_listed_tags";
+ private static final String LOCATION_TAGS_SEPARATOR = ";";
+
/**
* Creates and registers this proxy. If no suitable service is available for the proxy, returns
* null.
@@ -84,7 +90,7 @@ public class ProxyLocationProvider extends AbstractLocationProvider {
int nonOverlayPackageResId) {
// safe to use direct executor since our locks are not acquired in a code path invoked by
// our owning provider
- super(DIRECT_EXECUTOR, null, null);
+ super(DIRECT_EXECUTOR, null, null, null);
mContext = context;
mServiceWatcher = new ServiceWatcher(context, action, this::onBind,
@@ -94,22 +100,34 @@ public class ProxyLocationProvider extends AbstractLocationProvider {
mRequest = ProviderRequest.EMPTY_REQUEST;
}
+ private void updateLocationTagInfo(@NonNull BoundService boundService) {
+ if (boundService.metadata != null) {
+ final String tagsList = boundService.metadata.getString(KEY_LOCATION_TAGS);
+ if (tagsList != null) {
+ final String[] tags = tagsList.split(LOCATION_TAGS_SEPARATOR);
+ setLocationTags(new ArraySet<>(tags));
+ }
+ }
+ }
+
private boolean checkServiceResolves() {
return mServiceWatcher.checkServiceResolves();
}
- private void onBind(IBinder binder, ComponentName service) throws RemoteException {
+ private void onBind(IBinder binder, BoundService boundService) throws RemoteException {
ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
synchronized (mLock) {
mProxy = new Proxy();
- mService = service;
+ mService = boundService.component;
provider.setLocationProviderManager(mProxy);
ProviderRequest request = mRequest;
if (!request.equals(ProviderRequest.EMPTY_REQUEST)) {
provider.setRequest(request);
}
+
+ updateLocationTagInfo(boundService);
}
}
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
new file mode 100644
index 000000000000..c9653909adb6
--- /dev/null
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal;
+import android.location.LocationManagerInternal;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.function.HeptFunction;
+import com.android.internal.util.function.QuadFunction;
+import com.android.server.LocalServices;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class defines policy for special behaviors around app ops.
+ */
+public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegate {
+ @NonNull
+ private final Object mLock = new Object();
+
+ /**
+ * The locking policy around the location tags is a bit special. Since we want to
+ * avoid grabbing the lock on every op note we are taking the approach where the
+ * read and write are being done via a thread-safe data structure such that the
+ * lookup/insert are single thread-safe calls. When we update the cached state we
+ * use a lock to ensure the update's lookup and store calls are done atomically,
+ * so multiple writers would not interleave. The tradeoff is we make is that the
+ * concurrent data structure would use boxing/unboxing of integers but this is
+ * preferred to locking.
+ */
+ @GuardedBy("mLock - writes only - see above")
+ @NonNull
+ private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags =
+ new ConcurrentHashMap();
+
+ public AppOpsPolicy() {
+ final LocationManagerInternal locationManagerInternal = LocalServices.getService(
+ LocationManagerInternal.class);
+ locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> {
+ synchronized (mLock) {
+ final int uid = providerTagInfo.getUid();
+ // We make a copy of the per UID state to limit our mutation to one
+ // operation in the underlying concurrent data structure.
+ ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
+ if (uidTags != null) {
+ uidTags = new ArrayMap<>(uidTags);
+ }
+
+ final String packageName = providerTagInfo.getPackageName();
+ ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
+ if (packageTags != null) {
+ packageTags = new ArraySet<>(packageTags);
+ }
+
+ final Set<String> providerTags = providerTagInfo.getTags();
+ if (providerTags != null && !providerTags.isEmpty()) {
+ if (packageTags != null) {
+ packageTags.clear();
+ packageTags.addAll(providerTags);
+ } else {
+ packageTags = new ArraySet<>(providerTags);
+ }
+ if (uidTags == null) {
+ uidTags = new ArrayMap<>();
+ }
+ uidTags.put(packageName, packageTags);
+ mLocationTags.put(uid, uidTags);
+ } else if (uidTags != null) {
+ uidTags.remove(packageName);
+ if (!uidTags.isEmpty()) {
+ mLocationTags.put(uid, uidTags);
+ } else {
+ mLocationTags.remove(uid);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public int checkOperation(int code, int uid, String packageName, boolean raw,
+ QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl) {
+ return superImpl.apply(code, uid, packageName, raw);
+ }
+
+ @Override
+ public int checkAudioOperation(int code, int usage, int uid, String packageName,
+ QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) {
+ return superImpl.apply(code, usage, uid, packageName);
+ }
+
+ @Override
+ public int noteOperation(int code, int uid, @Nullable String packageName,
+ @Nullable String featureId, boolean shouldCollectAsyncNotedOp, @Nullable String message,
+ boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer, String, String,
+ Boolean, String, Boolean, Integer> superImpl) {
+ if (isHandledOp(code)) {
+ // Only a single lookup from the underlying concurrent data structure
+ final ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
+ if (uidTags != null) {
+ final ArraySet<String> packageTags = uidTags.get(packageName);
+ if (packageTags != null && packageTags.contains(featureId)) {
+ return superImpl.apply(resolveLocationOp(code), uid, packageName, featureId,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ }
+ }
+ }
+ return superImpl.apply(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
+ message, shouldCollectMessage);
+ }
+
+ private static boolean isHandledOp(int code) {
+ switch (code) {
+ case AppOpsManager.OP_FINE_LOCATION:
+ case AppOpsManager.OP_COARSE_LOCATION:
+ return true;
+ }
+ return false;
+ }
+
+ private static int resolveLocationOp(int code) {
+ switch (code) {
+ case AppOpsManager.OP_FINE_LOCATION:
+ return AppOpsManager.OP_FINE_LOCATION_SOURCE;
+ case AppOpsManager.OP_COARSE_LOCATION:
+ return AppOpsManager.OP_COARSE_LOCATION_SOURCE;
+ }
+ return code;
+ }
+}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index c4f29ea69218..ef0079e0c01f 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -16,6 +16,8 @@
package com.android.server.powerstats;
+import static java.lang.System.currentTimeMillis;
+
import android.content.Context;
import android.hardware.power.stats.Channel;
import android.hardware.power.stats.EnergyConsumer;
@@ -26,11 +28,14 @@ import android.hardware.power.stats.StateResidencyResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemClock;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.VisibleForTesting;
+
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils;
import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
@@ -61,6 +66,8 @@ public final class PowerStatsLogger extends Handler {
protected static final int MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY = 1;
protected static final int MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY = 2;
+ // TODO(b/181240441): Add a listener to update the Wall clock baseline when changed
+ private final long mStartWallTime;
private final PowerStatsDataStorage mPowerStatsMeterStorage;
private final PowerStatsDataStorage mPowerStatsModelStorage;
private final PowerStatsDataStorage mPowerStatsResidencyStorage;
@@ -79,6 +86,8 @@ public final class PowerStatsLogger extends Handler {
// Log power meter data.
EnergyMeasurement[] energyMeasurements =
mPowerStatsHALWrapper.readEnergyMeter(new int[0]);
+ EnergyMeasurementUtils.adjustTimeSinceBootToEpoch(energyMeasurements,
+ mStartWallTime);
mPowerStatsMeterStorage.write(
EnergyMeasurementUtils.getProtoBytes(energyMeasurements));
if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements);
@@ -86,6 +95,8 @@ public final class PowerStatsLogger extends Handler {
// Log power model data without attribution data.
EnergyConsumerResult[] ecrNoAttribution =
mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
+ EnergyConsumerResultUtils.adjustTimeSinceBootToEpoch(ecrNoAttribution,
+ mStartWallTime);
mPowerStatsModelStorage.write(
EnergyConsumerResultUtils.getProtoBytes(ecrNoAttribution, false));
if (DEBUG) EnergyConsumerResultUtils.print(ecrNoAttribution);
@@ -97,6 +108,8 @@ public final class PowerStatsLogger extends Handler {
// Log power model data with attribution data.
EnergyConsumerResult[] ecrAttribution =
mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
+ EnergyConsumerResultUtils.adjustTimeSinceBootToEpoch(ecrAttribution,
+ mStartWallTime);
mPowerStatsModelStorage.write(
EnergyConsumerResultUtils.getProtoBytes(ecrAttribution, true));
if (DEBUG) EnergyConsumerResultUtils.print(ecrAttribution);
@@ -108,6 +121,8 @@ public final class PowerStatsLogger extends Handler {
// Log state residency data.
StateResidencyResult[] stateResidencyResults =
mPowerStatsHALWrapper.getStateResidency(new int[0]);
+ StateResidencyResultUtils.adjustTimeSinceBootToEpoch(stateResidencyResults,
+ mStartWallTime);
mPowerStatsResidencyStorage.write(
StateResidencyResultUtils.getProtoBytes(stateResidencyResults));
if (DEBUG) StateResidencyResultUtils.print(stateResidencyResults);
@@ -293,12 +308,19 @@ public final class PowerStatsLogger extends Handler {
return mDeleteResidencyDataOnBoot;
}
+ @VisibleForTesting
+ public long getStartWallTime() {
+ return mStartWallTime;
+ }
+
public PowerStatsLogger(Context context, File dataStoragePath,
String meterFilename, String meterCacheFilename,
String modelFilename, String modelCacheFilename,
String residencyFilename, String residencyCacheFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
super(Looper.getMainLooper());
+ mStartWallTime = currentTimeMillis() - SystemClock.elapsedRealtime();
+ if (DEBUG) Slog.d(TAG, "mStartWallTime: " + mStartWallTime);
mPowerStatsHALWrapper = powerStatsHALWrapper;
mDataStoragePath = dataStoragePath;
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
index 11b22a574476..746a09882f93 100644
--- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -109,6 +109,18 @@ public class ProtoStreamUtils {
}
static class StateResidencyResultUtils {
+ public static void adjustTimeSinceBootToEpoch(StateResidencyResult[] stateResidencyResult,
+ long startWallTime) {
+ for (int i = 0; i < stateResidencyResult.length; i++) {
+ final int stateLength = stateResidencyResult[i].stateResidencyData.length;
+ for (int j = 0; j < stateLength; j++) {
+ final StateResidency stateResidencyData =
+ stateResidencyResult[i].stateResidencyData[j];
+ stateResidencyData.lastEntryTimestampMs += startWallTime;
+ }
+ }
+ }
+
public static byte[] getProtoBytes(StateResidencyResult[] stateResidencyResult) {
ProtoOutputStream pos = new ProtoOutputStream();
packProtoMessage(stateResidencyResult, pos);
@@ -306,6 +318,13 @@ public class ProtoStreamUtils {
}
static class EnergyMeasurementUtils {
+ public static void adjustTimeSinceBootToEpoch(EnergyMeasurement[] energyMeasurement,
+ long startWallTime) {
+ for (int i = 0; i < energyMeasurement.length; i++) {
+ energyMeasurement[i].timestampMs += startWallTime;
+ }
+ }
+
public static byte[] getProtoBytes(EnergyMeasurement[] energyMeasurement) {
ProtoOutputStream pos = new ProtoOutputStream();
packProtoMessage(energyMeasurement, pos);
@@ -518,6 +537,13 @@ public class ProtoStreamUtils {
}
static class EnergyConsumerResultUtils {
+ public static void adjustTimeSinceBootToEpoch(EnergyConsumerResult[] energyConsumerResult,
+ long startWallTime) {
+ for (int i = 0; i < energyConsumerResult.length; i++) {
+ energyConsumerResult[i].timestampMs += startWallTime;
+ }
+ }
+
public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult,
boolean includeAttribution) {
ProtoOutputStream pos = new ProtoOutputStream();
diff --git a/services/core/java/com/android/server/timedetector/DeviceConfig.java b/services/core/java/com/android/server/timedetector/DeviceConfig.java
deleted file mode 100644
index 7b9ad0f531aa..000000000000
--- a/services/core/java/com/android/server/timedetector/DeviceConfig.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.timedetector;
-
-import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringDef;
-
-import java.time.Duration;
-import java.util.concurrent.Executor;
-
-/**
- * A helper class for reading / monitoring the {@link
- * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace for server-configured flags.
- */
-public final class DeviceConfig {
-
- /**
- * An annotation used to indicate when a {@link
- * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} key is required.
- *
- * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider
- * also shares the {@link android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the
- * prefix "geotz_" on all of its key strings.
- */
- @StringDef(prefix = "KEY_", value = {
- KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED,
- KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT,
- KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
- KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
- KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
- })
- @interface DeviceConfigKey {}
-
- /**
- * The key to force location time zone detection on for a device. Only intended for use during
- * release testing with droidfooders. The user can still disable the feature by turning off the
- * master location switch, or disabling automatic time zone detection.
- */
- @DeviceConfigKey
- public static final String KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED =
- "force_location_time_zone_detection_enabled";
-
- /**
- * The key for the default value used to determine whether location time zone detection is
- * enabled when the user hasn't explicitly set it yet.
- */
- @DeviceConfigKey
- public static final String KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT =
- "location_time_zone_detection_enabled_default";
-
- /**
- * The key for the minimum delay after location time zone detection has been enabled before the
- * location time zone manager can report it is uncertain about the time zone.
- */
- @DeviceConfigKey
- public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS =
- "location_time_zone_detection_uncertainty_delay_millis";
-
- /**
- * The key for the timeout passed to a location time zone provider that tells it how long it has
- * to provide an explicit first suggestion without being declared uncertain.
- */
- @DeviceConfigKey
- public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS =
- "ltpz_init_timeout_millis";
-
- /**
- * The key for the extra time added to {@link
- * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone
- * manager before the location time zone provider will actually be declared uncertain.
- */
- @DeviceConfigKey
- public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
- "ltpz_init_timeout_fuzz_millis";
-
- /** Creates an instance. */
- public DeviceConfig() {}
-
- /** Adds a listener for the system_time namespace. */
- public void addListener(
- @NonNull Executor handlerExecutor, @NonNull Runnable listener) {
- android.provider.DeviceConfig.addOnPropertiesChangedListener(
- NAMESPACE_SYSTEM_TIME,
- handlerExecutor,
- properties -> listener.run());
- }
-
- /**
- * Returns a boolean value from {@link android.provider.DeviceConfig} from the system_time
- * namespace, or {@code defaultValue} if there is no explicit value set.
- */
- public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) {
- return android.provider.DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue);
- }
-
- /**
- * Returns a positive duration from {@link android.provider.DeviceConfig} from the system_time
- * namespace, or {@code defaultValue} if there is no explicit value set.
- */
- @Nullable
- public Duration getDurationFromMillis(
- @DeviceConfigKey String key, @Nullable Duration defaultValue) {
- long deviceConfigValue =
- android.provider.DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1);
- if (deviceConfigValue < 0) {
- return defaultValue;
- }
- return Duration.ofMillis(deviceConfigValue);
- }
-}
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
new file mode 100644
index 000000000000..8819b371b225
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timedetector;
+
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.content.Context;
+import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.timezonedetector.ConfigurationChangeListener;
+import com.android.server.timezonedetector.ServiceConfigAccessor;
+
+import java.time.Duration;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * A helper class for reading / monitoring the {@link DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace
+ * for server-configured flags.
+ */
+public final class ServerFlags {
+
+ private static final Optional<Boolean> OPTIONAL_TRUE = Optional.of(true);
+ private static final Optional<Boolean> OPTIONAL_FALSE = Optional.of(false);
+
+ /**
+ * An annotation used to indicate when a {@link DeviceConfig#NAMESPACE_SYSTEM_TIME} key is
+ * required.
+ *
+ * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider
+ * also shares the {@link DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the
+ * prefix "geotz_" on all of its key strings.
+ */
+ @StringDef(prefix = "KEY_", value = {
+ KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
+ KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+ KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+ KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+ KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+ KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
+ KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
+ KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
+ })
+ @interface DeviceConfigKey {}
+
+ /**
+ * Controls whether the location time zone manager service will started. Only observed if
+ * the device build is configured to support location-based time zone detection. See
+ * {@link ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupportedInConfig()} and {@link
+ * ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupported()}.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED =
+ "location_time_zone_detection_feature_supported";
+
+ /**
+ * The key for the server flag that can override the device config for whether the primary
+ * location time zone provider is enabled or disabled.
+ */
+ @DeviceConfigKey
+ public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
+ "primary_location_time_zone_provider_enabled_override";
+
+ /**
+ * The key for the server flag that can override the device config for whether the secondary
+ * location time zone provider is enabled or disabled.
+ */
+ @DeviceConfigKey
+ public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
+ "secondary_location_time_zone_provider_enabled_override";
+
+ /**
+ * The key for the minimum delay after location time zone detection has been enabled before the
+ * location time zone manager can report it is uncertain about the time zone.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS =
+ "location_time_zone_detection_uncertainty_delay_millis";
+
+ /**
+ * The key for the timeout passed to a location time zone provider that tells it how long it has
+ * to provide an explicit first suggestion without being declared uncertain.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS =
+ "ltpz_init_timeout_millis";
+
+ /**
+ * The key for the extra time added to {@link
+ * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone
+ * manager before the location time zone provider will actually be declared uncertain.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
+ "ltpz_init_timeout_fuzz_millis";
+
+ /**
+ * The key for the server flag that can override location time zone detection being enabled for
+ * a user. Only intended for use during release testing with droidfooders. The user can still
+ * disable the feature by turning off the master location switch, or by disabling automatic time
+ * zone detection.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE =
+ "location_time_zone_detection_setting_enabled_override";
+
+ /**
+ * The key for the default value used to determine whether location time zone detection is
+ * enabled when the user hasn't explicitly set it yet.
+ */
+ @DeviceConfigKey
+ public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT =
+ "location_time_zone_detection_setting_enabled_default";
+
+ @GuardedBy("mListeners")
+ private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>();
+
+ private static final Object SLOCK = new Object();
+
+ @GuardedBy("SLOCK")
+ @Nullable
+ private static ServerFlags sInstance;
+
+ private ServerFlags(Context context) {
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_SYSTEM_TIME,
+ context.getMainExecutor(),
+ this::handlePropertiesChanged);
+ }
+
+ /** Returns the singleton instance. */
+ public static ServerFlags getInstance(Context context) {
+ synchronized (SLOCK) {
+ if (sInstance == null) {
+ sInstance = new ServerFlags(context);
+ }
+ return sInstance;
+ }
+ }
+
+ private void handlePropertiesChanged(@NonNull DeviceConfig.Properties properties) {
+ synchronized (mListeners) {
+ for (Map.Entry<ConfigurationChangeListener, Set<String>> listenerEntry
+ : mListeners.entrySet()) {
+ if (intersects(listenerEntry.getValue(), properties.getKeyset())) {
+ listenerEntry.getKey().onChange();
+ }
+ }
+ }
+ }
+
+ private static boolean intersects(@NonNull Set<String> one, @NonNull Set<String> two) {
+ for (String toFind : one) {
+ if (two.contains(toFind)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Adds a listener for the system_time namespace that will trigger if any of the specified keys
+ * change. Listener callbacks are delivered on the main looper thread.
+ *
+ * <p>Note: Only for use by long-lived objects like other singletons. There is deliberately no
+ * associated remove method.
+ */
+ public void addListener(@NonNull ConfigurationChangeListener listener,
+ @NonNull Set<String> keys) {
+ Objects.requireNonNull(listener);
+ Objects.requireNonNull(keys);
+
+ synchronized (mListeners) {
+ mListeners.put(listener, keys);
+ }
+ }
+
+ /**
+ * Returns an optional boolean value from {@link DeviceConfig} from the system_time
+ * namespace, returns {@link Optional#empty()} if there is no explicit value set.
+ */
+ @NonNull
+ public Optional<Boolean> getOptionalBoolean(@DeviceConfigKey String key) {
+ String value = DeviceConfig.getProperty(NAMESPACE_SYSTEM_TIME, key);
+ return parseOptionalBoolean(value);
+ }
+
+ @NonNull
+ private static Optional<Boolean> parseOptionalBoolean(@Nullable String value) {
+ if (value == null) {
+ return Optional.empty();
+ } else {
+ return Boolean.parseBoolean(value) ? OPTIONAL_TRUE : OPTIONAL_FALSE;
+ }
+ }
+
+ /**
+ * Returns a boolean value from {@link DeviceConfig} from the system_time
+ * namespace, or {@code defaultValue} if there is no explicit value set.
+ */
+ public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) {
+ return DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue);
+ }
+
+ /**
+ * Returns a positive duration from {@link DeviceConfig} from the system_time
+ * namespace, or {@code defaultValue} if there is no explicit value set.
+ */
+ @Nullable
+ public Duration getDurationFromMillis(
+ @DeviceConfigKey String key, @Nullable Duration defaultValue) {
+ long deviceConfigValue = DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1);
+ if (deviceConfigValue < 0) {
+ return defaultValue;
+ }
+ return Duration.ofMillis(deviceConfigValue);
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java
index 4c7b1f38dd5a..aa8ad37815bf 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java
@@ -17,10 +17,11 @@
package com.android.server.timezonedetector;
/**
- * A listener used to receive notification that time zone configuration has changed.
+ * A listener used to receive notification that configuration has / may have changed (depending on
+ * the usecase).
*/
@FunctionalInterface
public interface ConfigurationChangeListener {
- /** Called when the current user or a configuration value has changed. */
+ /** Called when the configuration may have changed. */
void onChange();
}
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 1f73977444f8..3ae9d641e81c 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -33,26 +33,27 @@ import com.android.internal.util.Preconditions;
import java.util.Objects;
/**
- * Holds all configuration values that affect time zone behavior and some associated logic, e.g.
- * {@link #getAutoDetectionEnabledBehavior()}, {@link #getGeoDetectionEnabledBehavior()} and {@link
- * #createCapabilitiesAndConfig()}.
+ * Holds configuration values that affect user-facing time zone behavior and some associated logic.
+ * Some configuration is global, some is user scoped, but this class deliberately doesn't make a
+ * distinction for simplicity.
*/
public final class ConfigurationInternal {
- private final @UserIdInt int mUserId;
- private final boolean mUserConfigAllowed;
private final boolean mAutoDetectionSupported;
private final boolean mGeoDetectionSupported;
private final boolean mAutoDetectionEnabled;
+ private final @UserIdInt int mUserId;
+ private final boolean mUserConfigAllowed;
private final boolean mLocationEnabled;
private final boolean mGeoDetectionEnabled;
private ConfigurationInternal(Builder builder) {
- mUserId = builder.mUserId;
- mUserConfigAllowed = builder.mUserConfigAllowed;
mAutoDetectionSupported = builder.mAutoDetectionSupported;
mGeoDetectionSupported = builder.mGeoDetectionSupported;
mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
+
+ mUserId = builder.mUserId;
+ mUserConfigAllowed = builder.mUserConfigAllowed;
mLocationEnabled = builder.mLocationEnabled;
mGeoDetectionEnabled = builder.mGeoDetectionEnabled;
// if mGeoDetectionSupported then mAutoDetectionSupported, i.e. mGeoDetectionSupported
@@ -60,22 +61,6 @@ public final class ConfigurationInternal {
Preconditions.checkState(mAutoDetectionSupported || !mGeoDetectionSupported);
}
- /** Returns the ID of the user this configuration is associated with. */
- public @UserIdInt int getUserId() {
- return mUserId;
- }
-
- /** Returns the handle of the user this configuration is associated with. */
- @NonNull
- public UserHandle getUserHandle() {
- return UserHandle.of(mUserId);
- }
-
- /** Returns true if the user allowed to modify time zone configuration. */
- public boolean isUserConfigAllowed() {
- return mUserConfigAllowed;
- }
-
/** Returns true if the device supports any form of auto time zone detection. */
public boolean isAutoDetectionSupported() {
return mAutoDetectionSupported;
@@ -98,6 +83,22 @@ public final class ConfigurationInternal {
return mAutoDetectionSupported && mAutoDetectionEnabled;
}
+ /** Returns the ID of the user this configuration is associated with. */
+ public @UserIdInt int getUserId() {
+ return mUserId;
+ }
+
+ /** Returns the handle of the user this configuration is associated with. */
+ @NonNull
+ public UserHandle getUserHandle() {
+ return UserHandle.of(mUserId);
+ }
+
+ /** Returns true if the user allowed to modify time zone configuration. */
+ public boolean isUserConfigAllowed() {
+ return mUserConfigAllowed;
+ }
+
/** Returns true if user's location can be used generally. */
public boolean isLocationEnabled() {
return mLocationEnabled;
@@ -283,7 +284,7 @@ public final class ConfigurationInternal {
/**
* Sets whether any form of automatic time zone detection is supported on this device.
*/
- public Builder setAutoDetectionSupported(boolean supported) {
+ public Builder setAutoDetectionFeatureSupported(boolean supported) {
mAutoDetectionSupported = supported;
return this;
}
@@ -291,7 +292,7 @@ public final class ConfigurationInternal {
/**
* Sets whether geolocation time zone detection is supported on this device.
*/
- public Builder setGeoDetectionSupported(boolean supported) {
+ public Builder setGeoDetectionFeatureSupported(boolean supported) {
mGeoDetectionSupported = supported;
return this;
}
diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
index f52b9b1b1c58..e3caae9482d9 100644
--- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java
@@ -31,9 +31,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.location.LocationManager;
-import android.net.ConnectivityManager;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -42,10 +40,9 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
-import com.android.server.timedetector.DeviceConfig;
import java.util.Objects;
-import java.util.concurrent.Executor;
+import java.util.Optional;
/**
* The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
@@ -59,8 +56,7 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
@NonNull private final Handler mHandler;
@NonNull private final ContentResolver mCr;
@NonNull private final UserManager mUserManager;
- @NonNull private final DeviceConfig mDeviceConfig;
- @NonNull private final boolean mGeoDetectionSupported;
+ @NonNull private final ServiceConfigAccessor mServiceConfigAccessor;
@NonNull private final LocationManager mLocationManager;
// @NonNull after setConfigChangeListener() is called.
@@ -68,17 +64,16 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
private ConfigurationChangeListener mConfigChangeListener;
EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
- @NonNull DeviceConfig deviceConfig, boolean geoDetectionSupported) {
+ @NonNull ServiceConfigAccessor serviceConfigAccessor) {
mContext = Objects.requireNonNull(context);
mHandler = Objects.requireNonNull(handler);
- Executor handlerExecutor = new HandlerExecutor(mHandler);
mCr = context.getContentResolver();
mUserManager = context.getSystemService(UserManager.class);
mLocationManager = context.getSystemService(LocationManager.class);
- mDeviceConfig = deviceConfig;
- mGeoDetectionSupported = geoDetectionSupported;
+ mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
- // Wire up the change listeners. All invocations are performed on the mHandler thread.
+ // Wire up the config change listeners. All invocations are performed on the mHandler
+ // thread.
// Listen for the user changing / the user's location mode changing.
IntentFilter filter = new IntentFilter();
@@ -112,13 +107,6 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
handleConfigChangeOnHandlerThread();
}
}, UserHandle.USER_ALL);
-
- // Add async callbacks for changes to server-side flags: some of the flags affect device /
- // user config. All changes can be treated like a config change. If flags that affect config
- // haven't changed then call will be a no-op.
- mDeviceConfig.addListener(
- handlerExecutor,
- this::handleConfigChangeOnHandlerThread);
}
private void handleConfigChangeOnHandlerThread() {
@@ -140,10 +128,12 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
@Override
public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
return new ConfigurationInternal.Builder(userId)
- .setUserConfigAllowed(isUserConfigAllowed(userId))
- .setAutoDetectionSupported(isAutoDetectionSupported())
- .setGeoDetectionSupported(isGeoDetectionSupported())
+ .setAutoDetectionFeatureSupported(
+ mServiceConfigAccessor.isAutoDetectionFeatureSupported())
+ .setGeoDetectionFeatureSupported(
+ mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported())
.setAutoDetectionEnabled(isAutoDetectionEnabled())
+ .setUserConfigAllowed(isUserConfigAllowed(userId))
.setLocationEnabled(isLocationEnabled(userId))
.setGeoDetectionEnabled(isGeoDetectionEnabled(userId))
.build();
@@ -186,18 +176,19 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
// time zone detection: if we wrote it down then we'd set the value explicitly, which would
// prevent detecting "default" later. That might influence what happens on later releases
// that support new types of auto detection on the same hardware.
- if (isAutoDetectionSupported()) {
+ if (mServiceConfigAccessor.isAutoDetectionFeatureSupported()) {
final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled();
setAutoDetectionEnabledIfRequired(autoDetectionEnabled);
- // Avoid writing the geo detection enabled setting for devices that do not support geo
- // time zone detection: if we wrote it down then we'd set the value explicitly, which
- // would prevent detecting "default" later. That might influence what happens on later
- // releases that support geo detection on the same hardware.
- // Also avoid writing the geo detection enabled setting for devices that are currently
- // force-enabled: otherwise we might overwrite a droidfood user's real setting
- // permanently.
- if (isGeoDetectionSupported() && !isGeoDetectionForceEnabled()) {
+ // Avoid writing the geo detection enabled setting for devices with settings that
+ // are currently overridden by server flags: otherwise we might overwrite a droidfood
+ // user's real setting permanently.
+ // Also avoid writing the geo detection enabled setting for devices that do not support
+ // geo time zone detection: if we wrote it down then we'd set the value explicitly,
+ // which would prevent detecting "default" later. That might influence what happens on
+ // later releases that start to support geo detection on the same hardware.
+ if (!mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride().isPresent()
+ && mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) {
final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled);
}
@@ -209,14 +200,6 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle);
}
- private boolean isAutoDetectionSupported() {
- return deviceHasTelephonyNetwork() || isGeoDetectionSupported();
- }
-
- private boolean isGeoDetectionSupported() {
- return mGeoDetectionSupported;
- }
-
private boolean isAutoDetectionEnabled() {
return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
}
@@ -237,24 +220,20 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
// We may never use this, but it gives us a way to force location-based time zone detection
- // on for testers (where their other settings allow).
- boolean forceEnabled = isGeoDetectionForceEnabled();
- if (forceEnabled) {
- return true;
+ // on/off for testers (but only where their other settings would allow them to turn it on
+ // for themselves).
+ Optional<Boolean> override = mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride();
+ if (override.isPresent()) {
+ return override.get();
}
- final boolean geoDetectionEnabledByDefault = mDeviceConfig.getBoolean(
- DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT, false);
+ final boolean geoDetectionEnabledByDefault =
+ mServiceConfigAccessor.isGeoDetectionEnabledForUsersByDefault();
return Settings.Secure.getIntForUser(mCr,
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
(geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0;
}
- private boolean isGeoDetectionForceEnabled() {
- return mDeviceConfig.getBoolean(
- DeviceConfig.KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED, false);
- }
-
private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) {
// See comment in setAutoDetectionEnabledIfRequired. http://b/171953500
if (isGeoDetectionEnabled(userId) != enabled) {
@@ -262,10 +241,4 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
enabled ? 1 : 0, userId);
}
}
-
- private boolean deviceHasTelephonyNetwork() {
- // TODO b/150583524 Avoid the use of a deprecated API.
- return mContext.getSystemService(ConnectivityManager.class)
- .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
- }
}
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
new file mode 100644
index 000000000000..86c32f8d7b45
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timezonedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.os.SystemProperties;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.timedetector.ServerFlags;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * A singleton that provides access to service configuration for time zone detection. This hides how
+ * configuration is split between static, compile-time config and dynamic, server-pushed flags. It
+ * provides a rudimentary mechanism to signal when values have changed.
+ */
+public final class ServiceConfigAccessor {
+
+ private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet(
+ new ArraySet<>(new String[] {
+ ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
+ ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
+ ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
+ ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+ ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+ ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+ ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+ ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS
+ }));
+
+ // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
+ private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1);
+ // TODO(b/179488561): Put this back to 1 minute when primary provider is fully implemented
+ private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
+ Duration.ofSeconds(20);
+ private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
+
+ private static final Object SLOCK = new Object();
+
+ /** The singleton instance. Initialized once in {@link #getInstance(Context)}. */
+ @GuardedBy("SLOCK")
+ @Nullable
+ private static ServiceConfigAccessor sInstance;
+
+ @NonNull private final Context mContext;
+
+ /**
+ * An ultimate "feature switch" for location-based time zone detection. If this is
+ * {@code false}, the device cannot support the feature without a config change or a reboot:
+ * This affects what services are started on boot to minimize expense when the feature is not
+ * wanted.
+ */
+ private final boolean mGeoDetectionFeatureSupportedInConfig;
+
+ @NonNull private final ServerFlags mServerFlags;
+
+ private ServiceConfigAccessor(@NonNull Context context) {
+ mContext = Objects.requireNonNull(context);
+
+ // The config value is expected to be the main feature flag. Platform developers can also
+ // force enable the feature using a persistent system property. Because system properties
+ // can change, this value is cached and only changes on reboot.
+ mGeoDetectionFeatureSupportedInConfig = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection)
+ || SystemProperties.getBoolean(
+ "persist.sys.location_time_zone_detection_feature_supported", false);
+
+ mServerFlags = ServerFlags.getInstance(mContext);
+ }
+
+ /** Returns the singleton instance. */
+ public static ServiceConfigAccessor getInstance(Context context) {
+ synchronized (SLOCK) {
+ if (sInstance == null) {
+ sInstance = new ServiceConfigAccessor(context);
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Adds a listener that will be called server flags related to this class change. The callbacks
+ * are delivered on the main looper thread.
+ *
+ * <p>Note: Only for use by long-lived objects. There is deliberately no associated remove
+ * method.
+ */
+ public void addListener(@NonNull ConfigurationChangeListener listener) {
+ mServerFlags.addListener(listener, SERVER_FLAGS_KEYS_TO_WATCH);
+ }
+
+ /** Returns {@code true} if any form of automatic time zone detection is supported. */
+ public boolean isAutoDetectionFeatureSupported() {
+ return deviceHasTelephonyNetwork() || isGeoTimeZoneDetectionFeatureSupported();
+ }
+
+ private boolean deviceHasTelephonyNetwork() {
+ // TODO b/150583524 Avoid the use of a deprecated API.
+ return mContext.getSystemService(ConnectivityManager.class)
+ .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ }
+
+ /**
+ * Returns {@code true} if the location-based time zone detection feature can be supported on
+ * this device at all according to config. When {@code false}, implies that various other
+ * location-based settings will be turned off or rendered meaningless. Typically {@link
+ * #isGeoTimeZoneDetectionFeatureSupported()} should be used instead.
+ */
+ public boolean isGeoTimeZoneDetectionFeatureSupportedInConfig() {
+ return mGeoDetectionFeatureSupportedInConfig;
+ }
+
+ /**
+ * Returns {@code true} if the location-based time zone detection feature is supported on the
+ * device. This can be used during feature testing on builds that are capable of location time
+ * zone detection to enable / disable the feature for some users.
+ */
+ public boolean isGeoTimeZoneDetectionFeatureSupported() {
+ return mGeoDetectionFeatureSupportedInConfig
+ && isGeoTimeZoneDetectionFeatureSupportedInternal();
+ }
+
+ private boolean isGeoTimeZoneDetectionFeatureSupportedInternal() {
+ final boolean defaultEnabled = true;
+ return mServerFlags.getBoolean(
+ ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
+ defaultEnabled);
+ }
+
+ /**
+ * Returns {@code true} if the primary location time zone provider can be used.
+ */
+ public boolean isPrimaryLocationTimeZoneProviderEnabled() {
+ return getPrimaryLocationTimeZoneProviderEnabledOverride()
+ .orElse(isPrimaryLocationTimeZoneProviderEnabledInConfig());
+ }
+
+ private boolean isPrimaryLocationTimeZoneProviderEnabledInConfig() {
+ int providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
+ return getConfigBoolean(providerEnabledConfigId);
+ }
+
+ @NonNull
+ private Optional<Boolean> getPrimaryLocationTimeZoneProviderEnabledOverride() {
+ return mServerFlags.getOptionalBoolean(
+ ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
+ }
+
+ /**
+ * Returns {@code true} if the secondary location time zone provider can be used.
+ */
+ public boolean isSecondaryLocationTimeZoneProviderEnabled() {
+ return getSecondaryLocationTimeZoneProviderEnabledOverride()
+ .orElse(isSecondaryLocationTimeZoneProviderEnabledInConfig());
+ }
+
+ private boolean isSecondaryLocationTimeZoneProviderEnabledInConfig() {
+ int providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
+ return getConfigBoolean(providerEnabledConfigId);
+ }
+
+ @NonNull
+ private Optional<Boolean> getSecondaryLocationTimeZoneProviderEnabledOverride() {
+ return mServerFlags.getOptionalBoolean(
+ ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
+ }
+
+ /**
+ * Returns whether location time zone detection is enabled for users when there's no setting
+ * value. Intended for use during feature release testing to "opt-in" users that haven't shown
+ * an explicit preference.
+ */
+ public boolean isGeoDetectionEnabledForUsersByDefault() {
+ return mServerFlags.getBoolean(
+ ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT, false);
+ }
+
+ /**
+ * Returns whether location time zone detection is force enabled/disabled for users. Intended
+ * for use during feature release testing to force a given state.
+ */
+ @NonNull
+ public Optional<Boolean> getGeoDetectionSettingEnabledOverride() {
+ return mServerFlags.getOptionalBoolean(
+ ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE);
+ }
+
+ /**
+ * Returns the time to send to a location time zone provider that informs it how long it has
+ * to return its first time zone suggestion.
+ */
+ @NonNull
+ public Duration getLocationTimeZoneProviderInitializationTimeout() {
+ return mServerFlags.getDurationFromMillis(
+ ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
+ DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
+ }
+
+ /**
+ * Returns the time added to {@link #getLocationTimeZoneProviderInitializationTimeout()} by the
+ * server before unilaterally declaring the provider is uncertain.
+ */
+ @NonNull
+ public Duration getLocationTimeZoneProviderInitializationTimeoutFuzz() {
+ return mServerFlags.getDurationFromMillis(
+ ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
+ DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
+ }
+
+ /**
+ * Returns the time after uncertainty is detected by providers before the location time zone
+ * manager makes a suggestion to the time zone detector.
+ */
+ @NonNull
+ public Duration getLocationTimeZoneUncertaintyDelay() {
+ return mServerFlags.getDurationFromMillis(
+ ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
+ DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
+ }
+
+ private boolean getConfigBoolean(int providerEnabledConfigId) {
+ Resources resources = mContext.getResources();
+ return resources.getBoolean(providerEnabledConfigId);
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index 203a8a4e02cc..cd220b164851 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -27,16 +27,21 @@ import android.annotation.NonNull;
*/
public interface TimeZoneDetectorInternal extends Dumpable.Container {
- /** Adds a listener that will be invoked when time zone detection configuration is changed. */
- void addConfigurationListener(ConfigurationChangeListener listener);
+ /** Adds a listener that will be invoked when {@link ConfigurationInternal} may have changed. */
+ void addConfigurationListener(@NonNull ConfigurationChangeListener listener);
/**
* Removes a listener previously added via {@link
* #addConfigurationListener(ConfigurationChangeListener)}.
*/
- void removeConfigurationListener(ConfigurationChangeListener listener);
+ void removeConfigurationListener(@NonNull ConfigurationChangeListener listener);
- /** Returns the {@link ConfigurationInternal} for the current user. */
+ /**
+ * Returns a snapshot of the {@link ConfigurationInternal} for the current user. This is only a
+ * snapshot so callers must use {@link #addConfigurationListener(ConfigurationChangeListener)}
+ * to be notified when it changes.
+ */
+ @NonNull
ConfigurationInternal getCurrentUserConfigurationInternal();
/**
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index bd71ddf67094..c20400ae7a4b 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -33,7 +33,6 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
-import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -62,27 +61,6 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
static final String TAG = "time_zone_detector";
/**
- * A "feature switch" for location-based time zone detection. If this is {@code false}. It is
- * initialized and never refreshed; it affects what services are started on boot so consistency
- * is important.
- */
- @Nullable
- private static Boolean sGeoLocationTimeZoneDetectionSupported;
-
- /** Returns {@code true} if the location-based time zone detection feature is enabled. */
- public static boolean isGeoLocationTimeZoneDetectionSupported(Context context) {
- if (sGeoLocationTimeZoneDetectionSupported == null) {
- // The config value is expected to be the main switch. Platform developers can also
- // enable the feature using a persistent system property.
- sGeoLocationTimeZoneDetectionSupported = context.getResources().getBoolean(
- com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection)
- || SystemProperties.getBoolean(
- "persist.sys.location_time_zone_detection_feature_enabled", false);
- }
- return sGeoLocationTimeZoneDetectionSupported;
- }
-
- /**
* Handles the service lifecycle for {@link TimeZoneDetectorService} and
* {@link TimeZoneDetectorInternalImpl}.
*/
@@ -98,11 +76,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
Context context = getContext();
Handler handler = FgThread.getHandler();
- boolean geolocationTimeZoneDetectionSupported =
- isGeoLocationTimeZoneDetectionSupported(context);
+ ServiceConfigAccessor serviceConfigAccessor =
+ ServiceConfigAccessor.getInstance(context);
TimeZoneDetectorStrategy timeZoneDetectorStrategy =
- TimeZoneDetectorStrategyImpl.create(
- context, handler, geolocationTimeZoneDetectionSupported);
+ TimeZoneDetectorStrategyImpl.create(context, handler, serviceConfigAccessor);
// Create and publish the local service for use by internal callers.
TimeZoneDetectorInternal internal =
@@ -330,7 +307,8 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
boolean isGeoTimeZoneDetectionSupported() {
enforceManageTimeZoneDetectorPermission();
- return isGeoLocationTimeZoneDetectionSupported(mContext);
+ return ServiceConfigAccessor.getInstance(mContext)
+ .isGeoTimeZoneDetectionFeatureSupported();
}
@Override
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 0b1d6d71ea7b..8266f121822e 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -75,17 +75,21 @@ import android.util.IndentingPrintWriter;
public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container {
/**
- * Sets a listener that will be triggered whenever time zone detection configuration is
+ * Adds a listener that will be triggered whenever {@link ConfigurationInternal} may have
* changed.
*/
void addConfigChangeListener(@NonNull ConfigurationChangeListener listener);
- /** Returns the user's time zone configuration. */
+ /**
+ * Returns a snapshot of the configuration that controls time zone detector behavior for the
+ * specified user.
+ */
@NonNull
ConfigurationInternal getConfigurationInternal(@UserIdInt int userId);
/**
- * Returns the configuration that controls time zone detector behavior for the current user.
+ * Returns a snapshot of the configuration that controls time zone detector behavior for the
+ * current user.
*/
@NonNull
ConfigurationInternal getCurrentUserConfigurationInternal();
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index c464b74ca726..d163a0e22320 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -38,7 +38,6 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.timedetector.DeviceConfig;
import java.util.ArrayList;
import java.util.List;
@@ -204,11 +203,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
*/
public static TimeZoneDetectorStrategyImpl create(
@NonNull Context context, @NonNull Handler handler,
- boolean geoDetectionSupported) {
+ @NonNull ServiceConfigAccessor serviceConfigAccessor) {
- DeviceConfig deviceConfig = new DeviceConfig();
- EnvironmentImpl environment = new EnvironmentImpl(
- context, handler, deviceConfig, geoDetectionSupported);
+ Environment environment = new EnvironmentImpl(context, handler, serviceConfigAccessor);
return new TimeZoneDetectorStrategyImpl(environment);
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
index e463ee22452d..98e984d2c3ac 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
@@ -19,9 +19,9 @@ package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
import com.android.server.LocalServices;
-import com.android.server.timedetector.DeviceConfig;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.ServiceConfigAccessor;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
import java.time.Duration;
@@ -33,28 +33,19 @@ import java.util.Objects;
*/
class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
- // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
- private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1);
- // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
- private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
- Duration.ofSeconds(20);
- private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
-
@NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
- @NonNull private final LocationTimeZoneProviderController mController;
- @NonNull private final DeviceConfig mDeviceConfig;
+ @NonNull private final ServiceConfigAccessor mServiceConfigAccessor;
@NonNull private final ConfigurationChangeListener mConfigurationChangeListener;
ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
- @NonNull DeviceConfig deviceConfig,
+ @NonNull ServiceConfigAccessor serviceConfigAccessor,
@NonNull LocationTimeZoneProviderController controller) {
super(threadingDomain);
- mController = Objects.requireNonNull(controller);
- mDeviceConfig = Objects.requireNonNull(deviceConfig);
+ mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);
// Listen for configuration changes.
- mConfigurationChangeListener = () -> mThreadingDomain.post(mController::onConfigChanged);
+ mConfigurationChangeListener = () -> mThreadingDomain.post(controller::onConfigChanged);
mTimeZoneDetectorInternal.addConfigurationListener(mConfigurationChangeListener);
}
@@ -73,24 +64,18 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir
@Override
@NonNull
Duration getProviderInitializationTimeout() {
- return mDeviceConfig.getDurationFromMillis(
- DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
- DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
+ return mServiceConfigAccessor.getLocationTimeZoneProviderInitializationTimeout();
}
@Override
@NonNull
Duration getProviderInitializationTimeoutFuzz() {
- return mDeviceConfig.getDurationFromMillis(
- DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
- DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
+ return mServiceConfigAccessor.getLocationTimeZoneProviderInitializationTimeoutFuzz();
}
@Override
@NonNull
Duration getUncertaintyDelay() {
- return mDeviceConfig.getDurationFromMillis(
- DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
- DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
+ return mServiceConfigAccessor.getLocationTimeZoneUncertaintyDelay();
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 364eaf8dac04..0d1692a8781d 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -21,11 +21,11 @@ import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DI
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
+import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Resources;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -43,9 +43,8 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.SystemService;
-import com.android.server.timedetector.DeviceConfig;
+import com.android.server.timezonedetector.ServiceConfigAccessor;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
-import com.android.server.timezonedetector.TimeZoneDetectorService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -96,28 +95,31 @@ public class LocationTimeZoneManagerService extends Binder {
private LocationTimeZoneManagerService mService;
+ @NonNull
+ private final ServiceConfigAccessor mServerConfigAccessor;
+
public Lifecycle(@NonNull Context context) {
super(Objects.requireNonNull(context));
+ mServerConfigAccessor = ServiceConfigAccessor.getInstance(context);
}
@Override
public void onStart() {
Context context = getContext();
- if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
+ if (mServerConfigAccessor.isGeoTimeZoneDetectionFeatureSupportedInConfig()) {
mService = new LocationTimeZoneManagerService(context);
// The service currently exposes no LocalService or Binder API, but it extends
// Binder and is registered as a binder service so it can receive shell commands.
- publishBinderService("location_time_zone_manager", mService);
+ publishBinderService(SERVICE_NAME, mService);
} else {
- Slog.i(TAG, getClass() + " is disabled");
+ Slog.d(TAG, "Geo time zone detection feature is disabled in config");
}
}
@Override
- public void onBootPhase(int phase) {
- Context context = getContext();
- if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
+ public void onBootPhase(@BootPhase int phase) {
+ if (mServerConfigAccessor.isGeoTimeZoneDetectionFeatureSupportedInConfig()) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
// The location service must be functioning after this boot phase.
mService.onSystemReady();
@@ -159,6 +161,9 @@ public class LocationTimeZoneManagerService extends Binder {
/** The shared lock from {@link #mThreadingDomain}. */
@NonNull private final Object mSharedLock;
+ @NonNull
+ private final ServiceConfigAccessor mServiceConfigAccessor;
+
// Lazily initialized. Can be null if the service has been stopped.
@GuardedBy("mSharedLock")
private ControllerImpl mLocationTimeZoneDetectorController;
@@ -180,24 +185,38 @@ public class LocationTimeZoneManagerService extends Binder {
mHandler = FgThread.getHandler();
mThreadingDomain = new HandlerThreadingDomain(mHandler);
mSharedLock = mThreadingDomain.getLockObject();
+ mServiceConfigAccessor = ServiceConfigAccessor.getInstance(mContext);
}
+ // According to the SystemService docs: All lifecycle methods are called from the system
+ // server's main looper thread.
void onSystemReady() {
- // Called on an arbitrary thread during initialization.
- synchronized (mSharedLock) {
- // TODO(b/152744911): LocationManagerService watches for packages disappearing. Need to
- // do anything here?
+ mServiceConfigAccessor.addListener(this::handleServiceConfigurationChangedOnMainThread);
+ }
- // TODO(b/152744911): LocationManagerService watches for foreground app changes. Need to
- // do anything here?
- // TODO(b/152744911): LocationManagerService watches screen state. Need to do anything
- // here?
+ private void handleServiceConfigurationChangedOnMainThread() {
+ // This method is called on the main thread, but service logic takes place on the threading
+ // domain thread, so we post the work there.
+
+ // The way all service-level configuration changes are handled is to just restart this
+ // service - this is simple and effective, and service configuration changes should be rare.
+ mThreadingDomain.post(this::restartIfRequiredOnDomainThread);
+ }
+
+ private void restartIfRequiredOnDomainThread() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ // Stop and start the service, waiting until completion.
+ stopOnDomainThread();
+ startOnDomainThread();
}
}
+ // According to the SystemService docs: All lifecycle methods are called from the system
+ // server's main looper thread.
void onSystemThirdPartyAppsCanStart() {
- // Called on an arbitrary thread during initialization. We do not want to wait for
- // completion as it would delay boot.
+ // Do not wait for completion as it would delay boot.
final boolean waitForCompletion = false;
startInternal(waitForCompletion);
}
@@ -205,6 +224,9 @@ public class LocationTimeZoneManagerService extends Binder {
/**
* Starts the service during server initialization or during tests after a call to
* {@link #stop()}.
+ *
+ * <p>Because this method posts work to the {@code mThreadingDomain} thread and waits for
+ * completion, it cannot be called from the {@code mThreadingDomain} thread.
*/
void start() {
enforceManageTimeZoneDetectorPermission();
@@ -214,28 +236,17 @@ public class LocationTimeZoneManagerService extends Binder {
}
/**
- * Starts the service during server initialization or during tests after a call to
- * {@link #stop()}.
+ * Starts the service during server initialization, if the configuration changes or during tests
+ * after a call to {@link #stop()}.
*
* <p>To avoid tests needing to sleep, when {@code waitForCompletion} is {@code true}, this
* method will not return until all the system server components have started.
+ *
+ * <p>Because this method posts work to the {@code mThreadingDomain} thread, it cannot be
+ * called from the {@code mThreadingDomain} thread when {@code waitForCompletion} is true.
*/
private void startInternal(boolean waitForCompletion) {
- Runnable runnable = () -> {
- synchronized (mSharedLock) {
- if (mLocationTimeZoneDetectorController == null) {
- LocationTimeZoneProvider primary = createPrimaryProvider();
- LocationTimeZoneProvider secondary = createSecondaryProvider();
- mLocationTimeZoneDetectorController =
- new ControllerImpl(mThreadingDomain, primary, secondary);
- DeviceConfig deviceConfig = new DeviceConfig();
- mEnvironment = new ControllerEnvironmentImpl(
- mThreadingDomain, deviceConfig, mLocationTimeZoneDetectorController);
- ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
- mLocationTimeZoneDetectorController.initialize(mEnvironment, callback);
- }
- }
- };
+ Runnable runnable = this::startOnDomainThread;
if (waitForCompletion) {
mThreadingDomain.postAndWait(runnable, BLOCKING_OP_WAIT_DURATION_MILLIS);
} else {
@@ -243,11 +254,38 @@ public class LocationTimeZoneManagerService extends Binder {
}
}
+ private void startOnDomainThread() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ if (!mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) {
+ debugLog("Not starting " + SERVICE_NAME + ": it is disabled in service config");
+ return;
+ }
+
+ if (mLocationTimeZoneDetectorController == null) {
+ LocationTimeZoneProvider primary = createPrimaryProvider();
+ LocationTimeZoneProvider secondary = createSecondaryProvider();
+
+ ControllerImpl controller =
+ new ControllerImpl(mThreadingDomain, primary, secondary);
+ ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl(
+ mThreadingDomain, mServiceConfigAccessor, controller);
+ ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
+ controller.initialize(environment, callback);
+
+ mEnvironment = environment;
+ mLocationTimeZoneDetectorController = controller;
+ }
+ }
+ }
+
+ @NonNull
private LocationTimeZoneProvider createPrimaryProvider() {
LocationTimeZoneProviderProxy proxy;
if (isProviderInSimulationMode(PRIMARY_PROVIDER_NAME)) {
proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
- } else if (isProviderDisabled(PRIMARY_PROVIDER_NAME)) {
+ } else if (!isProviderEnabled(PRIMARY_PROVIDER_NAME)) {
proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
} else {
proxy = new RealLocationTimeZoneProviderProxy(
@@ -262,11 +300,12 @@ public class LocationTimeZoneManagerService extends Binder {
return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
}
+ @NonNull
private LocationTimeZoneProvider createSecondaryProvider() {
LocationTimeZoneProviderProxy proxy;
if (isProviderInSimulationMode(SECONDARY_PROVIDER_NAME)) {
proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
- } else if (isProviderDisabled(SECONDARY_PROVIDER_NAME)) {
+ } else if (!isProviderEnabled(SECONDARY_PROVIDER_NAME)) {
proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
} else {
proxy = new RealLocationTimeZoneProviderProxy(
@@ -282,33 +321,27 @@ public class LocationTimeZoneManagerService extends Binder {
}
/** Used for bug triage and in tests to simulate provider events. */
- private boolean isProviderInSimulationMode(String providerName) {
+ private boolean isProviderInSimulationMode(@NonNull String providerName) {
return isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_SIMULATED);
}
- /** Used for bug triage, tests and experiments to remove a provider. */
- private boolean isProviderDisabled(String providerName) {
- return !isProviderEnabledInConfig(providerName)
- || isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED);
- }
+ /** Used for bug triage, and by tests and experiments to remove a provider. */
+ private boolean isProviderEnabled(@NonNull String providerName) {
+ if (isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED)) {
+ return false;
+ }
- private boolean isProviderEnabledInConfig(String providerName) {
- int providerEnabledConfigId;
switch (providerName) {
case PRIMARY_PROVIDER_NAME: {
- providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
- break;
+ return mServiceConfigAccessor.isPrimaryLocationTimeZoneProviderEnabled();
}
case SECONDARY_PROVIDER_NAME: {
- providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
- break;
+ return mServiceConfigAccessor.isSecondaryLocationTimeZoneProviderEnabled();
}
default: {
throw new IllegalArgumentException(providerName);
}
}
- Resources resources = mContext.getResources();
- return resources.getBoolean(providerEnabledConfigId);
}
private boolean isProviderModeOverrideSet(@NonNull String providerName, @NonNull String mode) {
@@ -326,22 +359,29 @@ public class LocationTimeZoneManagerService extends Binder {
}
/**
- * Stops the service for tests. To avoid tests needing to sleep, this method will not return
- * until all the system server components have stopped.
+ * Stops the service for tests and other rare cases. To avoid tests needing to sleep, this
+ * method will not return until all the system server components have stopped.
+ *
+ * <p>Because this method posts work to the {@code mThreadingDomain} thread and waits it cannot
+ * be called from the {@code mThreadingDomain} thread.
*/
void stop() {
enforceManageTimeZoneDetectorPermission();
- mThreadingDomain.postAndWait(() -> {
- synchronized (mSharedLock) {
- if (mLocationTimeZoneDetectorController != null) {
- mLocationTimeZoneDetectorController.destroy();
- mLocationTimeZoneDetectorController = null;
- mEnvironment.destroy();
- mEnvironment = null;
- }
+ mThreadingDomain.postAndWait(this::stopOnDomainThread, BLOCKING_OP_WAIT_DURATION_MILLIS);
+ }
+
+ private void stopOnDomainThread() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ if (mLocationTimeZoneDetectorController != null) {
+ mLocationTimeZoneDetectorController.destroy();
+ mLocationTimeZoneDetectorController = null;
+ mEnvironment.destroy();
+ mEnvironment = null;
}
- }, BLOCKING_OP_WAIT_DURATION_MILLIS);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
index b53150c729bc..bdf4a70a6a2b 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
@@ -21,6 +21,7 @@ import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DI
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
+import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_DUMP_STATE;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_RECORD_PROVIDER_STATES;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND;
@@ -102,7 +103,7 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
- pw.println("Location Time Zone Manager (location_time_zone_manager) commands for tests:");
+ pw.printf("Location Time Zone Manager (%s) commands for tests:\n", SERVICE_NAME);
pw.println(" help");
pw.println(" Print this help text.");
pw.printf(" %s\n", SHELL_COMMAND_START);
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
index 38211efc1c63..531c62c5b4e9 100644
--- a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
@@ -25,7 +25,6 @@ import static com.android.server.timezonedetector.location.LocationTimeZoneManag
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -41,6 +40,7 @@ import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.server.ServiceWatcher;
+import com.android.server.ServiceWatcher.BoundService;
import java.util.Objects;
import java.util.function.Predicate;
@@ -123,7 +123,7 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
return resolves;
}
- private void onBind(IBinder binder, ComponentName componentName) {
+ private void onBind(IBinder binder, BoundService boundService) {
mThreadingDomain.assertCurrentThread();
synchronized (mSharedLock) {
diff --git a/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
index 14820319d9df..e8386bc22403 100644
--- a/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
+++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
@@ -63,8 +63,6 @@ final class TimeZoneProviderRequest {
return mSendUpdates;
}
- // TODO(b/152744911) - once there are a couple of implementations, decide whether this needs to
- // be passed to the TimeZoneProviderService and remove if it is not useful.
/**
* Returns the maximum time that the provider is allowed to initialize before it is expected to
* send an event of any sort. Only valid when {@link #sendUpdates()} is {@code true}. Failure to
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 71d2b2f7b882..cca85b217a74 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2393,15 +2393,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- // We may be deferring layout passes at the moment, but since the client is interested
- // in the new out values right now we need to force a layout.
- mWindowPlacerLocked.performSurfacePlacement(true /* force */);
-
+ // Create surfaceControl before surface placement otherwise layout will be skipped
+ // (because WS.isGoneForLayout() is true when there is no surface.
if (shouldRelayout) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
-
- result = win.relayoutVisibleWindow(result);
-
try {
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
} catch (Exception e) {
@@ -2413,6 +2407,17 @@ public class WindowManagerService extends IWindowManager.Stub
Binder.restoreCallingIdentity(origId);
return 0;
}
+ }
+
+ // We may be deferring layout passes at the moment, but since the client is interested
+ // in the new out values right now we need to force a layout.
+ mWindowPlacerLocked.performSurfacePlacement(true /* force */);
+
+ if (shouldRelayout) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
+
+ result = win.relayoutVisibleWindow(result);
+
if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
focusMayChange = true;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6bbd320017a7..a3d335340e9f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -162,6 +162,7 @@ import com.android.server.pm.ShortcutService;
import com.android.server.pm.UserManagerService;
import com.android.server.pm.dex.SystemServerDexLoadReporter;
import com.android.server.pm.verify.domain.DomainVerificationService;
+import com.android.server.policy.AppOpsPolicy;
import com.android.server.policy.PermissionPolicyService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.policy.role.RoleServicePlatformHelperImpl;
@@ -2656,6 +2657,14 @@ public final class SystemServer implements Dumpable {
}
t.traceEnd();
+ t.traceBegin("RegisterAppOpsPolicy");
+ try {
+ mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy());
+ } catch (Throwable e) {
+ reportWtf("registering app ops policy", e);
+ }
+ t.traceEnd();
+
// No dependency on Webview preparation in system server. But this should
// be completed before allowing 3rd party
final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation";
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 68d6557880c4..c4c9ad088e45 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -352,7 +352,8 @@ public class LocationProviderManagerTest {
@Test
public void testGetLastLocation_ClearOnMockRemoval() {
- MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, PROVIDER_IDENTITY);
+ MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, PROVIDER_IDENTITY,
+ null);
mockProvider.setAllowed(true);
mManager.setMockProvider(mockProvider);
@@ -1048,7 +1049,7 @@ public class LocationProviderManagerTest {
private final ArrayList<Runnable> mFlushCallbacks = new ArrayList<>();
TestProvider(ProviderProperties properties, CallerIdentity identity) {
- super(DIRECT_EXECUTOR, identity, properties);
+ super(DIRECT_EXECUTOR, identity, properties, null);
}
public void setProviderAllowed(boolean allowed) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
index 07170dacb4da..e8a0bb51e20f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
@@ -71,7 +71,8 @@ public class MockableLocationProviderTest {
.setPowerUsage(POWER_USAGE_LOW)
.setAccuracy(ACCURACY_FINE)
.build(),
- CallerIdentity.forTest(0, 1, "testpackage", "test"));
+ CallerIdentity.forTest(0, 1, "testpackage", "test"),
+ null);
mProvider = new MockableLocationProvider(lock);
mProvider.getController().setListener(mListener);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
index 775bdd580157..a1eadbe4a64f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
@@ -40,7 +40,7 @@ public class FakeProvider extends AbstractLocationProvider {
private final FakeProviderInterface mFakeInterface;
public FakeProvider(FakeProviderInterface fakeInterface) {
- super(Runnable::run, null, null);
+ super(Runnable::run, null, null, null);
mFakeInterface = fakeInterface;
}
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index ddbe81c81d6d..26b34fdd4e04 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -318,7 +318,8 @@ public class PowerStatsServiceTest {
assertTrue(pssProto.energyMeasurement.length == ENERGY_METER_COUNT);
for (int i = 0; i < pssProto.energyMeasurement.length; i++) {
assertTrue(pssProto.energyMeasurement[i].id == i);
- assertTrue(pssProto.energyMeasurement[i].timestampMs == i);
+ assertTrue(pssProto.energyMeasurement[i].timestampMs ==
+ i + mPowerStatsLogger.getStartWallTime());
assertTrue(pssProto.energyMeasurement[i].durationMs == i);
assertTrue(pssProto.energyMeasurement[i].energyUws == i);
}
@@ -359,7 +360,8 @@ public class PowerStatsServiceTest {
assertTrue(pssProto.energyConsumerResult.length == ENERGY_CONSUMER_COUNT);
for (int i = 0; i < pssProto.energyConsumerResult.length; i++) {
assertTrue(pssProto.energyConsumerResult[i].id == i);
- assertTrue(pssProto.energyConsumerResult[i].timestampMs == i);
+ assertTrue(pssProto.energyConsumerResult[i].timestampMs ==
+ i + mPowerStatsLogger.getStartWallTime());
assertTrue(pssProto.energyConsumerResult[i].energyUws == i);
assertTrue(pssProto.energyConsumerResult[i].attribution.length
== ENERGY_CONSUMER_ATTRIBUTION_COUNT);
@@ -420,7 +422,8 @@ public class PowerStatsServiceTest {
assertTrue(stateResidency.id == j);
assertTrue(stateResidency.totalTimeInStateMs == j);
assertTrue(stateResidency.totalStateEntryCount == j);
- assertTrue(stateResidency.lastEntryTimestampMs == j);
+ assertTrue(stateResidency.lastEntryTimestampMs ==
+ j + mPowerStatsLogger.getStartWallTime());
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 682a80c9b297..5d2755221288 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -46,8 +46,8 @@ public class ConfigurationInternalTest {
public void test_unrestricted() {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionSupported(true)
- .setGeoDetectionSupported(true)
+ .setAutoDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(true)
@@ -108,8 +108,8 @@ public class ConfigurationInternalTest {
public void test_restricted() {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(false)
- .setAutoDetectionSupported(true)
- .setGeoDetectionSupported(true)
+ .setAutoDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(true)
@@ -170,8 +170,8 @@ public class ConfigurationInternalTest {
public void test_autoDetectNotSupported() {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionSupported(false)
- .setGeoDetectionSupported(false)
+ .setAutoDetectionFeatureSupported(false)
+ .setGeoDetectionFeatureSupported(false)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(true)
@@ -232,8 +232,8 @@ public class ConfigurationInternalTest {
public void test_geoDetectNotSupported() {
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionSupported(true)
- .setGeoDetectionSupported(false)
+ .setAutoDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(false)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(true)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index d2452eaee657..14e0bbd6fa42 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -364,8 +364,8 @@ public class TimeZoneDetectorServiceTest {
// the tests.
final boolean geoDetectionEnabled = autoDetectionEnabled;
return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
- .setAutoDetectionSupported(true)
- .setGeoDetectionSupported(true)
+ .setAutoDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(true)
.setUserConfigAllowed(true)
.setAutoDetectionEnabled(autoDetectionEnabled)
.setLocationEnabled(geoDetectionEnabled)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index c8dba5f40882..f1f8b2f5e81a 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -90,8 +90,8 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(false)
- .setAutoDetectionSupported(true)
- .setGeoDetectionSupported(true)
+ .setAutoDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(false)
.setLocationEnabled(true)
.setGeoDetectionEnabled(false)
@@ -100,8 +100,8 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(false)
- .setAutoDetectionSupported(true)
- .setGeoDetectionSupported(true)
+ .setAutoDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(true)
@@ -110,8 +110,8 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionSupported(false)
- .setGeoDetectionSupported(false)
+ .setAutoDetectionFeatureSupported(false)
+ .setGeoDetectionFeatureSupported(false)
.setAutoDetectionEnabled(false)
.setLocationEnabled(true)
.setGeoDetectionEnabled(false)
@@ -120,8 +120,8 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionSupported(true)
- .setGeoDetectionSupported(false)
+ .setAutoDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(false)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(true)
@@ -130,8 +130,8 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(true)
- .setAutoDetectionSupported(true)
- .setGeoDetectionSupported(true)
+ .setAutoDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(false)
.setLocationEnabled(true)
.setGeoDetectionEnabled(false)
@@ -139,8 +139,8 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
- .setAutoDetectionSupported(true)
- .setGeoDetectionSupported(true)
+ .setAutoDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(true)
.setUserConfigAllowed(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
@@ -149,8 +149,8 @@ public class TimeZoneDetectorStrategyImplTest {
private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED =
new ConfigurationInternal.Builder(USER_ID)
- .setAutoDetectionSupported(true)
- .setGeoDetectionSupported(true)
+ .setAutoDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(true)
.setUserConfigAllowed(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
index d319488ba73b..8280cdcb18c4 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
@@ -44,8 +44,8 @@ final class TestSupport {
@UserIdInt int userId, boolean geoDetectionEnabled) {
return new ConfigurationInternal.Builder(userId)
.setUserConfigAllowed(true)
- .setAutoDetectionSupported(true)
- .setGeoDetectionSupported(true)
+ .setAutoDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(geoDetectionEnabled)