summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt22
-rwxr-xr-xapi/system-current.txt40
-rw-r--r--core/java/android/app/SystemServiceRegistry.java11
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java36
-rw-r--r--core/java/android/content/pm/ShortcutServiceInternal.java8
-rw-r--r--core/java/android/hardware/display/DeviceProductInfo.java224
-rw-r--r--core/java/android/os/IThermalService.aidl7
-rw-r--r--core/java/android/os/PowerManager.java160
-rw-r--r--core/java/android/provider/Settings.java29
-rw-r--r--core/java/android/view/DisplayInfo.java14
-rw-r--r--core/java/android/view/SurfaceControl.java5
-rw-r--r--core/java/android/view/ViewGroup.java25
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java205
-rw-r--r--core/java/android/widget/TextView.java43
-rw-r--r--core/jni/android_view_SurfaceControl.cpp84
-rw-r--r--core/tests/coretests/src/android/os/PowerManagerTest.java22
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSDetail.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java100
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java4
-rw-r--r--services/Android.bp10
-rw-r--r--services/api/lint-baseline.txt35
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java16
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java11
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java14
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java1
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java73
-rw-r--r--services/core/java/com/android/server/location/LocationFudger.java7
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java7
-rw-r--r--services/core/java/com/android/server/power/ThermalManagerService.java247
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java21
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java22
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java31
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java13
-rw-r--r--services/people/java/com/android/server/people/PeopleService.java2
-rw-r--r--services/people/java/com/android/server/people/SessionInfo.java6
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java26
-rw-r--r--services/people/java/com/android/server/people/prediction/AppTargetPredictor.java12
-rw-r--r--services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java116
-rw-r--r--services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java5
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java109
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl2
-rw-r--r--wifi/java/android/net/wifi/SoftApConfiguration.java71
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java20
-rwxr-xr-xwifi/java/android/net/wifi/WifiOemMigrationHook.java266
-rw-r--r--wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java2
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java11
61 files changed, 2121 insertions, 275 deletions
diff --git a/api/current.txt b/api/current.txt
index 15aab8243f21..a0b38cd52ced 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36633,6 +36633,7 @@ package android.os {
method public void addThermalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.PowerManager.OnThermalStatusChangedListener);
method public int getCurrentThermalStatus();
method public int getLocationPowerSaveMode();
+ method public float getThermalHeadroom(@IntRange(from=0, to=60) int);
method public boolean isDeviceIdleMode();
method public boolean isIgnoringBatteryOptimizations(String);
method public boolean isInteractive();
@@ -40585,13 +40586,13 @@ package android.provider {
field public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
field public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms";
field @Deprecated public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on";
- field public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
- field public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
+ field @Deprecated public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
+ field @Deprecated public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
field public static final String WIFI_ON = "wifi_on";
- field public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
- field public static final int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
- field public static final int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
- field public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
+ field @Deprecated public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
+ field @Deprecated public static final int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
+ field @Deprecated public static final int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
+ field @Deprecated public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
field public static final String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
field public static final String WINDOW_ANIMATION_SCALE = "window_animation_scale";
}
@@ -55605,6 +55606,7 @@ package android.view.accessibility {
method public CharSequence getContentDescription();
method public int getDrawingOrder();
method public CharSequence getError();
+ method @Nullable public android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo();
method public android.os.Bundle getExtras();
method public CharSequence getHintText();
method public int getInputType();
@@ -55757,6 +55759,7 @@ package android.view.accessibility {
field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
+ field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
@@ -55844,6 +55847,12 @@ package android.view.accessibility {
method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
}
+ public static final class AccessibilityNodeInfo.ExtraRenderingInfo {
+ method @Nullable public android.util.Size getLayoutParams();
+ method public float getTextSizeInPx();
+ method public int getTextSizeUnit();
+ }
+
public static final class AccessibilityNodeInfo.RangeInfo {
ctor public AccessibilityNodeInfo.RangeInfo(int, float, float, float);
method public float getCurrent();
@@ -60826,6 +60835,7 @@ package android.widget {
method @Nullable public android.graphics.drawable.Drawable getTextSelectHandleLeft();
method @Nullable public android.graphics.drawable.Drawable getTextSelectHandleRight();
method @android.view.ViewDebug.ExportedProperty(category="text") public float getTextSize();
+ method public int getTextSizeUnit();
method public int getTotalPaddingBottom();
method public int getTotalPaddingEnd();
method public int getTotalPaddingLeft();
diff --git a/api/system-current.txt b/api/system-current.txt
index f974380f72c3..a7dbf38764e5 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7536,6 +7536,7 @@ package android.net.wifi {
method public int getChannel();
method public int getMaxNumberOfClients();
method public int getShutdownTimeoutMillis();
+ method public boolean isAutoShutdownEnabled();
method public boolean isClientControlByUserEnabled();
method @Nullable public android.net.wifi.WifiConfiguration toWifiConfiguration();
field public static final int BAND_2GHZ = 1; // 0x1
@@ -7549,14 +7550,15 @@ package android.net.wifi {
ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
method @NonNull public android.net.wifi.SoftApConfiguration build();
method @NonNull public android.net.wifi.SoftApConfiguration.Builder enableClientControlByUser(boolean);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAutoShutdownEnabled(boolean);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientList(@NonNull java.util.List<android.net.MacAddress>, @NonNull java.util.List<android.net.MacAddress>);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
- method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(@IntRange(from=0) int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
- method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(@IntRange(from=0) int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
}
@@ -7750,6 +7752,7 @@ package android.net.wifi {
method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanAlwaysAvailable(boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanThrottleEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setVerboseLoggingEnabled(boolean);
@@ -7897,6 +7900,7 @@ package android.net.wifi {
public final class WifiOemMigrationHook {
method @Nullable public static android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData loadFromConfigStore();
+ method @NonNull public static android.net.wifi.WifiOemMigrationHook.SettingsMigrationData loadFromSettings(@NonNull android.content.Context);
}
public static final class WifiOemMigrationHook.ConfigStoreMigrationData implements android.os.Parcelable {
@@ -7914,6 +7918,31 @@ package android.net.wifi {
method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
}
+ public static final class WifiOemMigrationHook.SettingsMigrationData implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getP2pDeviceName();
+ method public boolean isP2pFactoryResetPending();
+ method public boolean isScanAlwaysAvailable();
+ method public boolean isScanThrottleEnabled();
+ method public boolean isSoftApTimeoutEnabled();
+ method public boolean isVerboseLoggingEnabled();
+ method public boolean isWakeUpEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemMigrationHook.SettingsMigrationData> CREATOR;
+ }
+
+ public static final class WifiOemMigrationHook.SettingsMigrationData.Builder {
+ ctor public WifiOemMigrationHook.SettingsMigrationData.Builder();
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData build();
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pDeviceName(@Nullable String);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pFactoryResetPending(boolean);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanAlwaysAvailable(boolean);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanThrottleEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setSoftApTimeoutEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setVerboseLoggingEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setWakeUpEnabled(boolean);
+ }
+
public class WifiScanner {
method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
@@ -9720,18 +9749,11 @@ package android.provider {
field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
field public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
- field public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
field public static final String TETHER_SUPPORTED = "tether_supported";
field public static final String THEATER_MODE_ON = "theater_mode_on";
field public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
field public static final String WIFI_BADGING_THRESHOLDS = "wifi_badging_thresholds";
- field public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
- field public static final String WIFI_P2P_PENDING_FACTORY_RESET = "wifi_p2p_pending_factory_reset";
- field public static final String WIFI_SCAN_ALWAYS_AVAILABLE = "wifi_scan_always_enabled";
- field public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
- field public static final String WIFI_SCORE_PARAMS = "wifi_score_params";
- field public static final String WIFI_VERBOSE_LOGGING_ENABLED = "wifi_verbose_logging_enabled";
field @Deprecated public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 655dd9b41c34..f1559f79996e 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -148,6 +148,7 @@ import android.os.IHardwarePropertiesManager;
import android.os.IPowerManager;
import android.os.IRecoverySystem;
import android.os.ISystemUpdateManager;
+import android.os.IThermalService;
import android.os.IUserManager;
import android.os.IncidentManager;
import android.os.PowerManager;
@@ -576,10 +577,12 @@ public final class SystemServiceRegistry {
new CachedServiceFetcher<PowerManager>() {
@Override
public PowerManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.POWER_SERVICE);
- IPowerManager service = IPowerManager.Stub.asInterface(b);
- return new PowerManager(ctx.getOuterContext(),
- service, ctx.mMainThread.getHandler());
+ IBinder powerBinder = ServiceManager.getServiceOrThrow(Context.POWER_SERVICE);
+ IPowerManager powerService = IPowerManager.Stub.asInterface(powerBinder);
+ IBinder thermalBinder = ServiceManager.getServiceOrThrow(Context.THERMAL_SERVICE);
+ IThermalService thermalService = IThermalService.Stub.asInterface(thermalBinder);
+ return new PowerManager(ctx.getOuterContext(), powerService, thermalService,
+ ctx.mMainThread.getHandler());
}});
registerService(Context.RECOVERY_SERVICE, RecoverySystem.class,
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 5b60b85f4721..3e1a480b4f66 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -25,6 +25,7 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.app.PropertyInvalidatedCache;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Handler;
@@ -1299,6 +1300,31 @@ public final class BluetoothDevice implements Parcelable {
return false;
}
+ private static final String BLUETOOTH_BONDING_CACHE_PROPERTY =
+ "cache_key.bluetooth.get_bond_state";
+ private final PropertyInvalidatedCache<BluetoothDevice, Integer> mBluetoothBondCache =
+ new PropertyInvalidatedCache<BluetoothDevice, Integer>(
+ 8, BLUETOOTH_BONDING_CACHE_PROPERTY) {
+ @Override
+ protected Integer recompute(BluetoothDevice query) {
+ try {
+ return sService.getBondState(query);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ };
+
+ /** @hide */
+ public void disableBluetoothGetBondStateCache() {
+ mBluetoothBondCache.disableLocal();
+ }
+
+ /** @hide */
+ public static void invalidateBluetoothGetBondStateCache() {
+ PropertyInvalidatedCache.invalidateCache(BLUETOOTH_BONDING_CACHE_PROPERTY);
+ }
+
/**
* Get the bond state of the remote device.
* <p>Possible values for the bond state are:
@@ -1316,9 +1342,13 @@ public final class BluetoothDevice implements Parcelable {
return BOND_NONE;
}
try {
- return service.getBondState(this);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
+ return mBluetoothBondCache.query(this);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof RemoteException) {
+ Log.e(TAG, "", e);
+ } else {
+ throw e;
+ }
}
return BOND_NONE;
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index a69905eb3de4..3a934211c7e5 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -22,6 +22,7 @@ import android.annotation.UserIdInt;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.LocusId;
import android.content.pm.LauncherApps.ShortcutQuery;
@@ -92,4 +93,11 @@ public abstract class ShortcutServiceInternal {
public abstract void uncacheShortcuts(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@NonNull List<String> shortcutIds, int userId);
+
+ /**
+ * Retrieves all of the direct share targets that match the given IntentFilter for the specified
+ * user.
+ */
+ public abstract List<ShortcutManager.ShareShortcutInfo> getShareTargets(
+ @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId);
}
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
new file mode 100644
index 000000000000..6ad7faed16d8
--- /dev/null
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Product-specific information about the display or the directly connected device on the
+ * display chain. For example, if the display is transitively connected, this field may contain
+ * product information about the intermediate device.
+ * @hide
+ */
+public final class DeviceProductInfo implements Parcelable {
+ final private String mName;
+ final private String mManufacturerPnpId;
+ final private String mProductId;
+ final private Integer mModelYear;
+ final private ManufactureDate mManufactureDate;
+
+ public DeviceProductInfo(
+ String name,
+ String manufacturerPnpId,
+ String productCode,
+ Integer modelYear,
+ ManufactureDate manufactureDate) {
+ this.mName = name;
+ this.mManufacturerPnpId = manufacturerPnpId;
+ this.mProductId = productCode;
+ this.mModelYear = modelYear;
+ this.mManufactureDate = manufactureDate;
+ }
+
+ private DeviceProductInfo(Parcel in) {
+ mName = in.readString();
+ mManufacturerPnpId = in.readString();
+ mProductId = (String) in.readValue(null);
+ mModelYear = (Integer) in.readValue(null);
+ mManufactureDate = (ManufactureDate) in.readValue(null);
+ }
+
+ /**
+ * @return Display name.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * @return Manufacturer Plug and Play ID.
+ */
+ public String getManufacturerPnpId() {
+ return mManufacturerPnpId;
+ }
+
+ /**
+ * @return Manufacturer product ID.
+ */
+ public String getProductId() {
+ return mProductId;
+ }
+
+ /**
+ * @return Model year of the device. Typically exactly one of model year or
+ * manufacture date will be present.
+ */
+ public Integer getModelYear() {
+ return mModelYear;
+ }
+
+ /**
+ * @return Manufacture date. Typically exactly one of model year or manufacture
+ * date will be present.
+ */
+ public ManufactureDate getManufactureDate() {
+ return mManufactureDate;
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceProductInfo{"
+ + "name="
+ + mName
+ + ", manufacturerPnpId="
+ + mManufacturerPnpId
+ + ", productId="
+ + mProductId
+ + ", modelYear="
+ + mModelYear
+ + ", manufactureDate="
+ + mManufactureDate
+ + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DeviceProductInfo that = (DeviceProductInfo) o;
+ return Objects.equals(mName, that.mName)
+ && Objects.equals(mManufacturerPnpId, that.mManufacturerPnpId)
+ && Objects.equals(mProductId, that.mProductId)
+ && Objects.equals(mModelYear, that.mModelYear)
+ && Objects.equals(mManufactureDate, that.mManufactureDate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mManufacturerPnpId, mProductId, mModelYear, mManufactureDate);
+ }
+
+ public static final Creator<DeviceProductInfo> CREATOR =
+ new Creator<DeviceProductInfo>() {
+ @Override
+ public DeviceProductInfo createFromParcel(Parcel in) {
+ return new DeviceProductInfo(in);
+ }
+
+ @Override
+ public DeviceProductInfo[] newArray(int size) {
+ return new DeviceProductInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mName);
+ dest.writeString(mManufacturerPnpId);
+ dest.writeValue(mProductId);
+ dest.writeValue(mModelYear);
+ dest.writeValue(mManufactureDate);
+ }
+
+ /**
+ * Stores information about the date of manufacture.
+ *
+ * @hide
+ */
+ public static class ManufactureDate implements Parcelable {
+ final private Integer mWeek;
+ final private Integer mYear;
+
+ public ManufactureDate(Integer week, Integer year) {
+ mWeek = week;
+ mYear = year;
+ }
+
+ protected ManufactureDate(Parcel in) {
+ mWeek = (Integer) in.readValue(null);
+ mYear = (Integer) in.readValue(null);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeValue(mWeek);
+ dest.writeValue(mYear);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<ManufactureDate> CREATOR =
+ new Creator<ManufactureDate>() {
+ @Override
+ public ManufactureDate createFromParcel(Parcel in) {
+ return new ManufactureDate(in);
+ }
+
+ @Override
+ public ManufactureDate[] newArray(int size) {
+ return new ManufactureDate[size];
+ }
+ };
+
+ public int getYear() {
+ return mYear;
+ }
+
+ public int getWeek() {
+ return mWeek;
+ }
+
+ @Override
+ public String toString() {
+ return "ManufactureDate{week=" + mWeek + ", year=" + mYear + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ManufactureDate that = (ManufactureDate) o;
+ return Objects.equals(mWeek, that.mWeek) && Objects.equals(mYear, that.mYear);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mWeek, mYear);
+ }
+ }
+}
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 8c989607e8db..ad002335a010 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -103,4 +103,11 @@ interface IThermalService {
* {@hide}
*/
List<CoolingDevice> getCurrentCoolingDevicesWithType(in int type);
+
+ /**
+ * @param forecastSeconds how many seconds ahead to forecast the provided headroom
+ * @return forecasted thermal headroom, normalized such that 1.0 indicates that throttling will
+ * occur; returns NaN if the headroom or forecast is unavailable
+ */
+ float getThermalHeadroom(int forecastSeconds);
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index a8fa6db232a2..199b5d55bb39 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -18,7 +18,9 @@ package android.os;
import android.Manifest.permission;
import android.annotation.CallbackExecutor;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -39,6 +41,7 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicLong;
/**
* This class gives you control of the power state of the device.
@@ -916,20 +919,22 @@ public final class PowerManager {
final IPowerManager mService;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
final Handler mHandler;
+ final IThermalService mThermalService;
/** We lazily initialize it.*/
private DeviceIdleManager mDeviceIdleManager;
- IThermalService mThermalService;
private final ArrayMap<OnThermalStatusChangedListener, IThermalStatusListener>
mListenerMap = new ArrayMap<>();
/**
* {@hide}
*/
- public PowerManager(Context context, IPowerManager service, Handler handler) {
+ public PowerManager(Context context, IPowerManager service, IThermalService thermalService,
+ Handler handler) {
mContext = context;
mService = service;
+ mThermalService = thermalService;
mHandler = handler;
}
@@ -1877,18 +1882,11 @@ public final class PowerManager {
* thermal throttling.
*/
public @ThermalStatus int getCurrentThermalStatus() {
- synchronized (this) {
- if (mThermalService == null) {
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- }
- try {
- return mThermalService.getCurrentThermalStatus();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ try {
+ return mThermalService.getCurrentThermalStatus();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
-
}
/**
@@ -1915,13 +1913,7 @@ public final class PowerManager {
*/
public void addThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
- synchronized (this) {
- if (mThermalService == null) {
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- }
- this.addThermalStatusListener(mContext.getMainExecutor(), listener);
- }
+ this.addThermalStatusListener(mContext.getMainExecutor(), listener);
}
/**
@@ -1934,35 +1926,29 @@ public final class PowerManager {
@NonNull OnThermalStatusChangedListener listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
Preconditions.checkNotNull(executor, "executor cannot be null");
- synchronized (this) {
- if (mThermalService == null) {
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- }
- Preconditions.checkArgument(!mListenerMap.containsKey(listener),
- "Listener already registered: " + listener);
- IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
- @Override
- public void onStatusChange(int status) {
- final long token = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> {
- listener.onThermalStatusChanged(status);
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- };
- try {
- if (mThermalService.registerThermalStatusListener(internalListener)) {
- mListenerMap.put(listener, internalListener);
- } else {
- throw new RuntimeException("Listener failed to set");
+ Preconditions.checkArgument(!mListenerMap.containsKey(listener),
+ "Listener already registered: " + listener);
+ IThermalStatusListener internalListener = new IThermalStatusListener.Stub() {
+ @Override
+ public void onStatusChange(int status) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ listener.onThermalStatusChanged(status);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
}
+ };
+ try {
+ if (mThermalService.registerThermalStatusListener(internalListener)) {
+ mListenerMap.put(listener, internalListener);
+ } else {
+ throw new RuntimeException("Listener failed to set");
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -1973,22 +1959,72 @@ public final class PowerManager {
*/
public void removeThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
- synchronized (this) {
- if (mThermalService == null) {
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- }
- IThermalStatusListener internalListener = mListenerMap.get(listener);
- Preconditions.checkArgument(internalListener != null, "Listener was not added");
- try {
- if (mThermalService.unregisterThermalStatusListener(internalListener)) {
- mListenerMap.remove(listener);
- } else {
- throw new RuntimeException("Listener failed to remove");
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ IThermalStatusListener internalListener = mListenerMap.get(listener);
+ Preconditions.checkArgument(internalListener != null, "Listener was not added");
+ try {
+ if (mThermalService.unregisterThermalStatusListener(internalListener)) {
+ mListenerMap.remove(listener);
+ } else {
+ throw new RuntimeException("Listener failed to remove");
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @CurrentTimeMillisLong
+ private final AtomicLong mLastHeadroomUpdate = new AtomicLong(0L);
+ private static final int MINIMUM_HEADROOM_TIME_MILLIS = 500;
+
+ /**
+ * Provides an estimate of how much thermal headroom the device currently has before hitting
+ * severe throttling.
+ *
+ * Note that this only attempts to track the headroom of slow-moving sensors, such as the skin
+ * temperature sensor. This means that there is no benefit to calling this function more
+ * frequently than about once per second, and attempts to call significantly more frequently may
+ * result in the function returning {@code NaN}.
+ *
+ * In addition, in order to be able to provide an accurate forecast, the system does not attempt
+ * to forecast until it has multiple temperature samples from which to extrapolate. This should
+ * only take a few seconds from the time of the first call, but during this time, no forecasting
+ * will occur, and the current headroom will be returned regardless of the value of
+ * {@code forecastSeconds}.
+ *
+ * The value returned is a non-negative float that represents how much of the thermal envelope
+ * is in use (or is forecasted to be in use). A value of 1.0 indicates that the device is (or
+ * will be) throttled at {@link #THERMAL_STATUS_SEVERE}. Such throttling can affect the CPU,
+ * GPU, and other subsystems. Values may exceed 1.0, but there is no implied mapping to specific
+ * thermal status levels beyond that point. This means that values greater than 1.0 may
+ * correspond to {@link #THERMAL_STATUS_SEVERE}, but may also represent heavier throttling.
+ *
+ * A value of 0.0 corresponds to a fixed distance from 1.0, but does not correspond to any
+ * particular thermal status or temperature. Values on (0.0, 1.0] may be expected to scale
+ * linearly with temperature, though temperature changes over time are typically not linear.
+ * Negative values will be clamped to 0.0 before returning.
+ *
+ * @param forecastSeconds how many seconds in the future to forecast. Given that device
+ * conditions may change at any time, forecasts from further in the
+ * future will likely be less accurate than forecasts in the near future.
+ * @return a value greater than or equal to 0.0 where 1.0 indicates the SEVERE throttling
+ * threshold, as described above. Returns NaN if the device does not support this
+ * functionality or if this function is called significantly faster than once per
+ * second.
+ */
+ public float getThermalHeadroom(@IntRange(from = 0, to = 60) int forecastSeconds) {
+ // Rate-limit calls into the thermal service
+ long now = SystemClock.elapsedRealtime();
+ long timeSinceLastUpdate = now - mLastHeadroomUpdate.get();
+ if (timeSinceLastUpdate < MINIMUM_HEADROOM_TIME_MILLIS) {
+ return Float.NaN;
+ }
+
+ try {
+ float forecast = mThermalService.getThermalHeadroom(forecastSeconds);
+ mLastHeadroomUpdate.set(SystemClock.elapsedRealtime());
+ return forecast;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0fa261a6a891..4523acb0eb26 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9093,26 +9093,34 @@ public final class Settings {
* Set to one of {@link #WIFI_SLEEP_POLICY_DEFAULT},
* {@link #WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED}, or
* {@link #WIFI_SLEEP_POLICY_NEVER}.
+ * @deprecated This is no longer used or set by the platform.
*/
+ @Deprecated
public static final String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
/**
* Value for {@link #WIFI_SLEEP_POLICY} to use the default Wi-Fi sleep
* policy, which is to sleep shortly after the turning off
* according to the {@link #STAY_ON_WHILE_PLUGGED_IN} setting.
+ * @deprecated This is no longer used by the platform.
*/
+ @Deprecated
public static final int WIFI_SLEEP_POLICY_DEFAULT = 0;
/**
* Value for {@link #WIFI_SLEEP_POLICY} to use the default policy when
* the device is on battery, and never go to sleep when the device is
* plugged in.
+ * @deprecated This is no longer used by the platform.
*/
+ @Deprecated
public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1;
/**
* Value for {@link #WIFI_SLEEP_POLICY} to never go to sleep.
+ * @deprecated This is no longer used by the platform.
*/
+ @Deprecated
public static final int WIFI_SLEEP_POLICY_NEVER = 2;
/**
@@ -10206,7 +10214,9 @@ public final class Settings {
/**
* Delay (in seconds) before repeating the Wi-Fi networks available notification.
* Connecting to a network will reset the timer.
+ * @deprecated This is no longer used or set by the platform.
*/
+ @Deprecated
public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
"wifi_networks_available_repeat_delay";
@@ -10236,7 +10246,9 @@ public final class Settings {
/**
* When the number of open networks exceeds this number, the
* least-recently-used excess networks will be removed.
+ * @deprecated This is no longer used or set by the platform.
*/
+ @Deprecated
public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
/**
@@ -10247,8 +10259,8 @@ public final class Settings {
/**
* Setting to allow scans to be enabled even wifi is turned off for connectivity.
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String WIFI_SCAN_ALWAYS_AVAILABLE =
"wifi_scan_always_enabled";
@@ -10257,8 +10269,8 @@ public final class Settings {
*
* Type: int (0 for false, 1 for true)
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String WIFI_P2P_PENDING_FACTORY_RESET =
"wifi_p2p_pending_factory_reset";
@@ -10267,8 +10279,8 @@ public final class Settings {
*
* Type: int (0 for false, 1 for true)
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
/**
@@ -10313,6 +10325,7 @@ public final class Settings {
* Most readers of this setting should simply check if value == 1 to determined the
* enabled state.
* @hide
+ * @deprecated To be removed.
*/
public static final String NETWORK_RECOMMENDATIONS_ENABLED =
"network_recommendations_enabled";
@@ -10352,13 +10365,11 @@ public final class Settings {
/**
* Whether wifi scan throttle is enabled or not.
- * This is intended to be used via adb commands or a menu in developer option to turn off
- * the default wifi scan throttling mechanism for apps.
*
* Type: int (0 for false, 1 for true)
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
/**
@@ -10460,8 +10471,8 @@ public final class Settings {
* Setting to enable verbose logging in Wi-Fi; disabled by default, and setting to 1
* will enable it. In the future, additional values may be supported.
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String WIFI_VERBOSE_LOGGING_ENABLED =
"wifi_verbose_logging_enabled";
@@ -10486,8 +10497,8 @@ public final class Settings {
* Default values are provided by code or device configurations.
* Errors in the parameters will cause the entire setting to be ignored.
* @hide
+ * @deprecated This is no longer used or set by the platform.
*/
- @SystemApi
public static final String WIFI_SCORE_PARAMS =
"wifi_score_params";
@@ -10529,8 +10540,8 @@ public final class Settings {
/**
* The Wi-Fi peer-to-peer device name
* @hide
+ * @deprecated To be removed.
*/
- @SystemApi
public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
/**
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b9868a7e1444..3047385410b0 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -28,6 +28,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.hardware.display.DeviceProductInfo;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -70,6 +71,13 @@ public final class DisplayInfo implements Parcelable {
public DisplayAddress address;
/**
+ * Product-specific information about the display or the directly connected device on the
+ * display chain. For example, if the display is transitively connected, this field may contain
+ * product information about the intermediate device.
+ */
+ public DeviceProductInfo deviceProductInfo;
+
+ /**
* The human-readable name of the display.
*/
public String name;
@@ -297,6 +305,7 @@ public final class DisplayInfo implements Parcelable {
&& type == other.type
&& displayId == other.displayId
&& Objects.equals(address, other.address)
+ && Objects.equals(deviceProductInfo, other.deviceProductInfo)
&& Objects.equals(uniqueId, other.uniqueId)
&& appWidth == other.appWidth
&& appHeight == other.appHeight
@@ -336,6 +345,7 @@ public final class DisplayInfo implements Parcelable {
type = other.type;
displayId = other.displayId;
address = other.address;
+ deviceProductInfo = other.deviceProductInfo;
name = other.name;
uniqueId = other.uniqueId;
appWidth = other.appWidth;
@@ -373,6 +383,7 @@ public final class DisplayInfo implements Parcelable {
type = source.readInt();
displayId = source.readInt();
address = source.readParcelable(null);
+ deviceProductInfo = source.readParcelable(null);
name = source.readString();
appWidth = source.readInt();
appHeight = source.readInt();
@@ -418,6 +429,7 @@ public final class DisplayInfo implements Parcelable {
dest.writeInt(type);
dest.writeInt(displayId);
dest.writeParcelable(address, flags);
+ dest.writeParcelable(deviceProductInfo, flags);
dest.writeString(name);
dest.writeInt(appWidth);
dest.writeInt(appHeight);
@@ -645,6 +657,8 @@ public final class DisplayInfo implements Parcelable {
if (address != null) {
sb.append(", address ").append(address);
}
+ sb.append(", deviceProductInfo ");
+ sb.append(deviceProductInfo);
sb.append(", state ");
sb.append(Display.stateToString(state));
if (ownerUid != 0 || ownerPackageName != null) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cf48c52825e9..29371b049b3c 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -41,6 +41,7 @@ import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.display.DeviceProductInfo;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
import android.os.Build;
@@ -1286,12 +1287,14 @@ public final class SurfaceControl implements Parcelable {
public boolean isInternal;
public float density;
public boolean secure;
+ public DeviceProductInfo deviceProductInfo;
@Override
public String toString() {
return "DisplayInfo{isInternal=" + isInternal
+ ", density=" + density
- + ", secure=" + secure + "}";
+ + ", secure=" + secure
+ + ", deviceProductInfo=" + deviceProductInfo + "}";
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d0086928e1b5..b6c46be66761 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3692,6 +3692,31 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
childrenForAccessibility.clear();
}
+ info.setAvailableExtraData(Collections.singletonList(
+ AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param info The info to which to add the extra data. Never {@code null}.
+ * @param extraDataKey A key specifying the type of extra data to add to the info. The
+ * extra data should be added to the {@link Bundle} returned by
+ * the info's {@link AccessibilityNodeInfo#getExtras} method. Never
+ * {@code null}.
+ * @param arguments A {@link Bundle} holding any arguments relevant for this request. May be
+ * {@code null} if the service provided no arguments.
+ *
+ */
+ @Override
+ public void addExtraDataToAccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info,
+ @NonNull String extraDataKey, @Nullable Bundle arguments) {
+ if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) {
+ final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo =
+ AccessibilityNodeInfo.ExtraRenderingInfo.obtain();
+ extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height);
+ info.setExtraRenderingInfo(extraRenderingInfo);
+ }
}
@Override
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index bf2de14e6811..eb4f9db39e3e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -50,8 +50,12 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.LongArray;
import android.util.Pools.SynchronizedPool;
+import android.util.Size;
+import android.util.TypedValue;
import android.view.TouchDelegate;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
import com.android.internal.R;
import com.android.internal.util.CollectionUtils;
@@ -634,6 +638,25 @@ public class AccessibilityNodeInfo implements Parcelable {
public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH =
"android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+ /**
+ * Key used to request extra data for accessibility scanning tool's purposes.
+ * The key requests that a {@link AccessibilityNodeInfo.ExtraRenderingInfo} be added to this
+ * info. This request is made with {@link #refreshWithExtraData(String, Bundle)} without
+ * argument.
+ * <p>
+ * The data can be retrieved from the {@link ExtraRenderingInfo} returned by
+ * {@link #getExtraRenderingInfo()} using {@link ExtraRenderingInfo#getLayoutParams},
+ * {@link ExtraRenderingInfo#getTextSizeInPx()} and
+ * {@link ExtraRenderingInfo#getTextSizeUnit()}. For layout params, it is supported by both
+ * {@link TextView} and {@link ViewGroup}. For text size and unit, it is only supported by
+ * {@link TextView}.
+ *
+ * @see #refreshWithExtraData(String, Bundle)
+ */
+
+ public static final String EXTRA_DATA_RENDERING_INFO_KEY =
+ "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
+
/** @hide */
public static final String EXTRA_DATA_REQUESTED_KEY =
"android.view.accessibility.AccessibilityNodeInfo.extra_data_requested";
@@ -804,6 +827,8 @@ public class AccessibilityNodeInfo implements Parcelable {
private TouchDelegateInfo mTouchDelegateInfo;
+ private ExtraRenderingInfo mExtraRenderingInfo;
+
private IBinder mLeashedChild;
private IBinder mLeashedParent;
private long mLeashedParentNodeId = UNDEFINED_NODE_ID;
@@ -991,6 +1016,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param extraDataKey The extra data requested. Data that must be requested
* with this mechanism is generally expensive to retrieve, so should only be
* requested when needed. See
+ * {@link #EXTRA_DATA_RENDERING_INFO_KEY},
* {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} and
* {@link #getAvailableExtraData()}.
* @param args A bundle of arguments for the request. These depend on the particular request.
@@ -1547,6 +1573,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* {@link #refreshWithExtraData(String, Bundle)}.
*
* @return An unmodifiable list of keys corresponding to extra data that can be requested.
+ * @see #EXTRA_DATA_RENDERING_INFO_KEY
* @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
*/
public List<String> getAvailableExtraData() {
@@ -2375,6 +2402,32 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Gets the conformance info if the node is meant to be refreshed with extra data.
+ *
+ * @return The conformance info.
+ */
+ @Nullable
+ public ExtraRenderingInfo getExtraRenderingInfo() {
+ return mExtraRenderingInfo;
+ }
+
+ /**
+ * Sets the conformance info if the node is meant to be refreshed with extra data.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param extraRenderingInfo The conformance info.
+ * @hide
+ */
+ public void setExtraRenderingInfo(@NonNull ExtraRenderingInfo extraRenderingInfo) {
+ enforceNotSealed();
+ mExtraRenderingInfo = extraRenderingInfo;
+ }
+
+ /**
* Gets if the content of this node is invalid. For example,
* a date is not well-formed.
*
@@ -3695,6 +3748,10 @@ public class AccessibilityNodeInfo implements Parcelable {
nonDefaultFields |= bitAt(fieldIndex);
}
fieldIndex++;
+ if (!Objects.equals(mExtraRenderingInfo, DEFAULT.mExtraRenderingInfo)) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+ fieldIndex++;
if (mLeashedChild != DEFAULT.mLeashedChild) {
nonDefaultFields |= bitAt(fieldIndex);
}
@@ -3833,6 +3890,12 @@ public class AccessibilityNodeInfo implements Parcelable {
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ parcel.writeValue(mExtraRenderingInfo.getLayoutParams());
+ parcel.writeFloat(mExtraRenderingInfo.getTextSizeInPx());
+ parcel.writeInt(mExtraRenderingInfo.getTextSizeUnit());
+ }
+
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
parcel.writeStrongBinder(mLeashedChild);
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -3941,6 +4004,9 @@ public class AccessibilityNodeInfo implements Parcelable {
if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
mCollectionItemInfo = (other.mCollectionItemInfo != null)
? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null;
+ if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
+ mExtraRenderingInfo = (other.mExtraRenderingInfo != null)
+ ? ExtraRenderingInfo.obtain(other.mExtraRenderingInfo) : null;
}
private void initCopyInfos(AccessibilityNodeInfo other) {
@@ -3955,6 +4021,9 @@ public class AccessibilityNodeInfo implements Parcelable {
mCollectionItemInfo = (cii == null) ? null
: new CollectionItemInfo(cii.mRowIndex, cii.mRowSpan, cii.mColumnIndex,
cii.mColumnSpan, cii.mHeading, cii.mSelected);
+ ExtraRenderingInfo ti = other.mExtraRenderingInfo;
+ mExtraRenderingInfo = (ti == null) ? null
+ : new ExtraRenderingInfo(ti);
}
/**
@@ -4083,6 +4152,14 @@ public class AccessibilityNodeInfo implements Parcelable {
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
+ mExtraRenderingInfo = ExtraRenderingInfo.obtain();
+ mExtraRenderingInfo.mLayoutParams = (Size) parcel.readValue(null);
+ mExtraRenderingInfo.mTextSizeInPx = parcel.readFloat();
+ mExtraRenderingInfo.mTextSizeUnit = parcel.readInt();
+ }
+
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
mLeashedChild = parcel.readStrongBinder();
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -5679,6 +5756,134 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Class with information of a view useful to evaluate accessibility needs. Developers can
+ * refresh the node with the key {@link #EXTRA_DATA_RENDERING_INFO_KEY} to fetch the text size
+ * and unit if it is {@link TextView} and the height and the width of layout params from
+ * {@link ViewGroup} or {@link TextView}.
+ *
+ * @see #EXTRA_DATA_RENDERING_INFO_KEY
+ * @see #refreshWithExtraData(String, Bundle)
+ */
+ public static final class ExtraRenderingInfo {
+ private static final int UNDEFINED_VALUE = -1;
+ private static final int MAX_POOL_SIZE = 20;
+ private static final SynchronizedPool<ExtraRenderingInfo> sPool =
+ new SynchronizedPool<>(MAX_POOL_SIZE);
+
+ private Size mLayoutParams;
+ private float mTextSizeInPx = UNDEFINED_VALUE;
+ private int mTextSizeUnit = UNDEFINED_VALUE;
+
+ /**
+ * Obtains a pooled instance.
+ * @hide
+ */
+ @NonNull
+ public static ExtraRenderingInfo obtain() {
+ final ExtraRenderingInfo info = sPool.acquire();
+ if (info == null) {
+ return new ExtraRenderingInfo(null);
+ }
+ return info;
+ }
+
+ /** Obtains a pooled instance that is a clone of another one. */
+ private static ExtraRenderingInfo obtain(ExtraRenderingInfo other) {
+ ExtraRenderingInfo extraRenderingInfo = ExtraRenderingInfo.obtain();
+ extraRenderingInfo.mLayoutParams = other.mLayoutParams;
+ extraRenderingInfo.mTextSizeInPx = other.mTextSizeInPx;
+ extraRenderingInfo.mTextSizeUnit = other.mTextSizeUnit;
+ return extraRenderingInfo;
+ }
+
+ /**
+ * Creates a new conformance info of a view, and this new instance is initialized from
+ * the given <code>other</code>.
+ *
+ * @param other The instance to clone.
+ */
+ private ExtraRenderingInfo(@Nullable ExtraRenderingInfo other) {
+ if (other != null) {
+ mLayoutParams = other.mLayoutParams;
+ mTextSizeInPx = other.mTextSizeInPx;
+ mTextSizeUnit = other.mTextSizeUnit;
+ }
+ }
+
+ /**
+ * @return a {@link Size} stores layout height and layout width of the view,
+ * or null otherwise.
+ */
+ public @Nullable Size getLayoutParams() {
+ return mLayoutParams;
+ }
+
+ /**
+ * Sets layout width and layout height of the view.
+ *
+ * @param width The layout width.
+ * @param height The layout height.
+ * @hide
+ */
+ public void setLayoutParams(int width, int height) {
+ mLayoutParams = new Size(width, height);
+ }
+
+ /**
+ * @return the text size of a {@code TextView}, or -1 otherwise.
+ */
+ public float getTextSizeInPx() {
+ return mTextSizeInPx;
+ }
+
+ /**
+ * Sets text size of the view.
+ *
+ * @param textSizeInPx The text size in pixels.
+ * @hide
+ */
+ public void setTextSizeInPx(float textSizeInPx) {
+ mTextSizeInPx = textSizeInPx;
+ }
+
+ /**
+ * @return the text size unit which type is {@link TypedValue#TYPE_DIMENSION} of a
+ * {@code TextView}, or -1 otherwise.
+ *
+ * @see TypedValue#TYPE_DIMENSION
+ */
+ public int getTextSizeUnit() {
+ return mTextSizeUnit;
+ }
+
+ /**
+ * Sets text size unit of the view.
+ *
+ * @param textSizeUnit The text size unit.
+ * @hide
+ */
+ public void setTextSizeUnit(int textSizeUnit) {
+ mTextSizeUnit = textSizeUnit;
+ }
+
+ /**
+ * Recycles this instance.
+ *
+ * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+ */
+ void recycle() {
+ clear();
+ sPool.release(this);
+ }
+
+ private void clear() {
+ mLayoutParams = null;
+ mTextSizeInPx = UNDEFINED_VALUE;
+ mTextSizeUnit = UNDEFINED_VALUE;
+ }
+ }
+
+ /**
* @see android.os.Parcelable.Creator
*/
public static final @android.annotation.NonNull Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0182975c13c0..815cc5cbb10d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,6 +17,7 @@
package android.widget;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
@@ -727,6 +728,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@UnsupportedAppUsage
private Layout mLayout;
private boolean mLocalesChanged = false;
+ private int mTextSizeUnit = -1;
// True if setKeyListener() has been explicitly called
private boolean mListenerChanged = false;
@@ -3842,6 +3844,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
ColorStateList mTextColorHint = null;
ColorStateList mTextColorLink = null;
int mTextSize = -1;
+ int mTextSizeUnit = -1;
LocaleList mTextLocales = null;
String mFontFamily = null;
Typeface mFontTypeface = null;
@@ -3869,6 +3872,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
+ " mTextColorHint:" + mTextColorHint + "\n"
+ " mTextColorLink:" + mTextColorLink + "\n"
+ " mTextSize:" + mTextSize + "\n"
+ + " mTextSizeUnit:" + mTextSizeUnit + "\n"
+ " mTextLocales:" + mTextLocales + "\n"
+ " mFontFamily:" + mFontFamily + "\n"
+ " mFontTypeface:" + mFontTypeface + "\n"
@@ -3980,6 +3984,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case com.android.internal.R.styleable.TextAppearance_textSize:
attributes.mTextSize =
appearance.getDimensionPixelSize(attr, attributes.mTextSize);
+ attributes.mTextSizeUnit = appearance.peekValue(attr).getComplexUnit();
break;
case com.android.internal.R.styleable.TextAppearance_textLocale:
final String localeString = appearance.getString(attr);
@@ -4073,6 +4078,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (attributes.mTextSize != -1) {
+ mTextSizeUnit = attributes.mTextSizeUnit;
setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
}
@@ -4295,6 +4301,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
r = c.getResources();
}
+ mTextSizeUnit = unit;
setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()),
shouldRequestLayout);
}
@@ -4315,6 +4322,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Gets the text size unit defined by the developer. It may be specified in resources or be
+ * passed as the unit argument of {@link #setTextSize(int, float)} at runtime.
+ *
+ * @return the dimension type of the text size unit originally defined.
+ * @see TypedValue#TYPE_DIMENSION
+ */
+ public int getTextSizeUnit() {
+ return mTextSizeUnit;
+ }
+
+ /**
* Gets the extent by which text should be stretched horizontally.
* This will usually be 1.0.
* @return The horizontal scale factor.
@@ -11769,8 +11787,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
- info.setAvailableExtraData(
- Arrays.asList(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY));
+ info.setAvailableExtraData(Arrays.asList(
+ EXTRA_DATA_RENDERING_INFO_KEY,
+ EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
+ ));
+ } else {
+ info.setAvailableExtraData(Arrays.asList(
+ EXTRA_DATA_RENDERING_INFO_KEY
+ ));
}
if (isFocused()) {
@@ -11824,11 +11848,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public void addExtraDataToAccessibilityNodeInfo(
AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
- // The only extra data we support requires arguments.
- if (arguments == null) {
- return;
- }
- if (extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
+ if (arguments != null && extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
int positionInfoStartIndex = arguments.getInt(
EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX, -1);
int positionInfoLength = arguments.getInt(
@@ -11856,6 +11876,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
info.getExtras().putParcelableArray(extraDataKey, boundingRects);
+ return;
+ }
+ if (extraDataKey.equals(AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY)) {
+ final AccessibilityNodeInfo.ExtraRenderingInfo extraRenderingInfo =
+ AccessibilityNodeInfo.ExtraRenderingInfo.obtain();
+ extraRenderingInfo.setLayoutParams(getLayoutParams().width, getLayoutParams().height);
+ extraRenderingInfo.setTextSizeInPx(getTextSize());
+ extraRenderingInfo.setTextSizeUnit(getTextSizeUnit());
+ info.setExtraRenderingInfo(extraRenderingInfo);
}
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 3f81b112d9f1..741ce8291e57 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -37,6 +37,7 @@
#include <stdio.h>
#include <system/graphics.h>
#include <ui/ConfigStoreTypes.h>
+#include <ui/DeviceProductInfo.h>
#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
#include <ui/DisplayedFrameStats.h>
@@ -65,9 +66,19 @@ static const char* const OutOfResourcesException =
static struct {
jclass clazz;
jmethodID ctor;
+} gIntegerClassInfo;
+
+static jobject toInteger(JNIEnv* env, int32_t i) {
+ return env->NewObject(gIntegerClassInfo.clazz, gIntegerClassInfo.ctor, i);
+}
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
jfieldID isInternal;
jfieldID density;
jfieldID secure;
+ jfieldID deviceProductInfo;
} gDisplayInfoClassInfo;
static struct {
@@ -111,6 +122,16 @@ static struct {
static struct {
jclass clazz;
+ jmethodID ctor;
+} gDeviceProductInfoClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gDeviceProductInfoManufactureDateClassInfo;
+
+static struct {
+ jclass clazz;
jmethodID builder;
} gGraphicBufferClassInfo;
@@ -773,6 +794,41 @@ static void nativeSetDisplaySize(JNIEnv* env, jclass clazz,
}
}
+static jobject convertDeviceProductInfoToJavaObject(
+ JNIEnv* env, const std::optional<DeviceProductInfo>& info) {
+ using ModelYear = android::DeviceProductInfo::ModelYear;
+ using ManufactureYear = android::DeviceProductInfo::ManufactureYear;
+ using ManufactureWeekAndYear = android::DeviceProductInfo::ManufactureWeekAndYear;
+
+ if (!info) return nullptr;
+ jstring name = env->NewStringUTF(info->name.data());
+ jstring manufacturerPnpId = env->NewStringUTF(info->manufacturerPnpId.data());
+ jobject productId = env->NewStringUTF(info->productId.data());
+ const auto& date = info->manufactureOrModelDate;
+ jobject modelYear, manufactureDate;
+ if (const auto* model = std::get_if<ModelYear>(&date)) {
+ modelYear = toInteger(env, model->year);
+ manufactureDate = nullptr;
+ } else if (const auto* manufactureWeekAndYear = std::get_if<ManufactureWeekAndYear>(&date)) {
+ modelYear = nullptr;
+ manufactureDate = env->NewObject(gDeviceProductInfoManufactureDateClassInfo.clazz,
+ gDeviceProductInfoManufactureDateClassInfo.ctor,
+ toInteger(env, manufactureWeekAndYear->week),
+ toInteger(env, manufactureWeekAndYear->year));
+ } else if (const auto* manufactureYear = std::get_if<ManufactureYear>(&date)) {
+ modelYear = nullptr;
+ manufactureDate = env->NewObject(gDeviceProductInfoManufactureDateClassInfo.clazz,
+ gDeviceProductInfoManufactureDateClassInfo.ctor,
+ nullptr,
+ toInteger(env, manufactureYear->year));
+ } else {
+ LOG_FATAL("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
+ }
+
+ return env->NewObject(gDeviceProductInfoClassInfo.clazz, gDeviceProductInfoClassInfo.ctor, name,
+ manufacturerPnpId, productId, modelYear, manufactureDate);
+}
+
static jobject nativeGetDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) {
DisplayInfo info;
if (const auto token = ibinderForJavaObject(env, tokenObj);
@@ -785,6 +841,8 @@ static jobject nativeGetDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj)
info.connectionType == DisplayConnectionType::Internal);
env->SetFloatField(object, gDisplayInfoClassInfo.density, info.density);
env->SetBooleanField(object, gDisplayInfoClassInfo.secure, info.secure);
+ env->SetObjectField(object, gDisplayInfoClassInfo.deviceProductInfo,
+ convertDeviceProductInfoToJavaObject(env, info.deviceProductInfo));
return object;
}
@@ -1527,12 +1585,19 @@ int register_android_view_SurfaceControl(JNIEnv* env)
int err = RegisterMethodsOrDie(env, "android/view/SurfaceControl",
sSurfaceControlMethods, NELEM(sSurfaceControlMethods));
+ jclass integerClass = FindClassOrDie(env, "java/lang/Integer");
+ gIntegerClassInfo.clazz = MakeGlobalRefOrDie(env, integerClass);
+ gIntegerClassInfo.ctor = GetMethodIDOrDie(env, gIntegerClassInfo.clazz, "<init>", "(I)V");
+
jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayInfo");
gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz);
gDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V");
gDisplayInfoClassInfo.isInternal = GetFieldIDOrDie(env, infoClazz, "isInternal", "Z");
gDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F");
gDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z");
+ gDisplayInfoClassInfo.deviceProductInfo =
+ GetFieldIDOrDie(env, infoClazz, "deviceProductInfo",
+ "Landroid/hardware/display/DeviceProductInfo;");
jclass configClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayConfig");
gDisplayConfigClassInfo.clazz = MakeGlobalRefOrDie(env, configClazz);
@@ -1573,6 +1638,25 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gHdrCapabilitiesClassInfo.ctor = GetMethodIDOrDie(env, hdrCapabilitiesClazz, "<init>",
"([IFFF)V");
+ jclass deviceProductInfoClazz =
+ FindClassOrDie(env, "android/hardware/display/DeviceProductInfo");
+ gDeviceProductInfoClassInfo.clazz = MakeGlobalRefOrDie(env, deviceProductInfoClazz);
+ gDeviceProductInfoClassInfo.ctor =
+ GetMethodIDOrDie(env, deviceProductInfoClazz, "<init>",
+ "(Ljava/lang/String;"
+ "Ljava/lang/String;"
+ "Ljava/lang/String;"
+ "Ljava/lang/Integer;"
+ "Landroid/hardware/display/DeviceProductInfo$ManufactureDate;)V");
+
+ jclass deviceProductInfoManufactureDateClazz =
+ FindClassOrDie(env, "android/hardware/display/DeviceProductInfo$ManufactureDate");
+ gDeviceProductInfoManufactureDateClassInfo.clazz =
+ MakeGlobalRefOrDie(env, deviceProductInfoManufactureDateClazz);
+ gDeviceProductInfoManufactureDateClassInfo.ctor =
+ GetMethodIDOrDie(env, deviceProductInfoManufactureDateClazz, "<init>",
+ "(Ljava/lang/Integer;Ljava/lang/Integer;)V");
+
jclass graphicsBufferClazz = FindClassOrDie(env, "android/graphics/GraphicBuffer");
gGraphicBufferClassInfo.clazz = MakeGlobalRefOrDie(env, graphicsBufferClazz);
gGraphicBufferClassInfo.builder = GetStaticMethodIDOrDie(env, graphicsBufferClazz,
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index ea0a0fd49f9f..37e8d5937bc5 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -244,6 +244,28 @@ public class PowerManagerTest extends AndroidTestCase {
}
@Test
+ public void testGetThermalHeadroom() throws Exception {
+ float headroom = mPm.getThermalHeadroom(0);
+ // If the device doesn't support thermal headroom, return early
+ if (Float.isNaN(headroom)) {
+ return;
+ }
+ assertTrue("Expected non-negative headroom", headroom >= 0.0f);
+ assertTrue("Expected reasonably small headroom", headroom < 10.0f);
+
+ // Call again immediately to ensure rate limiting works
+ headroom = mPm.getThermalHeadroom(0);
+ assertTrue("Expected NaN because of rate limiting", Float.isNaN(headroom));
+
+ // Sleep for a second before attempting to call again so as to not get rate limited
+ Thread.sleep(1000);
+ headroom = mPm.getThermalHeadroom(5);
+ assertFalse("Expected data to still be available", Float.isNaN(headroom));
+ assertTrue("Expected non-negative headroom", headroom >= 0.0f);
+ assertTrue("Expected reasonably small headroom", headroom < 10.0f);
+ }
+
+ @Test
public void testUserspaceRebootNotSupported_throwsUnsupportedOperationException() {
// Can't use assumption framework with AndroidTestCase :(
if (mPm.isRebootingUserspaceSupported()) {
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index ade1e0de7102..79e7c50e7987 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@ public class AccessibilityNodeInfoTest {
// The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
// See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
// and assertAccessibilityNodeInfoCleared in that class.
- private static final int NUM_MARSHALLED_PROPERTIES = 38;
+ private static final int NUM_MARSHALLED_PROPERTIES = 39;
/**
* The number of properties that are purposely not marshalled
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 019cb1459838..17aaff1f7383 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -25,10 +25,13 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.drawable.Animatable;
import android.util.AttributeSet;
+import android.util.Pair;
import android.util.SparseArray;
+import android.view.DisplayCutout;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
+import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -42,6 +45,7 @@ import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
public class QSDetail extends LinearLayout {
@@ -274,6 +278,32 @@ public class QSDetail extends LinearLayout {
}
}
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ DisplayCutout cutout = insets.getDisplayCutout();
+
+ Pair<Integer, Integer> padding = StatusBarWindowView.cornerCutoutMargins(
+ cutout, getDisplay());
+
+ if (padding == null) {
+ mQsDetailHeader.setPaddingRelative(
+ getResources().getDimensionPixelSize(R.dimen.qs_detail_header_padding),
+ getPaddingTop(),
+ getResources().getDimensionPixelSize(R.dimen.qs_detail_header_padding),
+ getPaddingBottom()
+ );
+ } else {
+ mQsDetailHeader.setPadding(
+ padding.first,
+ getPaddingTop(),
+ padding.second,
+ getPaddingBottom()
+ );
+ }
+
+ return super.onApplyWindowInsets(insets);
+ }
+
private void handleToggleStateChanged(boolean state, boolean toggleEnabled) {
mSwitchState = state;
if (mAnimatingOpen) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 2981252f148c..e612c07ac18a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -33,6 +33,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationSectionsMan
import com.android.systemui.statusbar.phone.NotificationGroupManager
import com.android.systemui.statusbar.policy.HeadsUpManager
import dagger.Lazy
+import java.util.Comparator
import java.util.Objects
import javax.inject.Inject
@@ -73,6 +74,9 @@ open class NotificationRankingManager @Inject constructor(
val aIsPeople = a.isPeopleNotification()
val bIsPeople = b.isPeopleNotification()
+ val aIsImportantPeople = a.isImportantPeopleNotification()
+ val bIsImportantPeople = b.isImportantPeopleNotification()
+
val aMedia = isImportantMedia(a)
val bMedia = isImportantMedia(b)
@@ -87,6 +91,8 @@ open class NotificationRankingManager @Inject constructor(
when {
usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1
+ usePeopleFiltering && aIsImportantPeople != bIsImportantPeople ->
+ if (aIsImportantPeople) -1 else 1
aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1
// Provide consistent ranking with headsUpManager
aHeadsUp -> headsUpManager.compare(a, b)
@@ -192,6 +198,9 @@ open class NotificationRankingManager @Inject constructor(
private fun NotificationEntry.isPeopleNotification() =
peopleNotificationIdentifier.isPeopleNotification(sbn, ranking)
+ private fun NotificationEntry.isImportantPeopleNotification() =
+ peopleNotificationIdentifier.isImportantPeopleNotification(sbn, ranking)
+
private fun NotificationEntry.isHighPriority() =
highPriorityProvider.isHighPriority(this)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 4672de046c49..e15fa2eac4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -23,6 +23,7 @@ import javax.inject.Singleton
interface PeopleNotificationIdentifier {
fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking): Boolean
+ fun isImportantPeopleNotification(sbn: StatusBarNotification, ranking: Ranking): Boolean
}
@Singleton
@@ -32,4 +33,7 @@ class PeopleNotificationIdentifierImpl @Inject constructor(
override fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking) =
ranking.isConversation || personExtractor.isPersonNotification(sbn)
+
+ override fun isImportantPeopleNotification(sbn: StatusBarNotification, ranking: Ranking) =
+ isPeopleNotification(sbn, ranking) && ranking.channel.isImportantConversation
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 551731824570..234ab936345b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1197,6 +1197,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
mMenuRow.onConfigurationChanged();
}
+ if (mImageResolver != null) {
+ mImageResolver.updateMaxImageSizes();
+ }
}
public void onUiModeChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index fa4bc2aba21a..52f7c2cfee96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -19,12 +19,17 @@ package com.android.systemui.statusbar.notification.row;
import android.app.ActivityManager;
import android.app.Notification;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageResolver;
import com.android.internal.widget.LocalImageResolver;
import com.android.internal.widget.MessagingMessage;
@@ -36,6 +41,10 @@ import java.util.Set;
/**
* Custom resolver with built-in image cache for image messages.
+ *
+ * If the URL points to a bitmap that's larger than the maximum width or height, the bitmap
+ * will be resized down to that maximum size before being cached. See {@link #getMaxImageWidth()},
+ * {@link #getMaxImageHeight()}, and {@link #resolveImage(Uri)} for the downscaling implementation.
*/
public class NotificationInlineImageResolver implements ImageResolver {
private static final String TAG = NotificationInlineImageResolver.class.getSimpleName();
@@ -44,6 +53,13 @@ public class NotificationInlineImageResolver implements ImageResolver {
private final ImageCache mImageCache;
private Set<Uri> mWantedUriSet;
+ // max allowed bitmap width, in pixels
+ @VisibleForTesting
+ protected int mMaxImageWidth;
+ // max allowed bitmap height, in pixels
+ @VisibleForTesting
+ protected int mMaxImageHeight;
+
/**
* Constructor.
* @param context Context.
@@ -56,6 +72,8 @@ public class NotificationInlineImageResolver implements ImageResolver {
if (mImageCache != null) {
mImageCache.setImageResolver(this);
}
+
+ updateMaxImageSizes();
}
/**
@@ -66,14 +84,49 @@ public class NotificationInlineImageResolver implements ImageResolver {
return mImageCache != null && !ActivityManager.isLowRamDeviceStatic();
}
+ private boolean isLowRam() {
+ return ActivityManager.isLowRamDeviceStatic();
+ }
+
+ /**
+ * Update the maximum width and height allowed for bitmaps, ex. after a configuration change.
+ */
+ public void updateMaxImageSizes() {
+ mMaxImageWidth = getMaxImageWidth();
+ mMaxImageHeight = getMaxImageHeight();
+ }
+
+ @VisibleForTesting
+ protected int getMaxImageWidth() {
+ return mContext.getResources().getDimensionPixelSize(isLowRam()
+ ? R.dimen.notification_custom_view_max_image_width_low_ram
+ : R.dimen.notification_custom_view_max_image_width);
+ }
+
+ @VisibleForTesting
+ protected int getMaxImageHeight() {
+ return mContext.getResources().getDimensionPixelSize(isLowRam()
+ ? R.dimen.notification_custom_view_max_image_height_low_ram
+ : R.dimen.notification_custom_view_max_image_height);
+ }
+
+ @VisibleForTesting
+ protected BitmapDrawable resolveImageInternal(Uri uri) throws IOException {
+ return (BitmapDrawable) LocalImageResolver.resolveImage(uri, mContext);
+ }
+
/**
- * To resolve image from specified uri directly.
+ * To resolve image from specified uri directly. If the resulting image is larger than the
+ * maximum allowed size, scale it down.
* @param uri Uri of the image.
* @return Drawable of the image.
* @throws IOException Throws if failed at resolving the image.
*/
Drawable resolveImage(Uri uri) throws IOException {
- return LocalImageResolver.resolveImage(uri, mContext);
+ BitmapDrawable image = resolveImageInternal(uri);
+ Bitmap bitmap = image.getBitmap();
+ image.setBitmap(Icon.scaleDownIfNecessary(bitmap, mMaxImageWidth, mMaxImageHeight));
+ return image;
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index c6b496dd8215..8e330c6f5049 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -145,6 +145,47 @@ class NotificationRankingManagerTest : SysuiTestCase() {
}
@Test
+ fun testSort_importantPeople() {
+ val aN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val aC = NotificationChannel("test", "", IMPORTANCE_DEFAULT)
+ aC.setConversationId("parent", "convo")
+ val a = NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_HIGH)
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(aN)
+ .setChannel(aC)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build()
+
+ val bN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val bC = NotificationChannel("test", "", IMPORTANCE_DEFAULT)
+ bC.setConversationId("parent", "convo")
+ bC.setImportantConversation(true)
+ val b = NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_HIGH)
+ .setPkg("pkg2")
+ .setOpPkg("pkg2")
+ .setTag("tag")
+ .setNotification(bN)
+ .setChannel(bC)
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build()
+
+
+ assertEquals(
+ listOf(b, a),
+ rankingManager.updateRanking(null, listOf(a, b), "test"))
+ }
+
+ @Test
fun testSort_properlySetsAlertingBucket() {
val notif = Notification.Builder(mContext, "test") .build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
new file mode 100644
index 000000000000..7f48cd1313fe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationInlineImageResolverTest extends SysuiTestCase {
+
+ NotificationInlineImageResolver mResolver;
+ Bitmap mBitmap;
+ BitmapDrawable mBitmapDrawable;
+ Uri mUri;
+
+ @Before
+ public void setup() {
+ mResolver = spy(new NotificationInlineImageResolver(mContext, null));
+ mBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
+ mBitmapDrawable = new BitmapDrawable(mContext.getResources(), mBitmap);
+ mUri = mock(Uri.class);
+ }
+
+ @Test
+ public void refreshMaxImageSizes() {
+ assertNotEquals("Starts different height", mResolver.mMaxImageHeight, 20);
+ assertNotEquals("Starts different width", mResolver.mMaxImageWidth, 15);
+
+ doReturn(20).when(mResolver).getMaxImageHeight();
+ doReturn(15).when(mResolver).getMaxImageWidth();
+
+ mResolver.updateMaxImageSizes();
+
+ assertEquals("Height matches new config", mResolver.mMaxImageHeight, 20);
+ assertEquals("Width matches new config", mResolver.mMaxImageWidth, 15);
+ }
+
+ @Test
+ public void resolveImage_sizeTooBig() throws IOException {
+ doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri);
+ mResolver.mMaxImageHeight = 5;
+ mResolver.mMaxImageWidth = 5;
+
+ // original bitmap size is 10x10
+ BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri);
+ Bitmap resolvedBitmap = resolved.getBitmap();
+ assertEquals("Bitmap width reduced", 5, resolvedBitmap.getWidth());
+ assertEquals("Bitmap height reduced", 5, resolvedBitmap.getHeight());
+ assertNotSame("Bitmap replaced", resolvedBitmap, mBitmap);
+ }
+
+ @Test
+ public void resolveImage_sizeOK() throws IOException {
+ doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri);
+ mResolver.mMaxImageWidth = 15;
+ mResolver.mMaxImageHeight = 15;
+
+ // original bitmap size is 10x10
+ BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri);
+ Bitmap resolvedBitmap = resolved.getBitmap();
+ assertEquals("Bitmap width unchanged", 10, resolvedBitmap.getWidth());
+ assertEquals("Bitmap height unchanged", 10, resolvedBitmap.getHeight());
+ assertSame("Bitmap not replaced", resolvedBitmap, mBitmap);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e5ee439d4bb7..d81b8c2af246 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -50,6 +50,7 @@ import android.metrics.LogMaker;
import android.os.Binder;
import android.os.Handler;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -256,7 +257,8 @@ public class StatusBarTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationFilter.class, mNotificationFilter);
IPowerManager powerManagerService = mock(IPowerManager.class);
- mPowerManager = new PowerManager(mContext, powerManagerService,
+ IThermalService thermalService = mock(IThermalService.class);
+ mPowerManager = new PowerManager(mContext, powerManagerService, thermalService,
Handler.createAsync(Looper.myLooper()));
mNotificationInterruptionStateProvider =
diff --git a/services/Android.bp b/services/Android.bp
index 416f448a965f..c77e75da66ba 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -127,6 +127,16 @@ droidstubs {
api_file: "api/current.txt",
removed_api_file: "api/removed.txt",
},
+ last_released: {
+ api_file: ":last-released-system-server-api",
+ removed_api_file: "api/removed.txt",
+ baseline_file: ":system-server-api-incompatibilities-with-last-released"
+ },
+ api_lint: {
+ enabled: true,
+ new_since: ":last-released-system-server-api",
+ baseline_file: "api/lint-baseline.txt",
+ },
},
}
diff --git a/services/api/lint-baseline.txt b/services/api/lint-baseline.txt
new file mode 100644
index 000000000000..0b8658cf469d
--- /dev/null
+++ b/services/api/lint-baseline.txt
@@ -0,0 +1,35 @@
+// Baseline format: 1.0
+InternalClasses: com.android.permission.persistence.RuntimePermissionsPersistence:
+ Internal classes must not be exposed
+InternalClasses: com.android.permission.persistence.RuntimePermissionsState:
+ Internal classes must not be exposed
+InternalClasses: com.android.permission.persistence.RuntimePermissionsState.PermissionState:
+ Internal classes must not be exposed
+InternalClasses: com.android.role.persistence.RolesPersistence:
+ Internal classes must not be exposed
+InternalClasses: com.android.role.persistence.RolesState:
+ Internal classes must not be exposed
+InternalClasses: com.android.server.SystemService:
+ Internal classes must not be exposed
+InternalClasses: com.android.server.SystemService.TargetUser:
+ Internal classes must not be exposed
+
+
+ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder):
+ Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder)}
+ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder, boolean):
+ Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder,boolean)}
+
+
+UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#delete(android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `delete`
+UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#read(android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `read`
+UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#write(com.android.permission.persistence.RuntimePermissionsState, android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `write`
+UserHandleName: com.android.role.persistence.RolesPersistence#delete(android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `delete`
+UserHandleName: com.android.role.persistence.RolesPersistence#read(android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `read`
+UserHandleName: com.android.role.persistence.RolesPersistence#write(com.android.role.persistence.RolesState, android.os.UserHandle):
+ Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `write`
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 6852fbfd2487..5db5115b4afe 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -213,11 +213,6 @@ public class LocationManagerService extends ILocationManager.Stub {
private PackageManager mPackageManager;
private PowerManager mPowerManager;
- // TODO: sharing a location fudger with mock providers can leak information as the mock provider
- // can be used to retrieve offset information. the fudger should likely be reset whenever mock
- // providers are added or removed
- private LocationFudger mLocationFudger;
-
private GeofenceManager mGeofenceManager;
private GeocoderProxy mGeocodeProvider;
@@ -285,8 +280,6 @@ public class LocationManagerService extends ILocationManager.Stub {
mPackageManager = mContext.getPackageManager();
mAppOps = mContext.getSystemService(AppOpsManager.class);
mPowerManager = mContext.getSystemService(PowerManager.class);
-
- mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
mGeofenceManager = new GeofenceManager(mContext, mSettingsHelper);
PowerManagerInternal localPowerManager =
@@ -663,6 +656,8 @@ public class LocationManagerService extends ILocationManager.Stub {
private final String mName;
+ private final LocationFudger mLocationFudger;
+
// if the provider is enabled for a given user id - null or not present means unknown
@GuardedBy("mLock")
private final SparseArray<Boolean> mEnabled;
@@ -680,6 +675,7 @@ public class LocationManagerService extends ILocationManager.Stub {
private LocationProviderManager(String name) {
mName = name;
+ mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
mEnabled = new SparseArray<>(2);
mLastLocation = new SparseArray<>(2);
mLastCoarseLocation = new SparseArray<>(2);
@@ -704,7 +700,9 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
mProvider.setMockProvider(provider);
- // when removing a mock provider, also clear any mock last locations
+ // when removing a mock provider, also clear any mock last locations and reset the
+ // location fudger. the mock provider could have been used to infer the current
+ // location fudger offsets.
if (provider == null) {
for (int i = 0; i < mLastLocation.size(); i++) {
Location lastLocation = mLastLocation.valueAt(i);
@@ -719,6 +717,8 @@ public class LocationManagerService extends ILocationManager.Stub {
mLastCoarseLocation.setValueAt(i, null);
}
}
+
+ mLocationFudger.resetOffsets();
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index ac41434a1b5c..18adc0ba27ee 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import android.hardware.display.DeviceProductInfo;
import android.hardware.display.DisplayViewport;
import android.util.DisplayMetrics;
import android.view.Display;
@@ -288,6 +289,13 @@ final class DisplayDeviceInfo {
public DisplayAddress address;
/**
+ * Product-specific information about the display or the directly connected device on the
+ * display chain. For example, if the display is transitively connected, this field may contain
+ * product information about the intermediate device.
+ */
+ public DeviceProductInfo deviceProductInfo;
+
+ /**
* Display state.
*/
public int state = Display.STATE_ON;
@@ -360,6 +368,7 @@ final class DisplayDeviceInfo {
|| rotation != other.rotation
|| type != other.type
|| !Objects.equals(address, other.address)
+ || !Objects.equals(deviceProductInfo, other.deviceProductInfo)
|| ownerUid != other.ownerUid
|| !Objects.equals(ownerPackageName, other.ownerPackageName)) {
diff |= DIFF_OTHER;
@@ -396,6 +405,7 @@ final class DisplayDeviceInfo {
rotation = other.rotation;
type = other.type;
address = other.address;
+ deviceProductInfo = other.deviceProductInfo;
state = other.state;
ownerUid = other.ownerUid;
ownerPackageName = other.ownerPackageName;
@@ -429,6 +439,7 @@ final class DisplayDeviceInfo {
if (address != null) {
sb.append(", address ").append(address);
}
+ sb.append(", deviceProductInfo ").append(deviceProductInfo);
sb.append(", state ").append(Display.stateToString(state));
if (ownerUid != 0 || ownerPackageName != null) {
sb.append(", owner ").append(ownerPackageName);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4ebbddabd6db..e578ac1fcd42 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -513,6 +513,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f);
mInfo.xDpi = config.xDpi;
mInfo.yDpi = config.yDpi;
+ mInfo.deviceProductInfo = mDisplayInfo.deviceProductInfo;
// Assume that all built-in displays that have secure output (eg. HDCP) also
// support compositing from gralloc protected buffers.
@@ -891,8 +892,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
pw.println("mBacklight=" + mBacklight);
pw.println("mAllmSupported=" + mAllmSupported);
pw.println("mAllmRequested=" + mAllmRequested);
- pw.println("mGameContentTypeSupported" + mGameContentTypeSupported);
- pw.println("mGameContentTypeRequested" + mGameContentTypeRequested);
+ pw.println("mGameContentTypeSupported=" + mGameContentTypeSupported);
+ pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested);
pw.println("mDisplayInfo=" + mDisplayInfo);
pw.println("mDisplayConfigs=");
for (int i = 0; i < mDisplayConfigs.length; i++) {
@@ -902,14 +903,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
for (int i = 0; i < mSupportedModes.size(); i++) {
pw.println(" " + mSupportedModes.valueAt(i));
}
- pw.print("mSupportedColorModes=[");
- for (int i = 0; i < mSupportedColorModes.size(); i++) {
- if (i != 0) {
- pw.print(", ");
- }
- pw.print(mSupportedColorModes.get(i));
- }
- pw.println("]");
+ pw.print("mSupportedColorModes=" + mSupportedColorModes.toString());
}
private int findDisplayConfigIdLocked(int modeId) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 0c9445a05551..ac81a6c813f7 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -269,6 +269,7 @@ final class LogicalDisplay {
mBaseDisplayInfo.type = deviceInfo.type;
mBaseDisplayInfo.address = deviceInfo.address;
+ mBaseDisplayInfo.deviceProductInfo = deviceInfo.deviceProductInfo;
mBaseDisplayInfo.name = deviceInfo.name;
mBaseDisplayInfo.uniqueId = deviceInfo.uniqueId;
mBaseDisplayInfo.appWidth = maskedWidth;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 6174e5418caf..b84d3226362b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -77,7 +77,7 @@ final class HdmiCecController {
private static final int NUM_LOGICAL_ADDRESS = 16;
- private static final int MAX_CEC_MESSAGE_HISTORY = 200;
+ private static final int MAX_HDMI_MESSAGE_HISTORY = 250;
// Predicate for whether the given logical address is remote device's one or not.
private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
@@ -111,9 +111,9 @@ final class HdmiCecController {
// Stores the local CEC devices in the system. Device type is used for key.
private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
- // Stores recent CEC messages history for debugging purpose.
- private final ArrayBlockingQueue<MessageHistoryRecord> mMessageHistory =
- new ArrayBlockingQueue<>(MAX_CEC_MESSAGE_HISTORY);
+ // Stores recent CEC messages and HDMI Hotplug event history for debugging purpose.
+ private final ArrayBlockingQueue<Dumpable> mMessageHistory =
+ new ArrayBlockingQueue<>(MAX_HDMI_MESSAGE_HISTORY);
private final NativeWrapper mNativeWrapperImpl;
@@ -618,7 +618,7 @@ final class HdmiCecController {
void sendCommand(final HdmiCecMessage cecMessage,
final HdmiControlService.SendMessageCallback callback) {
assertRunOnServiceThread();
- addMessageToHistory(false /* isReceived */, cecMessage);
+ addCecMessageToHistory(false /* isReceived */, cecMessage);
runOnIoThread(new Runnable() {
@Override
public void run() {
@@ -658,7 +658,7 @@ final class HdmiCecController {
assertRunOnServiceThread();
HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
HdmiLogger.debug("[R]:" + command);
- addMessageToHistory(true /* isReceived */, command);
+ addCecMessageToHistory(true /* isReceived */, command);
onReceiveCommand(command);
}
@@ -669,16 +669,26 @@ final class HdmiCecController {
private void handleHotplug(int port, boolean connected) {
assertRunOnServiceThread();
HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);
+ addHotplugEventToHistory(port, connected);
mService.onHotplug(port, connected);
}
@ServiceThreadOnly
- private void addMessageToHistory(boolean isReceived, HdmiCecMessage message) {
+ private void addHotplugEventToHistory(int port, boolean connected) {
assertRunOnServiceThread();
- MessageHistoryRecord record = new MessageHistoryRecord(isReceived, message);
- if (!mMessageHistory.offer(record)) {
+ addEventToHistory(new HotplugHistoryRecord(port, connected));
+ }
+
+ @ServiceThreadOnly
+ private void addCecMessageToHistory(boolean isReceived, HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ addEventToHistory(new MessageHistoryRecord(isReceived, message));
+ }
+
+ private void addEventToHistory(Dumpable event) {
+ if (!mMessageHistory.offer(event)) {
mMessageHistory.poll();
- mMessageHistory.offer(record);
+ mMessageHistory.offer(event);
}
}
@@ -689,10 +699,11 @@ final class HdmiCecController {
mLocalDevices.valueAt(i).dump(pw);
pw.decreaseIndent();
}
+
pw.println("CEC message history:");
pw.increaseIndent();
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- for (MessageHistoryRecord record : mMessageHistory) {
+ for (Dumpable record : mMessageHistory) {
record.dump(pw, sdf);
}
pw.decreaseIndent();
@@ -792,17 +803,27 @@ final class HdmiCecController {
}
}
- private final class MessageHistoryRecord {
- private final long mTime;
+ private abstract static class Dumpable {
+ protected final long mTime;
+
+ Dumpable() {
+ mTime = System.currentTimeMillis();
+ }
+
+ abstract void dump(IndentingPrintWriter pw, SimpleDateFormat sdf);
+ }
+
+ private static final class MessageHistoryRecord extends Dumpable {
private final boolean mIsReceived; // true if received message and false if sent message
private final HdmiCecMessage mMessage;
- public MessageHistoryRecord(boolean isReceived, HdmiCecMessage message) {
- mTime = System.currentTimeMillis();
+ MessageHistoryRecord(boolean isReceived, HdmiCecMessage message) {
+ super();
mIsReceived = isReceived;
mMessage = message;
}
+ @Override
void dump(final IndentingPrintWriter pw, SimpleDateFormat sdf) {
pw.print(mIsReceived ? "[R]" : "[S]");
pw.print(" time=");
@@ -811,4 +832,26 @@ final class HdmiCecController {
pw.println(mMessage);
}
}
+
+ private static final class HotplugHistoryRecord extends Dumpable {
+ private final int mPort;
+ private final boolean mConnected;
+
+ HotplugHistoryRecord(int port, boolean connected) {
+ super();
+ mPort = port;
+ mConnected = connected;
+ }
+
+ @Override
+ void dump(final IndentingPrintWriter pw, SimpleDateFormat sdf) {
+ pw.print("[H]");
+ pw.print(" time=");
+ pw.print(sdf.format(new Date(mTime)));
+ pw.print(" hotplug port=");
+ pw.print(mPort);
+ pw.print(" connected=");
+ pw.println(mConnected);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java
index a069e7ace636..1f458ed4e29d 100644
--- a/services/core/java/com/android/server/location/LocationFudger.java
+++ b/services/core/java/com/android/server/location/LocationFudger.java
@@ -87,6 +87,13 @@ public class LocationFudger {
mRandom = random;
mAccuracyM = Math.max(accuracyM, MIN_ACCURACY_M);
+ resetOffsets();
+ }
+
+ /**
+ * Resets the random offsets completely.
+ */
+ public void resetOffsets() {
mLatitudeOffsetM = nextRandomOffset();
mLongitudeOffsetM = nextRandomOffset();
mNextUpdateRealtimeMs = mClock.millis() + OFFSET_UPDATE_INTERVAL_MS;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 377fd16d4e19..12f7d5c27459 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2773,6 +2773,13 @@ public class ShortcutService extends IShortcutService.Stub {
userId, /* doCache= */ false);
}
+ @Override
+ public List<ShortcutManager.ShareShortcutInfo> getShareTargets(
+ @NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId) {
+ return ShortcutService.this.getShareTargets(
+ callingPackage, intentFilter, userId).getList();
+ }
+
private void updateCachedShortcutsInternal(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@NonNull List<String> shortcutIds, int userId, boolean doCache) {
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 491c5ab2ac03..da3cbf9d03b4 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -16,15 +16,18 @@
package com.android.server.power;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.thermal.V1_0.ThermalStatus;
import android.hardware.thermal.V1_0.ThermalStatusCode;
import android.hardware.thermal.V1_1.IThermalCallback;
import android.hardware.thermal.V2_0.IThermalChangedCallback;
+import android.hardware.thermal.V2_0.TemperatureThreshold;
import android.hardware.thermal.V2_0.ThrottlingSeverity;
import android.os.Binder;
import android.os.CoolingDevice;
+import android.os.Handler;
import android.os.HwBinder;
import android.os.IThermalEventListener;
import android.os.IThermalService;
@@ -36,6 +39,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
+import android.os.SystemClock;
import android.os.Temperature;
import android.util.ArrayMap;
import android.util.EventLog;
@@ -43,6 +47,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
@@ -54,6 +59,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -100,6 +106,9 @@ public class ThermalManagerService extends SystemService {
/** Hal ready. */
private final AtomicBoolean mHalReady = new AtomicBoolean();
+ /** Watches temperatures to forecast when throttling will occur */
+ private final TemperatureWatcher mTemperatureWatcher = new TemperatureWatcher();
+
/** Invalid throttling status */
private static final int INVALID_THROTTLING = Integer.MIN_VALUE;
@@ -154,6 +163,7 @@ public class ThermalManagerService extends SystemService {
onTemperatureChanged(temperatures.get(i), false);
}
onTemperatureMapChangedLocked();
+ mTemperatureWatcher.updateSevereThresholds();
mHalReady.set(true);
}
}
@@ -462,6 +472,15 @@ public class ThermalManagerService extends SystemService {
}
}
+ @Override
+ public float getThermalHeadroom(int forecastSeconds) {
+ if (!mHalReady.get()) {
+ return Float.NaN;
+ }
+
+ return mTemperatureWatcher.getForecast(forecastSeconds);
+ }
+
private void dumpItemsLocked(PrintWriter pw, String prefix,
Collection<?> items) {
for (Iterator iterator = items.iterator(); iterator.hasNext();) {
@@ -616,6 +635,10 @@ public class ThermalManagerService extends SystemService {
protected abstract List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
int type);
+ @NonNull
+ protected abstract List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type);
+
protected abstract boolean connectToHal();
protected abstract void dump(PrintWriter pw, String prefix);
@@ -728,6 +751,12 @@ public class ThermalManagerService extends SystemService {
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
@@ -857,6 +886,12 @@ public class ThermalManagerService extends SystemService {
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
@@ -975,6 +1010,32 @@ public class ThermalManagerService extends SystemService {
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ synchronized (mHalLock) {
+ List<TemperatureThreshold> ret = new ArrayList<>();
+ if (mThermalHal20 == null) {
+ return ret;
+ }
+ try {
+ mThermalHal20.getTemperatureThresholds(shouldFilter, type,
+ (status, thresholds) -> {
+ if (ThermalStatusCode.SUCCESS == status.code) {
+ ret.addAll(thresholds);
+ } else {
+ Slog.e(TAG,
+ "Couldn't get temperature thresholds because of HAL "
+ + "error: " + status.debugMessage);
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't getTemperatureThresholds, reconnecting...", e);
+ }
+ return ret;
+ }
+ }
+
+ @Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
@@ -1001,4 +1062,190 @@ public class ThermalManagerService extends SystemService {
}
}
+ private class TemperatureWatcher {
+ private final Handler mHandler = BackgroundThread.getHandler();
+
+ /** Map of skin temperature sensor name to a corresponding list of samples */
+ @GuardedBy("mSamples")
+ private final ArrayMap<String, ArrayList<Sample>> mSamples = new ArrayMap<>();
+
+ /** Map of skin temperature sensor name to the corresponding SEVERE temperature threshold */
+ @GuardedBy("mSamples")
+ private ArrayMap<String, Float> mSevereThresholds = new ArrayMap<>();
+
+ @GuardedBy("mSamples")
+ private long mLastForecastCallTimeMillis = 0;
+
+ void updateSevereThresholds() {
+ synchronized (mSamples) {
+ List<TemperatureThreshold> thresholds =
+ mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN);
+ for (int t = 0; t < thresholds.size(); ++t) {
+ TemperatureThreshold threshold = thresholds.get(t);
+ if (threshold.hotThrottlingThresholds.length <= ThrottlingSeverity.SEVERE) {
+ continue;
+ }
+ float temperature =
+ threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE];
+ if (!Float.isNaN(temperature)) {
+ mSevereThresholds.put(threshold.name,
+ threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE]);
+ }
+ }
+ }
+ }
+
+ private static final int INACTIVITY_THRESHOLD_MILLIS = 10000;
+ private static final int RING_BUFFER_SIZE = 30;
+
+ private void updateTemperature() {
+ synchronized (mSamples) {
+ if (SystemClock.elapsedRealtime() - mLastForecastCallTimeMillis
+ < INACTIVITY_THRESHOLD_MILLIS) {
+ // Trigger this again after a second as long as forecast has been called more
+ // recently than the inactivity timeout
+ mHandler.postDelayed(this::updateTemperature, 1000);
+ } else {
+ // Otherwise, we've been idle for at least 10 seconds, so we should
+ // shut down
+ mSamples.clear();
+ return;
+ }
+
+ long now = SystemClock.elapsedRealtime();
+ List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(true,
+ Temperature.TYPE_SKIN);
+
+ for (int t = 0; t < temperatures.size(); ++t) {
+ Temperature temperature = temperatures.get(t);
+
+ // Filter out invalid temperatures. If this results in no values being stored at
+ // all, the mSamples.empty() check in getForecast() will catch it.
+ if (Float.isNaN(temperature.getValue())) {
+ continue;
+ }
+
+ ArrayList<Sample> samples = mSamples.computeIfAbsent(temperature.getName(),
+ k -> new ArrayList<>(RING_BUFFER_SIZE));
+ if (samples.size() == RING_BUFFER_SIZE) {
+ samples.remove(0);
+ }
+ samples.add(new Sample(now, temperature.getValue()));
+ }
+ }
+ }
+
+ /**
+ * Calculates the trend using a linear regression. As the samples are degrees Celsius with
+ * associated timestamps in milliseconds, the slope is in degrees Celsius per millisecond.
+ */
+ private float getSlopeOf(List<Sample> samples) {
+ long sumTimes = 0L;
+ float sumTemperatures = 0.0f;
+ for (int s = 0; s < samples.size(); ++s) {
+ Sample sample = samples.get(s);
+ sumTimes += sample.time;
+ sumTemperatures += sample.temperature;
+ }
+ long meanTime = sumTimes / samples.size();
+ float meanTemperature = sumTemperatures / samples.size();
+
+ long sampleVariance = 0L;
+ float sampleCovariance = 0.0f;
+ for (int s = 0; s < samples.size(); ++s) {
+ Sample sample = samples.get(s);
+ long timeDelta = sample.time - meanTime;
+ float temperatureDelta = sample.temperature - meanTemperature;
+ sampleVariance += timeDelta * timeDelta;
+ sampleCovariance += timeDelta * temperatureDelta;
+ }
+
+ return sampleCovariance / sampleVariance;
+ }
+
+ /**
+ * Used to determine the temperature corresponding to 0.0. Given that 1.0 is pinned at the
+ * temperature corresponding to the SEVERE threshold, we set 0.0 to be that temperature
+ * minus DEGREES_BETWEEN_ZERO_AND_ONE.
+ */
+ private static final float DEGREES_BETWEEN_ZERO_AND_ONE = 30.0f;
+
+ private float normalizeTemperature(float temperature, float severeThreshold) {
+ synchronized (mSamples) {
+ float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE;
+ if (temperature <= zeroNormalized) {
+ return 0.0f;
+ }
+ float delta = temperature - zeroNormalized;
+ return delta / DEGREES_BETWEEN_ZERO_AND_ONE;
+ }
+ }
+
+ private static final int MINIMUM_SAMPLE_COUNT = 3;
+
+ float getForecast(int forecastSeconds) {
+ synchronized (mSamples) {
+ mLastForecastCallTimeMillis = System.currentTimeMillis();
+ if (mSamples.isEmpty()) {
+ updateTemperature();
+ }
+
+ // If somehow things take much longer than expected or there are no temperatures
+ // to sample, return early
+ if (mSamples.isEmpty()) {
+ Slog.e(TAG, "No temperature samples found");
+ return Float.NaN;
+ }
+
+ // If we don't have any thresholds, we can't normalize the temperatures,
+ // so return early
+ if (mSevereThresholds.isEmpty()) {
+ Slog.e(TAG, "No temperature thresholds found");
+ return Float.NaN;
+ }
+
+ float maxNormalized = Float.NaN;
+ for (Map.Entry<String, ArrayList<Sample>> entry : mSamples.entrySet()) {
+ String name = entry.getKey();
+ ArrayList<Sample> samples = entry.getValue();
+
+ Float threshold = mSevereThresholds.get(name);
+ if (threshold == null) {
+ Slog.e(TAG, "No threshold found for " + name);
+ continue;
+ }
+
+ float currentTemperature = samples.get(0).temperature;
+
+ if (samples.size() < MINIMUM_SAMPLE_COUNT) {
+ // Don't try to forecast, just use the latest one we have
+ float normalized = normalizeTemperature(currentTemperature, threshold);
+ if (Float.isNaN(maxNormalized) || normalized > maxNormalized) {
+ maxNormalized = normalized;
+ }
+ continue;
+ }
+
+ float slope = getSlopeOf(samples);
+ float normalized = normalizeTemperature(
+ currentTemperature + slope * forecastSeconds * 1000, threshold);
+ if (Float.isNaN(maxNormalized) || normalized > maxNormalized) {
+ maxNormalized = normalized;
+ }
+ }
+
+ return maxNormalized;
+ }
+ }
+
+ private class Sample {
+ public long time;
+ public float temperature;
+
+ Sample(long time, float temperature) {
+ this.time = time;
+ this.temperature = temperature;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 840abb12bb13..1b21117d6145 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
@@ -84,9 +83,6 @@ import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
-import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
-import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -105,12 +101,15 @@ import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
import static com.android.server.wm.DisplayContentProto.DOCKED_STACK_DIVIDER_CONTROLLER;
import static com.android.server.wm.DisplayContentProto.DPI;
import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
+import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
import static com.android.server.wm.DisplayContentProto.ID;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
+import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
import static com.android.server.wm.DisplayContentProto.TASKS;
import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
@@ -1553,6 +1552,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
/**
+ * If the provided {@link ActivityRecord} can be displayed in an orientation different from the
+ * display's, it will be rotated to match its requested orientation.
+ *
+ * @see #rotationForActivityInDifferentOrientation(ActivityRecord).
+ * @see WindowToken#applyFixedRotationTransform(DisplayInfo, DisplayFrames, Configuration)
+ */
+ void rotateInDifferentOrientationIfNeeded(ActivityRecord activityRecord) {
+ int rotation = rotationForActivityInDifferentOrientation(activityRecord);
+ if (rotation != NO_ROTATION) {
+ startFixedRotationTransform(activityRecord, rotation);
+ }
+ }
+
+ /**
* Update rotation of the display.
*
* @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 251d0f1105d5..53dd32633a2f 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -34,6 +34,7 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.app.ActivityManager.TaskSnapshot;
import android.app.WindowConfiguration;
import android.graphics.Point;
@@ -423,6 +424,11 @@ public class RecentsAnimationController implements DeathRecipient {
mService.mWindowPlacerLocked.performSurfacePlacement();
+ // If the target activity has a fixed orientation which is different from the current top
+ // activity, it will be rotated before being shown so we avoid a screen rotation
+ // animation when showing the Recents view.
+ mDisplayContent.rotateInDifferentOrientationIfNeeded(mTargetActivityRecord);
+
// Notify that the animation has started
if (mStatusBar != null) {
mStatusBar.onRecentsAnimationStateChanged(true /* running */);
@@ -695,6 +701,9 @@ public class RecentsAnimationController implements DeathRecipient {
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(
mTargetActivityRecord.token);
}
+ if (mTargetActivityRecord.hasFixedRotationTransform()) {
+ mTargetActivityRecord.clearFixedRotationTransform();
+ }
}
// Notify that the animation has ended
@@ -828,6 +837,19 @@ public class RecentsAnimationController implements DeathRecipient {
return task != null && isAnimatingTask(task) && !isTargetApp(windowState.mActivityRecord);
}
+ /**
+ * If the animation target ActivityRecord has a fixed rotation ({@link
+ * WindowToken#hasFixedRotationTransform()}, the provided wallpaper will be rotated accordingly.
+ *
+ * This avoids any screen rotation animation when animating to the Recents view.
+ */
+ void applyFixedRotationTransformIfNeeded(@NonNull WindowToken wallpaper) {
+ if (mTargetActivityRecord == null) {
+ return;
+ }
+ wallpaper.applyFixedRotationTransform(mTargetActivityRecord);
+ }
+
@VisibleForTesting
class TaskAnimationAdapter implements AnimationAdapter {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index d23bf978cbab..1e22141f232a 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -122,10 +122,37 @@ class WallpaperWindowToken extends WindowToken {
mDisplayContent.setLayoutNeeded();
}
- final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+ final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+
+ if (visible) {
+ final WindowState wallpaperTarget = wallpaperController.getWallpaperTarget();
+ final RecentsAnimationController recentsAnimationController =
+ mWmService.getRecentsAnimationController();
+ if (wallpaperTarget != null
+ && recentsAnimationController != null
+ && recentsAnimationController.isAnimatingTask(wallpaperTarget.getTask())) {
+ // If the Recents animation is running, and the wallpaper target is the animating
+ // task we want the wallpaper to be rotated in the same orientation as the
+ // RecentsAnimation's target (e.g the launcher)
+ recentsAnimationController.applyFixedRotationTransformIfNeeded(this);
+ } else if (wallpaperTarget != null
+ && wallpaperTarget.mToken.hasFixedRotationTransform()) {
+ // If the wallpaper target has a fixed rotation, we want the wallpaper to follow its
+ // rotation
+ applyFixedRotationTransform(wallpaperTarget.mToken);
+ } else if (hasFixedRotationTransform()) {
+ clearFixedRotationTransform();
+ }
+ }
+
+ DisplayInfo displayInfo = getFixedRotationTransformDisplayInfo();
+ if (displayInfo == null) {
+ displayInfo = mDisplayContent.getDisplayInfo();
+ }
+
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
- final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+
for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
final WindowState wallpaper = mChildren.get(wallpaperNdx);
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 118056631cb1..48c7812afec0 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -377,6 +377,19 @@ class WindowToken extends WindowContainer<WindowState> {
onConfigurationChanged(getParent().getConfiguration());
}
+ /**
+ * Copies the {@link FixedRotationTransformState} (if any) from the other WindowToken to this
+ * one.
+ */
+ void applyFixedRotationTransform(WindowToken other) {
+ final FixedRotationTransformState fixedRotationState = other.mFixedRotationTransformState;
+ if (fixedRotationState != null) {
+ applyFixedRotationTransform(fixedRotationState.mDisplayInfo,
+ fixedRotationState.mDisplayFrames,
+ fixedRotationState.mRotatedOverrideConfiguration);
+ }
+ }
+
/** Clears the transformation and continue updating the orientation change of display. */
void clearFixedRotationTransform() {
if (mFixedRotationTransformState == null) {
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 663bf4f30708..2499614a3738 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -88,7 +88,7 @@ public class PeopleService extends SystemService {
@Override
public void onCreatePredictionSession(AppPredictionContext context,
AppPredictionSessionId sessionId) {
- mSessions.put(sessionId, new SessionInfo(context, mDataManager));
+ mSessions.put(sessionId, new SessionInfo(context, mDataManager, sessionId.getUserId()));
}
@Override
diff --git a/services/people/java/com/android/server/people/SessionInfo.java b/services/people/java/com/android/server/people/SessionInfo.java
index eaa0781f12ef..28612f1dd49b 100644
--- a/services/people/java/com/android/server/people/SessionInfo.java
+++ b/services/people/java/com/android/server/people/SessionInfo.java
@@ -16,6 +16,7 @@
package com.android.server.people;
+import android.annotation.UserIdInt;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppTarget;
import android.app.prediction.IPredictionCallback;
@@ -38,9 +39,10 @@ class SessionInfo {
private final RemoteCallbackList<IPredictionCallback> mCallbacks =
new RemoteCallbackList<>();
- SessionInfo(AppPredictionContext predictionContext, DataManager dataManager) {
+ SessionInfo(AppPredictionContext predictionContext, DataManager dataManager,
+ @UserIdInt int callingUserId) {
mAppTargetPredictor = AppTargetPredictor.create(predictionContext,
- this::updatePredictions, dataManager);
+ this::updatePredictions, dataManager, callingUserId);
}
void addCallback(IPredictionCallback callback) {
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index dd9cbd00f6a2..6b97c98b0029 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -34,13 +34,11 @@ import android.content.IntentFilter;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.net.Uri;
-import android.os.Binder;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Process;
@@ -83,7 +81,6 @@ import java.util.function.Function;
*/
public class DataManager {
- private static final String PLATFORM_PACKAGE_NAME = "android";
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
private static final long QUERY_EVENTS_MAX_AGE_MS = DateUtils.DAY_IN_MILLIS;
@@ -106,7 +103,6 @@ public class DataManager {
private ShortcutServiceInternal mShortcutServiceInternal;
private PackageManagerInternal mPackageManagerInternal;
- private ShortcutManager mShortcutManager;
private UserManager mUserManager;
public DataManager(Context context) {
@@ -125,7 +121,6 @@ public class DataManager {
public void initialize() {
mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
- mShortcutManager = mContext.getSystemService(ShortcutManager.class);
mUserManager = mContext.getSystemService(UserManager.class);
mShortcutServiceInternal.addListener(new ShortcutServiceListener());
@@ -171,8 +166,7 @@ public class DataManager {
mNotificationListeners.put(userId, notificationListener);
try {
notificationListener.registerAsSystemService(mContext,
- new ComponentName(PLATFORM_PACKAGE_NAME, getClass().getCanonicalName()),
- userId);
+ new ComponentName(mContext, getClass()), userId);
} catch (RemoteException e) {
// Should never occur for local calls.
}
@@ -242,8 +236,8 @@ public class DataManager {
* Iterates through all the {@link PackageData}s owned by the unlocked users who are in the
* same profile group as the calling user.
*/
- public void forAllPackages(Consumer<PackageData> consumer) {
- List<UserInfo> users = mUserManager.getEnabledProfiles(mInjector.getCallingUserId());
+ void forPackagesInProfile(@UserIdInt int callingUserId, Consumer<PackageData> consumer) {
+ List<UserInfo> users = mUserManager.getEnabledProfiles(callingUserId);
for (UserInfo userInfo : users) {
UserData userData = getUnlockedUserData(userInfo.id);
if (userData != null) {
@@ -275,8 +269,10 @@ public class DataManager {
* Gets the {@link ShareShortcutInfo}s from all packages owned by the calling user that match
* the specified {@link IntentFilter}.
*/
- public List<ShareShortcutInfo> getShareShortcuts(@NonNull IntentFilter intentFilter) {
- return mShortcutManager.getShareTargets(intentFilter);
+ public List<ShareShortcutInfo> getShareShortcuts(@NonNull IntentFilter intentFilter,
+ @UserIdInt int callingUserId) {
+ return mShortcutServiceInternal.getShareTargets(
+ mContext.getPackageName(), intentFilter, callingUserId);
}
/** Reports the {@link AppTargetEvent} from App Prediction Manager. */
@@ -361,7 +357,7 @@ public class DataManager {
@ShortcutQuery.QueryFlags int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC
| ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
return mShortcutServiceInternal.getShortcuts(
- mInjector.getCallingUserId(), /*callingPackage=*/ PLATFORM_PACKAGE_NAME,
+ UserHandle.USER_SYSTEM, mContext.getPackageName(),
/*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null,
/*componentName=*/ null, queryFlags, userId, MY_PID, MY_UID);
}
@@ -775,7 +771,7 @@ public class DataManager {
@Override
public void onReceive(Context context, Intent intent) {
- forAllPackages(PackageData::saveToDisk);
+ forAllUnlockedUsers(userData -> userData.forAllPackages(PackageData::saveToDisk));
}
}
@@ -809,9 +805,5 @@ public class DataManager {
Function<String, PackageData> packageDataGetter) {
return new UsageStatsQueryHelper(userId, packageDataGetter);
}
-
- int getCallingUserId() {
- return Binder.getCallingUserHandle().getIdentifier();
- }
}
}
diff --git a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
index 44f3e35833d9..19cf8af5d66b 100644
--- a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
@@ -18,6 +18,7 @@ package com.android.server.people.prediction;
import android.annotation.MainThread;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppTarget;
@@ -42,25 +43,28 @@ public class AppTargetPredictor {
/** Creates a {@link AppTargetPredictor} instance based on the prediction context. */
public static AppTargetPredictor create(@NonNull AppPredictionContext predictionContext,
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
- @NonNull DataManager dataManager) {
+ @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
if (UI_SURFACE_SHARE.equals(predictionContext.getUiSurface())) {
return new ShareTargetPredictor(
- predictionContext, updatePredictionsMethod, dataManager);
+ predictionContext, updatePredictionsMethod, dataManager, callingUserId);
}
- return new AppTargetPredictor(predictionContext, updatePredictionsMethod, dataManager);
+ return new AppTargetPredictor(
+ predictionContext, updatePredictionsMethod, dataManager, callingUserId);
}
private final AppPredictionContext mPredictionContext;
private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
private final DataManager mDataManager;
+ final int mCallingUserId;
private final ExecutorService mCallbackExecutor;
AppTargetPredictor(@NonNull AppPredictionContext predictionContext,
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
- @NonNull DataManager dataManager) {
+ @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
mPredictionContext = predictionContext;
mUpdatePredictionsMethod = updatePredictionsMethod;
mDataManager = dataManager;
+ mCallingUserId = callingUserId;
mCallbackExecutor = Executors.newSingleThreadExecutor();
}
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 280ced3a07c5..90d821641149 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -19,6 +19,7 @@ package com.android.server.people.prediction;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppTarget;
@@ -45,8 +46,8 @@ class ShareTargetPredictor extends AppTargetPredictor {
ShareTargetPredictor(@NonNull AppPredictionContext predictionContext,
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
- @NonNull DataManager dataManager) {
- super(predictionContext, updatePredictionsMethod, dataManager);
+ @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
+ super(predictionContext, updatePredictionsMethod, dataManager, callingUserId);
mIntentFilter = predictionContext.getExtras().getParcelable(
ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
}
@@ -84,7 +85,7 @@ class ShareTargetPredictor extends AppTargetPredictor {
List<ShareTarget> getShareTargets() {
List<ShareTarget> shareTargets = new ArrayList<>();
List<ShareShortcutInfo> shareShortcuts =
- getDataManager().getShareShortcuts(mIntentFilter);
+ getDataManager().getShareShortcuts(mIntentFilter, mCallingUserId);
for (ShareShortcutInfo shareShortcut : shareShortcuts) {
ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
index 2a78b6f6ca24..2c84f2603b5c 100644
--- a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
@@ -26,6 +26,7 @@ import android.content.Intent;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.OsProtoEnums;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -52,13 +53,14 @@ import org.mockito.MockitoAnnotations;
public class CachedDeviceStateServiceTest {
@Mock private BatteryManagerInternal mBatteryManager;
@Mock private IPowerManager mPowerManager;
+ @Mock private IThermalService mThermalService;
private BroadcastInterceptingContext mContext;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getContext();
- PowerManager powerManager = new PowerManager(context, mPowerManager, null);
+ PowerManager powerManager = new PowerManager(context, mPowerManager, mThermalService, null);
mContext = new BroadcastInterceptingContext(context) {
@Override
public Object getSystemService(String name) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 69ca643f1abf..ae8d5545e069 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -74,6 +74,7 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallback;
@@ -147,6 +148,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Mock private Context mMockContext;
@Mock private IPowerManager mMockIPowerManager;
+ @Mock private IThermalService mMockIThermalService;
@Mock private PackageManager mMockPackageManager;
@Spy private AccessibilityServiceInfo mSpyServiceInfo = new AccessibilityServiceInfo();
@Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
@@ -174,7 +176,7 @@ public class AbstractAccessibilityServiceConnectionTest {
.thenReturn(mMockMagnificationController);
PowerManager powerManager =
- new PowerManager(mMockContext, mMockIPowerManager, mHandler);
+ new PowerManager(mMockContext, mMockIPowerManager, mMockIThermalService, mHandler);
when(mMockContext.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.hasSystemFeature(FEATURE_FINGERPRINT)).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index 41235560dc91..85b8fcbbcc61 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -34,6 +34,7 @@ import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import android.content.Context;
import android.os.Handler;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -73,6 +74,7 @@ public class KeyEventDispatcherTest {
private KeyEventFilter mKeyEventFilter1;
private KeyEventFilter mKeyEventFilter2;
private IPowerManager mMockPowerManagerService;
+ private IThermalService mMockThermalService;
private MessageCapturingHandler mMessageCapturingHandler;
private ArgumentCaptor<Integer> mFilter1SequenceCaptor = ArgumentCaptor.forClass(Integer.class);
private ArgumentCaptor<Integer> mFilter2SequenceCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -82,10 +84,12 @@ public class KeyEventDispatcherTest {
Looper looper = InstrumentationRegistry.getContext().getMainLooper();
mInputEventsHandler = new MessageCapturingHandler(looper, null);
mMockPowerManagerService = mock(IPowerManager.class);
+ mMockThermalService = mock(IThermalService.class);
// TODO: It would be better to mock PowerManager rather than its binder, but the class is
// final.
PowerManager powerManager =
- new PowerManager(mock(Context.class), mMockPowerManagerService, new Handler(looper));
+ new PowerManager(mock(Context.class), mMockPowerManagerService, mMockThermalService,
+ new Handler(looper));
mMessageCapturingHandler = new MessageCapturingHandler(looper, null);
mKeyEventDispatcher = new KeyEventDispatcher(mInputEventsHandler, SEND_FRAMEWORK_KEY_EVENT,
mLock, powerManager, mMessageCapturingHandler);
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index e90cb4641752..ac0cac14be7b 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -37,6 +37,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.PowerManager;
import android.os.RemoteException;
import android.provider.DeviceConfig;
@@ -74,6 +75,8 @@ public class AttentionManagerServiceTest {
@Mock
private IPowerManager mMockIPowerManager;
@Mock
+ private IThermalService mMockIThermalService;
+ @Mock
Context mContext;
@Before
@@ -84,7 +87,7 @@ public class AttentionManagerServiceTest {
// setup power manager mock
PowerManager mPowerManager;
doReturn(true).when(mMockIPowerManager).isInteractive();
- mPowerManager = new PowerManager(mContext, mMockIPowerManager, null);
+ mPowerManager = new PowerManager(mContext, mMockIPowerManager, mMockIThermalService, null);
Object mLock = new Object();
// setup a spy on attention manager
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 9e98427db709..fa19814f401f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -34,6 +34,7 @@ import android.content.ContextWrapper;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -123,6 +124,7 @@ public class HdmiControlServiceTest {
private HdmiPortInfo[] mHdmiPortInfo;
@Mock private IPowerManager mIPowerManagerMock;
+ @Mock private IThermalService mIThermalServiceMock;
@Before
public void setUp() throws Exception {
@@ -130,7 +132,8 @@ public class HdmiControlServiceTest {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, null);
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, null);
when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 3ecd3193058a..5e104a5210d7 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -54,7 +54,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.content.pm.parsing.AndroidPackage;
@@ -87,6 +86,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -110,7 +110,6 @@ public final class DataManagerTest {
@Mock private ShortcutServiceInternal mShortcutServiceInternal;
@Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock private PackageManagerInternal mPackageManagerInternal;
- @Mock private ShortcutManager mShortcutManager;
@Mock private UserManager mUserManager;
@Mock private TelephonyManager mTelephonyManager;
@Mock private TelecomManager mTelecomManager;
@@ -123,7 +122,6 @@ public final class DataManagerTest {
private NotificationChannel mNotificationChannel;
private DataManager mDataManager;
- private int mCallingUserId;
private CancellationSignal mCancellationSignal;
private TestInjector mInjector;
@@ -145,14 +143,11 @@ public final class DataManagerTest {
}).when(mPackageManagerInternal).forEachInstalledPackage(any(Consumer.class), anyInt());
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ when(mContext.getPackageName()).thenReturn("android");
Context originalContext = getInstrumentation().getTargetContext();
when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
- when(mContext.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(mShortcutManager);
- when(mContext.getSystemServiceName(ShortcutManager.class)).thenReturn(
- Context.SHORTCUT_SERVICE);
-
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mContext.getSystemServiceName(UserManager.class)).thenReturn(
Context.USER_SERVICE);
@@ -191,8 +186,6 @@ public final class DataManagerTest {
NOTIFICATION_CHANNEL_ID, "test channel", NotificationManager.IMPORTANCE_DEFAULT);
mNotificationChannel.setConversationId("test", TEST_SHORTCUT_ID);
- mCallingUserId = USER_ID_PRIMARY;
-
mCancellationSignal = new CancellationSignal();
mInjector = new TestInjector();
@@ -222,9 +215,7 @@ public final class DataManagerTest {
mDataManager.onShortcutAddedOrUpdated(
buildShortcutInfo("pkg_3", USER_ID_SECONDARY, "sc_3", buildPerson()));
- List<ConversationInfo> conversations = new ArrayList<>();
- mDataManager.forAllPackages(
- packageData -> packageData.forAllConversations(conversations::add));
+ List<ConversationInfo> conversations = getConversationsInPrimary();
// USER_ID_SECONDARY is not in the same profile group as USER_ID_PRIMARY.
assertEquals(2, conversations.size());
@@ -250,18 +241,14 @@ public final class DataManagerTest {
mDataManager.onShortcutAddedOrUpdated(
buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2", buildPerson()));
- List<ConversationInfo> conversations = new ArrayList<>();
- mDataManager.forAllPackages(
- packageData -> packageData.forAllConversations(conversations::add));
+ List<ConversationInfo> conversations = getConversationsInPrimary();
// USER_ID_PRIMARY_MANAGED is not locked, so only USER_ID_PRIMARY's conversation is stored.
assertEquals(1, conversations.size());
assertEquals("sc_1", conversations.get(0).getShortcutId());
mDataManager.onUserStopped(USER_ID_PRIMARY);
- conversations.clear();
- mDataManager.forAllPackages(
- packageData -> packageData.forAllConversations(conversations::add));
+ conversations = getConversationsInPrimary();
assertTrue(conversations.isEmpty());
}
@@ -289,12 +276,8 @@ public final class DataManagerTest {
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
mDataManager.reportAppTargetEvent(appTargetEvent, intentFilter);
- List<Range<Long>> activeShareTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeShareTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.TYPE_SHARE_IMAGE)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
+ Event.SHARE_EVENT_TYPES);
assertEquals(1, activeShareTimeSlots.size());
}
@@ -315,9 +298,7 @@ public final class DataManagerTest {
USER_ID_PRIMARY);
contentObserver.onChange(false, ContactsContract.Contacts.CONTENT_URI, USER_ID_PRIMARY);
- List<ConversationInfo> conversations = new ArrayList<>();
- mDataManager.forAllPackages(
- packageData -> packageData.forAllConversations(conversations::add));
+ List<ConversationInfo> conversations = getConversationsInPrimary();
assertEquals(1, conversations.size());
assertEquals(TEST_SHORTCUT_ID, conversations.get(0).getShortcutId());
@@ -338,12 +319,8 @@ public final class DataManagerTest {
listenerService.onNotificationPosted(mStatusBarNotification);
- List<Range<Long>> activeNotificationOpenTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeNotificationOpenTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.TYPE_NOTIFICATION_POSTED)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeNotificationOpenTimeSlots = getActiveSlotsForTestShortcut(
+ Event.NOTIFICATION_EVENT_TYPES);
assertEquals(1, activeNotificationOpenTimeSlots.size());
}
@@ -361,12 +338,8 @@ public final class DataManagerTest {
listenerService.onNotificationRemoved(mStatusBarNotification, null,
NotificationListenerService.REASON_CLICK);
- List<Range<Long>> activeNotificationOpenTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeNotificationOpenTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.TYPE_NOTIFICATION_OPENED)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeNotificationOpenTimeSlots = getActiveSlotsForTestShortcut(
+ Event.NOTIFICATION_EVENT_TYPES);
assertEquals(1, activeNotificationOpenTimeSlots.size());
}
@@ -469,12 +442,7 @@ public final class DataManagerTest {
mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
new Event(currentTimestamp - MILLIS_PER_MINUTE * 5L, Event.TYPE_CALL_MISSED));
- List<Range<Long>> activeTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.CALL_EVENT_TYPES)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
assertEquals(3, activeTimeSlots.size());
}
@@ -498,12 +466,7 @@ public final class DataManagerTest {
mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER, outgoingSmsEvent);
mInjector.mSmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER, incomingSmsEvent);
- List<Range<Long>> activeTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.SMS_EVENT_TYPES)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
assertEquals(2, activeTimeSlots.size());
}
@@ -551,22 +514,12 @@ public final class DataManagerTest {
mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_CALL_OUTGOING));
- List<Range<Long>> activeTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.CALL_EVENT_TYPES)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
assertEquals(1, activeTimeSlots.size());
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultDialer(null);
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
- activeTimeSlots.clear();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.CALL_EVENT_TYPES)
- .getActiveTimeSlots()));
+ activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
assertTrue(activeTimeSlots.isEmpty());
}
@@ -583,22 +536,12 @@ public final class DataManagerTest {
mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_SMS_OUTGOING));
- List<Range<Long>> activeTimeSlots = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.SMS_EVENT_TYPES)
- .getActiveTimeSlots()));
+ List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
assertEquals(1, activeTimeSlots.size());
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(null);
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
- activeTimeSlots.clear();
- mDataManager.forAllPackages(packageData ->
- activeTimeSlots.addAll(
- packageData.getEventHistory(TEST_SHORTCUT_ID)
- .getEventIndex(Event.SMS_EVENT_TYPES)
- .getActiveTimeSlots()));
+ activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
assertTrue(activeTimeSlots.isEmpty());
}
@@ -607,6 +550,24 @@ public final class DataManagerTest {
LocalServices.addService(clazz, mock);
}
+ private List<ConversationInfo> getConversationsInPrimary() {
+ List<ConversationInfo> conversations = new ArrayList<>();
+ mDataManager.forPackagesInProfile(USER_ID_PRIMARY,
+ packageData -> packageData.forAllConversations(conversations::add));
+ return conversations;
+ }
+
+ private List<Range<Long>> getActiveSlotsForTestShortcut(
+ Set<Integer> eventTypes) {
+ List<Range<Long>> activeSlots = new ArrayList<>();
+ mDataManager.forPackagesInProfile(USER_ID_PRIMARY, packageData ->
+ activeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(eventTypes)
+ .getActiveTimeSlots()));
+ return activeSlots;
+ }
+
private ShortcutInfo buildShortcutInfo(String packageName, int userId, String id,
@Nullable Person person) {
Context mockContext = mock(Context.class);
@@ -778,10 +739,5 @@ public final class DataManagerTest {
mSmsQueryHelper = new TestSmsQueryHelper(context, eventConsumer);
return mSmsQueryHelper;
}
-
- @Override
- int getCallingUserId() {
- return mCallingUserId;
- }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index 808906e3a06a..f498a9450c9e 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -73,7 +74,7 @@ public final class ShareTargetPredictorTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mDataManager.getShareShortcuts(any())).thenReturn(mShareShortcuts);
+ when(mDataManager.getShareShortcuts(any(), anyInt())).thenReturn(mShareShortcuts);
when(mDataManager.getPackage(PACKAGE_1, USER_ID)).thenReturn(mPackageData1);
when(mDataManager.getPackage(PACKAGE_2, USER_ID)).thenReturn(mPackageData2);
@@ -82,7 +83,8 @@ public final class ShareTargetPredictorTest {
.setPredictedTargetCount(NUM_PREDICTED_TARGETS)
.setExtras(new Bundle())
.build();
- mPredictor = new ShareTargetPredictor(predictionContext, targets -> { }, mDataManager);
+ mPredictor = new ShareTargetPredictor(
+ predictionContext, targets -> { }, mDataManager, USER_ID);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index ccf7ca9d3cf0..624cb83f9e19 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -28,10 +28,12 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.hardware.thermal.V2_0.TemperatureThreshold;
import android.os.CoolingDevice;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.IThermalEventListener;
+import android.os.IThermalService;
import android.os.IThermalStatusListener;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -72,6 +74,8 @@ public class ThermalManagerServiceTest {
@Mock
private IPowerManager mIPowerManagerMock;
@Mock
+ private IThermalService mIThermalServiceMock;
+ @Mock
private IThermalEventListener mEventListener1;
@Mock
private IThermalEventListener mEventListener2;
@@ -133,6 +137,12 @@ public class ThermalManagerServiceTest {
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
return true;
}
@@ -153,7 +163,7 @@ public class ThermalManagerServiceTest {
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mFakeHal = new ThermalHalFake();
- mPowerManager = new PowerManager(mContext, mIPowerManagerMock, null);
+ mPowerManager = new PowerManager(mContext, mIPowerManagerMock, mIThermalServiceMock, null);
when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
resetListenerMock();
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index d5cdbeb121b0..035a2f11112c 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -36,6 +36,7 @@ import android.content.IntentSender;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IRecoverySystemProgressListener;
+import android.os.IThermalService;
import android.os.Looper;
import android.os.PowerManager;
@@ -62,6 +63,7 @@ public class RecoverySystemServiceTest {
private RecoverySystemService.UncryptSocket mUncryptSocket;
private Context mContext;
private IPowerManager mIPowerManager;
+ private IThermalService mIThermalService;
private FileWriter mUncryptUpdateFileWriter;
private LockSettingsInternal mLockSettingsInternal;
@@ -77,8 +79,9 @@ public class RecoverySystemServiceTest {
Looper looper = InstrumentationRegistry.getContext().getMainLooper();
mIPowerManager = mock(IPowerManager.class);
+ mIThermalService = mock(IThermalService.class);
PowerManager powerManager = new PowerManager(mock(Context.class), mIPowerManager,
- new Handler(looper));
+ mIThermalService, new Handler(looper));
mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 604fcd38fc44..ccce0436ea59 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -359,8 +359,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Before
public void setUp() throws Exception {
+ // Shell permisssions will override permissions of our app, so add all necessary permissions
+ // fo this test here:
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
- "android.permission.WRITE_DEVICE_CONFIG", "android.permission.READ_DEVICE_CONFIG");
+ "android.permission.WRITE_DEVICE_CONFIG",
+ "android.permission.READ_DEVICE_CONFIG",
+ "android.permission.READ_CONTACTS");
MockitoAnnotations.initMocks(this);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 7753a32d8ca0..6a8917cda9fd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -20,7 +20,9 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
@@ -53,6 +55,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import android.app.ActivityManager.TaskSnapshot;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
@@ -65,12 +69,16 @@ import androidx.test.filters.SmallTest;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import com.google.common.truth.Truth;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+
/**
* Build/Install/Run:
* atest WmTests:RecentsAnimationControllerTest
@@ -330,6 +338,107 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE));
}
+ @Test
+ public void testRecentViewInFixedPortraitWhenTopAppInLandscape() {
+ mWm.mIsFixedRotationTransformEnabled = true;
+ mWm.setRecentsAnimationController(mController);
+
+ final ActivityStack homeStack = mDisplayContent.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final ActivityRecord homeAppWindow =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build();
+ final ActivityRecord appWindow = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState win0 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
+ appWindow.addWindow(win0);
+
+ final ActivityRecord landActivity = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ landActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, landActivity, "win1");
+ landActivity.addWindow(win1);
+
+ assertEquals(landActivity.getTask().getTopVisibleActivity(), landActivity);
+ assertEquals(landActivity.findMainWindow(), win1);
+
+ // Ensure that the display is in Landscape
+ landActivity.onDescendantOrientationChanged(landActivity.token, landActivity);
+ assertEquals(Configuration.ORIENTATION_LANDSCAPE,
+ mDisplayContent.getConfiguration().orientation);
+
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeAppWindow);
+
+ // Check that the home app is in portrait
+ assertEquals(Configuration.ORIENTATION_PORTRAIT,
+ homeAppWindow.getConfiguration().orientation);
+ }
+
+ @Test
+ public void testWallpaperHasFixedRotationApplied() {
+ mWm.mIsFixedRotationTransformEnabled = true;
+ mWm.setRecentsAnimationController(mController);
+
+ // Create a portrait home stack, a wallpaper and a landscape application displayed on top.
+
+ // Home stack
+ final ActivityStack homeStack = mDisplayContent.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final ActivityRecord homeActivity =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build();
+ homeActivity.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+
+ final WindowState homeWindow = createWindow(null, TYPE_BASE_APPLICATION, homeActivity,
+ "homeWindow");
+ homeActivity.addWindow(homeWindow);
+ homeWindow.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+
+ // Landscape application
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState applicationWindow = createWindow(null, TYPE_BASE_APPLICATION, activity,
+ "applicationWindow");
+ activity.addWindow(applicationWindow);
+ activity.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+
+ // Wallpaper
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+ "wallpaperWindow");
+
+ // Make sure the landscape activity is on top and the display is in landscape
+ activity.moveFocusableActivityToTop("test");
+ mDisplayContent.getConfiguration().windowConfiguration.setRotation(
+ mDisplayContent.getRotation());
+
+
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+
+ // Start the recents animation
+ mController
+ .initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity);
+
+ mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+
+ // Check preconditions
+ ArrayList<WallpaperWindowToken> wallpapers = new ArrayList<>(1);
+ mDisplayContent.forAllWallpaperWindows(wallpapers::add);
+
+ Truth.assertThat(wallpapers).hasSize(1);
+ Truth.assertThat(wallpapers.get(0).getTopChild()).isEqualTo(wallpaperWindow);
+
+ // Actual check
+ assertEquals(Configuration.ORIENTATION_PORTRAIT,
+ wallpapers.get(0).getConfiguration().orientation);
+ }
+
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1c330e263d7e..3025caf74000 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -125,6 +125,8 @@ interface IWifiManager
DhcpInfo getDhcpInfo();
+ void setScanAlwaysAvailable(boolean isAvailable);
+
boolean isScanAlwaysAvailable();
boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws);
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 0db3313ae137..ae5bf7d5ae84 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -17,6 +17,7 @@
package android.net.wifi;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -202,6 +203,11 @@ public final class SoftApConfiguration implements Parcelable {
private final List<MacAddress> mAllowedClientList;
/**
+ * Whether auto shutdown of soft AP is enabled or not.
+ */
+ private final boolean mAutoShutdownEnabled;
+
+ /**
* Delay in milliseconds before shutting down soft AP when
* there are no connected devices.
*/
@@ -240,9 +246,9 @@ public final class SoftApConfiguration implements Parcelable {
/** Private constructor for Builder and Parcelable implementation. */
private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
@Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
- @SecurityType int securityType, int maxNumberOfClients, int shutdownTimeoutMillis,
- boolean clientControlByUser, @NonNull List<MacAddress> blockedList,
- @NonNull List<MacAddress> allowedList) {
+ @SecurityType int securityType, int maxNumberOfClients, boolean shutdownTimeoutEnabled,
+ int shutdownTimeoutMillis, boolean clientControlByUser,
+ @NonNull List<MacAddress> blockedList, @NonNull List<MacAddress> allowedList) {
mSsid = ssid;
mBssid = bssid;
mPassphrase = passphrase;
@@ -251,6 +257,7 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = channel;
mSecurityType = securityType;
mMaxNumberOfClients = maxNumberOfClients;
+ mAutoShutdownEnabled = shutdownTimeoutEnabled;
mShutdownTimeoutMillis = shutdownTimeoutMillis;
mClientControlByUser = clientControlByUser;
mBlockedClientList = new ArrayList<>(blockedList);
@@ -274,6 +281,7 @@ public final class SoftApConfiguration implements Parcelable {
&& mChannel == other.mChannel
&& mSecurityType == other.mSecurityType
&& mMaxNumberOfClients == other.mMaxNumberOfClients
+ && mAutoShutdownEnabled == other.mAutoShutdownEnabled
&& mShutdownTimeoutMillis == other.mShutdownTimeoutMillis
&& mClientControlByUser == other.mClientControlByUser
&& Objects.equals(mBlockedClientList, other.mBlockedClientList)
@@ -283,8 +291,9 @@ public final class SoftApConfiguration implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid,
- mBand, mChannel, mSecurityType, mMaxNumberOfClients, mShutdownTimeoutMillis,
- mClientControlByUser, mBlockedClientList, mAllowedClientList);
+ mBand, mChannel, mSecurityType, mMaxNumberOfClients, mAutoShutdownEnabled,
+ mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList,
+ mAllowedClientList);
}
@Override
@@ -299,6 +308,7 @@ public final class SoftApConfiguration implements Parcelable {
sbuf.append(" \n Channel =").append(mChannel);
sbuf.append(" \n SecurityType=").append(getSecurityType());
sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients);
+ sbuf.append(" \n AutoShutdownEnabled=").append(mAutoShutdownEnabled);
sbuf.append(" \n ShutdownTimeoutMillis=").append(mShutdownTimeoutMillis);
sbuf.append(" \n ClientControlByUser=").append(mClientControlByUser);
sbuf.append(" \n BlockedClientList=").append(mBlockedClientList);
@@ -316,6 +326,7 @@ public final class SoftApConfiguration implements Parcelable {
dest.writeInt(mChannel);
dest.writeInt(mSecurityType);
dest.writeInt(mMaxNumberOfClients);
+ dest.writeBoolean(mAutoShutdownEnabled);
dest.writeInt(mShutdownTimeoutMillis);
dest.writeBoolean(mClientControlByUser);
dest.writeTypedList(mBlockedClientList);
@@ -335,7 +346,7 @@ public final class SoftApConfiguration implements Parcelable {
in.readString(),
in.readParcelable(MacAddress.class.getClassLoader()),
in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
- in.readInt(), in.readInt(), in.readBoolean(),
+ in.readInt(), in.readBoolean(), in.readInt(), in.readBoolean(),
in.createTypedArrayList(MacAddress.CREATOR),
in.createTypedArrayList(MacAddress.CREATOR));
}
@@ -429,6 +440,18 @@ public final class SoftApConfiguration implements Parcelable {
}
/**
+ * Returns whether auto shutdown is enabled or not.
+ * The Soft AP will shutdown when there are no devices associated to it for
+ * the timeout duration. See {@link Builder#setAutoShutdownEnabled(boolean)}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isAutoShutdownEnabled() {
+ return mAutoShutdownEnabled;
+ }
+
+ /**
* Returns the shutdown timeout in milliseconds.
* The Soft AP will shutdown when there are no devices associated to it for
* the timeout duration. See {@link Builder#setShutdownTimeoutMillis(int)}.
@@ -551,6 +574,7 @@ public final class SoftApConfiguration implements Parcelable {
private int mChannel;
private int mMaxNumberOfClients;
private int mSecurityType;
+ private boolean mAutoShutdownEnabled;
private int mShutdownTimeoutMillis;
private boolean mClientControlByUser;
private List<MacAddress> mBlockedClientList;
@@ -568,6 +592,7 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = 0;
mMaxNumberOfClients = 0;
mSecurityType = SECURITY_TYPE_OPEN;
+ mAutoShutdownEnabled = true; // enabled by default.
mShutdownTimeoutMillis = 0;
mClientControlByUser = false;
mBlockedClientList = new ArrayList<>();
@@ -588,6 +613,7 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = other.mChannel;
mMaxNumberOfClients = other.mMaxNumberOfClients;
mSecurityType = other.mSecurityType;
+ mAutoShutdownEnabled = other.mAutoShutdownEnabled;
mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
mClientControlByUser = other.mClientControlByUser;
mBlockedClientList = new ArrayList<>(other.mBlockedClientList);
@@ -603,8 +629,8 @@ public final class SoftApConfiguration implements Parcelable {
public SoftApConfiguration build() {
return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients,
- mShutdownTimeoutMillis, mClientControlByUser, mBlockedClientList,
- mAllowedClientList);
+ mAutoShutdownEnabled, mShutdownTimeoutMillis, mClientControlByUser,
+ mBlockedClientList, mAllowedClientList);
}
/**
@@ -789,7 +815,7 @@ public final class SoftApConfiguration implements Parcelable {
* @return Builder for chaining.
*/
@NonNull
- public Builder setMaxNumberOfClients(int maxNumberOfClients) {
+ public Builder setMaxNumberOfClients(@IntRange(from = 0) int maxNumberOfClients) {
if (maxNumberOfClients < 0) {
throw new IllegalArgumentException("maxNumberOfClients should be not negative");
}
@@ -798,6 +824,25 @@ public final class SoftApConfiguration implements Parcelable {
}
/**
+ * Specifies whether auto shutdown is enabled or not.
+ * The Soft AP will shut down when there are no devices connected to it for
+ * the timeout duration.
+ *
+ * <p>
+ * <li>If not set, defaults to true</li>
+ *
+ * @param enable true to enable, false to disable.
+ * @return Builder for chaining.
+ *
+ * @see #setShutdownTimeoutMillis(int)
+ */
+ @NonNull
+ public Builder setAutoShutdownEnabled(boolean enable) {
+ mAutoShutdownEnabled = enable;
+ return this;
+ }
+
+ /**
* Specifies the shutdown timeout in milliseconds.
* The Soft AP will shut down when there are no devices connected to it for
* the timeout duration.
@@ -807,14 +852,16 @@ public final class SoftApConfiguration implements Parcelable {
*
* <p>
* <li>If not set, defaults to 0</li>
- * <li>The shut down timout will apply when
- * {@link Settings.Global.SOFT_AP_TIMEOUT_ENABLED} is true</li>
+ * <li>The shut down timeout will apply when {@link #setAutoShutdownEnabled(boolean)} is
+ * set to true</li>
*
* @param timeoutMillis milliseconds of the timeout delay.
* @return Builder for chaining.
+ *
+ * @see #setAutoShutdownEnabled(boolean)
*/
@NonNull
- public Builder setShutdownTimeoutMillis(int timeoutMillis) {
+ public Builder setShutdownTimeoutMillis(@IntRange(from = 0) int timeoutMillis) {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("Invalid timeout value");
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index f693315c6cff..af2f2461ac94 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2755,6 +2755,26 @@ public class WifiManager {
}
/**
+ * Set if scanning is always available.
+ *
+ * If set to {@code true}, apps can issue {@link #startScan} and fetch scan results
+ * even when Wi-Fi is turned off.
+ *
+ * @param isAvailable true to enable, false to disable.
+ * @hide
+ * @see #isScanAlwaysAvailable()
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void setScanAlwaysAvailable(boolean isAvailable) {
+ try {
+ mService.setScanAlwaysAvailable(isAvailable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Check if scanning is always available.
*
* If this return {@code true}, apps can issue {@link #startScan} and fetch scan results
diff --git a/wifi/java/android/net/wifi/WifiOemMigrationHook.java b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
index 22d778637101..44dbb98a8ab8 100755
--- a/wifi/java/android/net/wifi/WifiOemMigrationHook.java
+++ b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
@@ -21,8 +21,10 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
+import android.provider.Settings;
import java.util.List;
@@ -33,6 +35,9 @@ import java.util.List;
*/
@SystemApi
public final class WifiOemMigrationHook {
+
+ private WifiOemMigrationHook() { }
+
/**
* Container for all the wifi config data to migrate.
*/
@@ -152,8 +157,6 @@ public final class WifiOemMigrationHook {
}
}
- private WifiOemMigrationHook() { }
-
/**
* Load data from OEM's config store.
* <p>
@@ -178,4 +181,263 @@ public final class WifiOemMigrationHook {
// Note: OEM's should add code to parse data from their config store format here!
return null;
}
+
+ /**
+ * Container for all the wifi settings data to migrate.
+ */
+ public static final class SettingsMigrationData implements Parcelable {
+ private final boolean mScanAlwaysAvailable;
+ private final boolean mP2pFactoryResetPending;
+ private final String mP2pDeviceName;
+ private final boolean mSoftApTimeoutEnabled;
+ private final boolean mWakeupEnabled;
+ private final boolean mScanThrottleEnabled;
+ private final boolean mVerboseLoggingEnabled;
+
+ private SettingsMigrationData(boolean scanAlwaysAvailable, boolean p2pFactoryResetPending,
+ @Nullable String p2pDeviceName, boolean softApTimeoutEnabled, boolean wakeupEnabled,
+ boolean scanThrottleEnabled, boolean verboseLoggingEnabled) {
+ mScanAlwaysAvailable = scanAlwaysAvailable;
+ mP2pFactoryResetPending = p2pFactoryResetPending;
+ mP2pDeviceName = p2pDeviceName;
+ mSoftApTimeoutEnabled = softApTimeoutEnabled;
+ mWakeupEnabled = wakeupEnabled;
+ mScanThrottleEnabled = scanThrottleEnabled;
+ mVerboseLoggingEnabled = verboseLoggingEnabled;
+ }
+
+ public static final @NonNull Parcelable.Creator<SettingsMigrationData> CREATOR =
+ new Parcelable.Creator<SettingsMigrationData>() {
+ @Override
+ public SettingsMigrationData createFromParcel(Parcel in) {
+ boolean scanAlwaysAvailable = in.readBoolean();
+ boolean p2pFactoryResetPending = in.readBoolean();
+ String p2pDeviceName = in.readString();
+ boolean softApTimeoutEnabled = in.readBoolean();
+ boolean wakeupEnabled = in.readBoolean();
+ boolean scanThrottleEnabled = in.readBoolean();
+ boolean verboseLoggingEnabled = in.readBoolean();
+ return new SettingsMigrationData(
+ scanAlwaysAvailable, p2pFactoryResetPending,
+ p2pDeviceName, softApTimeoutEnabled, wakeupEnabled,
+ scanThrottleEnabled, verboseLoggingEnabled);
+ }
+
+ @Override
+ public SettingsMigrationData[] newArray(int size) {
+ return new SettingsMigrationData[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBoolean(mScanAlwaysAvailable);
+ dest.writeBoolean(mP2pFactoryResetPending);
+ dest.writeString(mP2pDeviceName);
+ dest.writeBoolean(mSoftApTimeoutEnabled);
+ dest.writeBoolean(mWakeupEnabled);
+ dest.writeBoolean(mScanThrottleEnabled);
+ dest.writeBoolean(mVerboseLoggingEnabled);
+ }
+
+ /**
+ * @return True if scans are allowed even when wifi is toggled off, false otherwise.
+ */
+ public boolean isScanAlwaysAvailable() {
+ return mScanAlwaysAvailable;
+ }
+
+ /**
+ * @return indicate whether factory reset request is pending.
+ */
+ public boolean isP2pFactoryResetPending() {
+ return mP2pFactoryResetPending;
+ }
+
+ /**
+ * @return the Wi-Fi peer-to-peer device name
+ */
+ public @Nullable String getP2pDeviceName() {
+ return mP2pDeviceName;
+ }
+
+ /**
+ * @return Whether soft AP will shut down after a timeout period when no devices are
+ * connected.
+ */
+ public boolean isSoftApTimeoutEnabled() {
+ return mSoftApTimeoutEnabled;
+ }
+
+ /**
+ * @return whether Wi-Fi Wakeup feature is enabled.
+ */
+ public boolean isWakeUpEnabled() {
+ return mWakeupEnabled;
+ }
+
+ /**
+ * @return Whether wifi scan throttle is enabled or not.
+ */
+ public boolean isScanThrottleEnabled() {
+ return mScanThrottleEnabled;
+ }
+
+ /**
+ * @return Whether to enable verbose logging in Wi-Fi.
+ */
+ public boolean isVerboseLoggingEnabled() {
+ return mVerboseLoggingEnabled;
+ }
+
+ /**
+ * Builder to create instance of {@link SettingsMigrationData}.
+ */
+ public static final class Builder {
+ private boolean mScanAlwaysAvailable;
+ private boolean mP2pFactoryResetPending;
+ private String mP2pDeviceName;
+ private boolean mSoftApTimeoutEnabled;
+ private boolean mWakeupEnabled;
+ private boolean mScanThrottleEnabled;
+ private boolean mVerboseLoggingEnabled;
+
+ public Builder() {
+ }
+
+ /**
+ * Setting to allow scans even when wifi is toggled off.
+ *
+ * @param available true if available, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setScanAlwaysAvailable(boolean available) {
+ mScanAlwaysAvailable = available;
+ return this;
+ }
+
+ /**
+ * Indicate whether factory reset request is pending.
+ *
+ * @param pending true if pending, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setP2pFactoryResetPending(boolean pending) {
+ mP2pFactoryResetPending = pending;
+ return this;
+ }
+
+ /**
+ * The Wi-Fi peer-to-peer device name
+ *
+ * @param name Name if set, null otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setP2pDeviceName(@Nullable String name) {
+ mP2pDeviceName = name;
+ return this;
+ }
+
+ /**
+ * Whether soft AP will shut down after a timeout period when no devices are connected.
+ *
+ * @param enabled true if enabled, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setSoftApTimeoutEnabled(boolean enabled) {
+ mSoftApTimeoutEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Value to specify if Wi-Fi Wakeup feature is enabled.
+ *
+ * @param enabled true if enabled, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setWakeUpEnabled(boolean enabled) {
+ mWakeupEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Whether wifi scan throttle is enabled or not.
+ *
+ * @param enabled true if enabled, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setScanThrottleEnabled(boolean enabled) {
+ mScanThrottleEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Setting to enable verbose logging in Wi-Fi.
+ *
+ * @param enabled true if enabled, false otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setVerboseLoggingEnabled(boolean enabled) {
+ mVerboseLoggingEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Build an instance of {@link SettingsMigrationData}.
+ *
+ * @return Instance of {@link SettingsMigrationData}.
+ */
+ public @NonNull SettingsMigrationData build() {
+ return new SettingsMigrationData(mScanAlwaysAvailable, mP2pFactoryResetPending,
+ mP2pDeviceName, mSoftApTimeoutEnabled, mWakeupEnabled, mScanThrottleEnabled,
+ mVerboseLoggingEnabled);
+ }
+ }
+ }
+
+ /**
+ * Load data from Settings.Global values.
+ *
+ * <p>
+ * Note:
+ * <li> This is method is invoked once on the first bootup. OEM can safely delete these settings
+ * once the migration is complete. The first & only relevant invocation of
+ * {@link #loadFromSettings(Context)} ()} occurs when a previously released
+ * device upgrades to the wifi mainline module from an OEM implementation of the wifi stack.
+ * </li>
+ *
+ * @param context Context to use for loading the settings provider.
+ * @return Instance of {@link SettingsMigrationData} for migrating data.
+ */
+ @NonNull
+ public static SettingsMigrationData loadFromSettings(@NonNull Context context) {
+ return new SettingsMigrationData.Builder()
+ .setScanAlwaysAvailable(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1)
+ .setP2pFactoryResetPending(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WIFI_P2P_PENDING_FACTORY_RESET, 0) == 1)
+ .setP2pDeviceName(
+ Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.WIFI_P2P_DEVICE_NAME))
+ .setSoftApTimeoutEnabled(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1) == 1)
+ .setWakeUpEnabled(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1)
+ .setScanThrottleEnabled(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WIFI_SCAN_THROTTLE_ENABLED, 1) == 1)
+ .setVerboseLoggingEnabled(
+ Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 1) == 1)
+ .build();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index d9584885a045..060ddf05b8d7 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -125,6 +125,7 @@ public class SoftApConfigurationTest {
.setChannel(149, SoftApConfiguration.BAND_5GHZ)
.setHiddenSsid(true)
.setMaxNumberOfClients(10)
+ .setAutoShutdownEnabled(true)
.setShutdownTimeoutMillis(500000)
.enableClientControlByUser(true)
.setClientList(testBlockedClientList, testAllowedClientList)
@@ -136,6 +137,7 @@ public class SoftApConfigurationTest {
assertThat(original.getChannel()).isEqualTo(149);
assertThat(original.isHiddenSsid()).isEqualTo(true);
assertThat(original.getMaxNumberOfClients()).isEqualTo(10);
+ assertThat(original.isAutoShutdownEnabled()).isEqualTo(true);
assertThat(original.getShutdownTimeoutMillis()).isEqualTo(500000);
assertThat(original.isClientControlByUserEnabled()).isEqualTo(true);
assertThat(original.getBlockedClientList()).isEqualTo(testBlockedClientList);
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 853212aafcdf..234d929fc05a 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -2401,4 +2401,15 @@ public class WifiManagerTest {
assertFalse(mWifiManager.isAutoWakeupEnabled());
verify(mWifiService).isAutoWakeupEnabled();
}
+
+
+ @Test
+ public void testScanAvailable() throws Exception {
+ mWifiManager.setScanAlwaysAvailable(true);
+ verify(mWifiService).setScanAlwaysAvailable(true);
+
+ when(mWifiService.isScanAlwaysAvailable()).thenReturn(false);
+ assertFalse(mWifiManager.isScanAlwaysAvailable());
+ verify(mWifiService).isScanAlwaysAvailable();
+ }
}