diff options
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(); + } } |