Merge "Build statsd framework jar against module_current"
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index d4ceabd..7b3764a 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -334,7 +334,8 @@
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
- if (leaseExpiryTimeMillis != 0 && leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
+ if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0
+ && leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
throw new IllegalArgumentException(
"Lease expiry cannot be later than blobs expiry time");
}
diff --git a/api/current.txt b/api/current.txt
index ac42e66..a0b38cd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36633,6 +36633,7 @@
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 @@
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";
}
@@ -53891,6 +53892,7 @@
method public boolean requestRectangleOnScreen(android.graphics.Rect);
method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
method public final void requestUnbufferedDispatch(android.view.MotionEvent);
+ method public final void requestUnbufferedDispatch(int);
method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
method public void resetPivot();
method public static int resolveSize(int, int);
@@ -55604,6 +55606,7 @@
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();
@@ -55756,6 +55759,7 @@
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";
@@ -55843,6 +55847,12 @@
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();
@@ -60825,6 +60835,7 @@
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 f974380..a7dbf38 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7536,6 +7536,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index bf6afe7..a4e8fdc 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -8231,14 +8231,6 @@
}
/**
- * HWUI renders pipeline type: GL (0) or Vulkan (1).
- */
-enum PipelineType {
- GL = 0;
- VULKAN = 1;
-}
-
-/**
* HWUI stats for a given app.
*/
message GraphicsStats {
@@ -8251,9 +8243,16 @@
// The start & end timestamps in UTC as
// milliseconds since January 1, 1970
// Compatible with java.util.Date#setTime()
- optional int64 stats_start = 3;
+ optional int64 start_millis = 3;
- optional int64 stats_end = 4;
+ optional int64 end_millis = 4;
+
+ // HWUI renders pipeline type: GL (1) or Vulkan (2).
+ enum PipelineType {
+ UNKNOWN = 0;
+ GL = 1;
+ VULKAN = 2;
+ }
// HWUI renders pipeline type: GL or Vulkan.
optional PipelineType pipeline = 5;
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 83d609c..0a138cf 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -960,7 +960,7 @@
return false;
}
List<GestureDescription.GestureStep> steps =
- MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 100);
+ MotionEventGenerator.getGestureStepsFromGestureDescription(gesture, 16);
try {
synchronized (mLock) {
mGestureStatusCallbackSequence++;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 526c0b3..948546b 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -88,6 +88,7 @@
void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList);
+ ParceledListSlice getConversations(boolean onlyImportant);
ParceledListSlice getConversationsForPackage(String pkg, int uid);
ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 655dd9b..f1559f7 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -148,6 +148,7 @@
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 @@
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 5b60b85..3e1a480 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -25,6 +25,7 @@
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 @@
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 @@
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 a69905e..3a93421 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -22,6 +22,7 @@
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 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 0000000..6ad7fae
--- /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 8c98960..ad00233 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -103,4 +103,11 @@
* {@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 a8fa6db..199b5d5 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -18,7 +18,9 @@
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 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 @@
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 @@
* 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 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 @@
@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);
- }
+ 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");
- }
- } 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 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");
}
- 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();
- }
+ } 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/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index e78aef5..4bd31f8 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -16,6 +16,7 @@
package android.os.storage;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IVold;
@@ -124,4 +125,14 @@
* legacy storage, {@code false} otherwise.
*/
public abstract boolean hasLegacyExternalStorage(int uid);
+
+ /**
+ * Makes sure app-private data directories on external storage are setup correctly
+ * after an application is installed or upgraded. The main use for this is OBB dirs,
+ * which can be created/modified by the installer.
+ *
+ * @param packageName the package name of the package
+ * @param uid the uid of the package
+ */
+ public abstract void prepareAppDataAfterInstall(@NonNull String packageName, int uid);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f86c971..4523acb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8595,6 +8595,15 @@
public static final String NOTIFICATION_HISTORY_ENABLED = "notification_history_enabled";
/**
+ * When enabled conversations marked as favorites will be set to bubble.
+ *
+ * The value 1 - enable, 0 - disable
+ * @hide
+ */
+ public static final String BUBBLE_IMPORTANT_CONVERSATIONS
+ = "bubble_important_conversations";
+
+ /**
* Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right
* swipe).
*
@@ -9084,26 +9093,34 @@
* 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;
/**
@@ -10197,7 +10214,9 @@
/**
* 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";
@@ -10227,7 +10246,9 @@
/**
* 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";
/**
@@ -10238,8 +10259,8 @@
/**
* 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";
@@ -10248,8 +10269,8 @@
*
* 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";
@@ -10258,8 +10279,8 @@
*
* 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";
/**
@@ -10304,6 +10325,7 @@
* 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";
@@ -10343,13 +10365,11 @@
/**
* 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";
/**
@@ -10451,8 +10471,8 @@
* 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";
@@ -10477,8 +10497,8 @@
* 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";
@@ -10520,8 +10540,8 @@
/**
* 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/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java
index 9847695..ab465ab 100644
--- a/core/java/android/service/notification/ConversationChannelWrapper.java
+++ b/core/java/android/service/notification/ConversationChannelWrapper.java
@@ -33,6 +33,8 @@
private CharSequence mGroupLabel;
private CharSequence mParentChannelLabel;
private ShortcutInfo mShortcutInfo;
+ private String mPkg;
+ private int mUid;
public ConversationChannelWrapper() {}
@@ -41,6 +43,8 @@
mGroupLabel = in.readCharSequence();
mParentChannelLabel = in.readCharSequence();
mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+ mPkg = in.readStringNoHelper();
+ mUid = in.readInt();
}
@Override
@@ -49,6 +53,8 @@
dest.writeCharSequence(mGroupLabel);
dest.writeCharSequence(mParentChannelLabel);
dest.writeParcelable(mShortcutInfo, flags);
+ dest.writeStringNoHelper(mPkg);
+ dest.writeInt(mUid);
}
@Override
@@ -103,6 +109,22 @@
mShortcutInfo = shortcutInfo;
}
+ public String getPkg() {
+ return mPkg;
+ }
+
+ public void setPkg(String pkg) {
+ mPkg = pkg;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public void setUid(int uid) {
+ mUid = uid;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -111,12 +133,14 @@
return Objects.equals(getNotificationChannel(), that.getNotificationChannel()) &&
Objects.equals(getGroupLabel(), that.getGroupLabel()) &&
Objects.equals(getParentChannelLabel(), that.getParentChannelLabel()) &&
- Objects.equals(getShortcutInfo(), that.getShortcutInfo());
+ Objects.equals(getShortcutInfo(), that.getShortcutInfo()) &&
+ Objects.equals(getPkg(), that.getPkg()) &&
+ getUid() == that.getUid();
}
@Override
public int hashCode() {
return Objects.hash(getNotificationChannel(), getGroupLabel(), getParentChannelLabel(),
- getShortcutInfo());
+ getShortcutInfo(), getPkg(), getUid());
}
}
diff --git a/core/java/android/view/CutoutSpecification.java b/core/java/android/view/CutoutSpecification.java
index d21a952..850e9fc 100644
--- a/core/java/android/view/CutoutSpecification.java
+++ b/core/java/android/view/CutoutSpecification.java
@@ -406,9 +406,7 @@
}
currentIndex += RIGHT_MARKER.length();
} else if (specWithoutDp.startsWith(BOTTOM_MARKER, currentIndex)) {
- if (!mPositionFromCenterVertical) {
- parseSvgPathSpec(region, sb.toString());
- }
+ parseSvgPathSpec(region, sb.toString());
currentIndex += BOTTOM_MARKER.length();
/* prepare to parse the rest path */
@@ -416,9 +414,7 @@
mBindBottomCutout = true;
mPositionFromBottom = true;
} else if (specWithoutDp.startsWith(CENTER_VERTICAL_MARKER, currentIndex)) {
- if (!mPositionFromBottom) {
- parseSvgPathSpec(region, sb.toString());
- }
+ parseSvgPathSpec(region, sb.toString());
currentIndex += CENTER_VERTICAL_MARKER.length();
/* prepare to parse the rest path */
@@ -431,14 +427,16 @@
/* prepare to parse the rest path */
resetStatus(sb);
} else if (specWithoutDp.startsWith(BIND_LEFT_CUTOUT_MARKER, currentIndex)) {
- if (!mBindBottomCutout && !mBindRightCutout) {
- mBindLeftCutout = true;
- }
+ mBindBottomCutout = false;
+ mBindRightCutout = false;
+ mBindLeftCutout = true;
+
currentIndex += BIND_LEFT_CUTOUT_MARKER.length();
} else if (specWithoutDp.startsWith(BIND_RIGHT_CUTOUT_MARKER, currentIndex)) {
- if (!mBindBottomCutout && !mBindLeftCutout) {
- mBindRightCutout = true;
- }
+ mBindBottomCutout = false;
+ mBindLeftCutout = false;
+ mBindRightCutout = true;
+
currentIndex += BIND_RIGHT_CUTOUT_MARKER.length();
} else {
currentIndex += 1;
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b9868a7..3047385 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -28,6 +28,7 @@
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 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 @@
&& 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 @@
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 @@
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 @@
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 @@
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/InputDevice.java b/core/java/android/view/InputDevice.java
index 360dedd..58e5b2d 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -28,6 +29,8 @@
import android.os.Parcelable;
import android.os.Vibrator;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -138,6 +141,19 @@
*/
public static final int SOURCE_CLASS_JOYSTICK = 0x00000010;
+ /** @hide */
+ @IntDef(flag = true, prefix = { "SOURCE_CLASS_" }, value = {
+ SOURCE_CLASS_NONE,
+ SOURCE_CLASS_BUTTON,
+ SOURCE_CLASS_POINTER,
+ SOURCE_CLASS_POINTER,
+ SOURCE_CLASS_TRACKBALL,
+ SOURCE_CLASS_POSITION,
+ SOURCE_CLASS_JOYSTICK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface InputSourceClass {}
+
/**
* The input source is unknown.
*/
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cf48c52..29371b0 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -41,6 +41,7 @@
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 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/View.java b/core/java/android/view/View.java
index b9be33c..4c7307e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -110,6 +110,7 @@
import android.view.AccessibilityIterators.TextSegmentIterator;
import android.view.AccessibilityIterators.WordTextSegmentIterator;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.InputDevice.InputSourceClass;
import android.view.Window.OnContentApplyWindowInsetsListener;
import android.view.WindowInsets.Type;
import android.view.WindowInsetsAnimation.Bounds;
@@ -5205,6 +5206,14 @@
private int mExplicitStyle;
/**
+ * Specifies which input source classes should provide unbuffered input events to this view
+ *
+ * @see View#requestUnbufferedDispatch(int)
+ */
+ @InputSourceClass
+ int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -8013,6 +8022,10 @@
mAttachInfo.mKeyDispatchState.reset(this);
}
+ if (mParent != null) {
+ mParent.onDescendantUnbufferedRequested();
+ }
+
notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
}
@@ -15820,12 +15833,17 @@
* system not batch {@link MotionEvent}s but instead deliver them as soon as they're
* available. This method should only be called for touch events.
*
- * <p class="note">This api is not intended for most applications. Buffered dispatch
+ * <p class="note">This API is not intended for most applications. Buffered dispatch
* provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent
* streams will not improve your input latency. Side effects include: increased latency,
* jittery scrolls and inability to take advantage of system resampling. Talk to your input
* professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for
* you.</p>
+ *
+ * To receive unbuffered events for arbitrary input device source classes, use
+ * {@link #requestUnbufferedDispatch(int)},
+ *
+ * @see View#requestUnbufferedDispatch(int)
*/
public final void requestUnbufferedDispatch(MotionEvent event) {
final int action = event.getAction();
@@ -15837,6 +15855,27 @@
mAttachInfo.mUnbufferedDispatchRequested = true;
}
+ /**
+ * Request unbuffered dispatch of the given event source class to this view.
+ * This is similar to {@link View#requestUnbufferedDispatch(MotionEvent)}, but does not
+ * automatically terminate, and allows the specification of arbitrary input source classes.
+ *
+ * @param source The combined input source class to request unbuffered dispatch for. All
+ * events coming from these source classes will not be buffered. Set to
+ * {@link InputDevice#SOURCE_CLASS_NONE} in order to return to default behaviour.
+ *
+ * @see View#requestUnbufferedDispatch(MotionEvent)
+ */
+ public final void requestUnbufferedDispatch(@InputSourceClass int source) {
+ if (mUnbufferedInputSource == source) {
+ return;
+ }
+ mUnbufferedInputSource = source;
+ if (mParent != null) {
+ mParent.onDescendantUnbufferedRequested();
+ }
+ }
+
private boolean hasSize() {
return (mBottom > mTop) && (mRight > mLeft);
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0367536..b6c46be 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3692,6 +3692,31 @@
}
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
@@ -9004,4 +9029,30 @@
getChildAt(i).encode(encoder);
}
}
+
+ /** @hide */
+ @Override
+ public final void onDescendantUnbufferedRequested() {
+ // First look at the focused child for focused events
+ int focusedChildNonPointerSource = InputDevice.SOURCE_CLASS_NONE;
+ if (mFocused != null) {
+ focusedChildNonPointerSource = mFocused.mUnbufferedInputSource
+ & (~InputDevice.SOURCE_CLASS_POINTER);
+ }
+ mUnbufferedInputSource = focusedChildNonPointerSource;
+
+ // Request unbuffered dispatch for pointer events for this view if any child requested
+ // unbuffered dispatch for pointer events. This is because we can't expect that the pointer
+ // source would dispatch to the focused view.
+ for (int i = 0; i < mChildrenCount; i++) {
+ final View child = mChildren[i];
+ if ((child.mUnbufferedInputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ mUnbufferedInputSource |= InputDevice.SOURCE_CLASS_POINTER;
+ break;
+ }
+ }
+ if (mParent != null) {
+ mParent.onDescendantUnbufferedRequested();
+ }
+ }
}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index f25206d..775c15e 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -675,4 +675,18 @@
*/
default void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
}
+
+ /**
+ * Unbuffered dispatch has been requested by a child of this view parent.
+ * This method is called by the View hierarchy to signal ancestors that a View needs to
+ * request unbuffered dispatch.
+ *
+ * @see View#requestUnbufferedDispatch(int)
+ * @hide
+ */
+ default void onDescendantUnbufferedRequested() {
+ if (getParent() != null) {
+ getParent().onDescendantUnbufferedRequested();
+ }
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 043e5be..204d2c8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -18,6 +18,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.View.PFLAG_DRAW_ANIMATION;
@@ -116,6 +117,7 @@
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedValue;
+import android.view.InputDevice.InputSourceClass;
import android.view.InsetsState.InternalInsetsType;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl.Transaction;
@@ -499,6 +501,10 @@
int mPendingInputEventCount;
boolean mProcessInputEventsScheduled;
boolean mUnbufferedInputDispatch;
+ boolean mUnbufferedInputDispatchBySource;
+ @InputSourceClass
+ int mUnbufferedInputSource = SOURCE_CLASS_NONE;
+
String mPendingInputEventQueueLengthCounterName = "pq";
InputStage mFirstInputStage;
@@ -1849,7 +1855,7 @@
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
- if (!mUnbufferedInputDispatch) {
+ if (!mUnbufferedInputDispatch && !mUnbufferedInputDispatchBySource) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
@@ -7505,6 +7511,9 @@
writer.print(mTraversalScheduled);
writer.print(innerPrefix); writer.print("mIsAmbientMode=");
writer.print(mIsAmbientMode);
+ writer.print(innerPrefix); writer.print("mUnbufferedInputSource=");
+ writer.print(Integer.toHexString(mUnbufferedInputSource));
+
if (mTraversalScheduled) {
writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")");
} else {
@@ -8109,6 +8118,7 @@
@Override
public void onInputEvent(InputEvent event) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
+ processUnbufferedRequest(event);
List<InputEvent> processedEvents;
try {
processedEvents =
@@ -8134,7 +8144,7 @@
@Override
public void onBatchedInputEventPending() {
- if (mUnbufferedInputDispatch) {
+ if (mUnbufferedInputDispatch || mUnbufferedInputDispatchBySource) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
@@ -8151,6 +8161,17 @@
unscheduleConsumeBatchedInput();
super.dispose();
}
+
+ private void processUnbufferedRequest(InputEvent event) {
+ if (!(event instanceof MotionEvent)) {
+ return;
+ }
+ mUnbufferedInputDispatchBySource =
+ (event.getSource() & mUnbufferedInputSource) != SOURCE_CLASS_NONE;
+ if (mUnbufferedInputDispatchBySource && mConsumeBatchedInputScheduled) {
+ scheduleConsumeBatchedInputImmediately();
+ }
+ }
}
WindowInputEventReceiver mInputEventReceiver;
@@ -9601,4 +9622,9 @@
return mSurfaceControl;
}
}
+
+ @Override
+ public void onDescendantUnbufferedRequested() {
+ mUnbufferedInputSource = mView.mUnbufferedInputSource;
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index bf2de14..eb4f9db 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -50,8 +50,12 @@
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 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 @@
private TouchDelegateInfo mTouchDelegateInfo;
+ private ExtraRenderingInfo mExtraRenderingInfo;
+
private IBinder mLeashedChild;
private IBinder mLeashedParent;
private long mLeashedParentNodeId = UNDEFINED_NODE_ID;
@@ -991,6 +1016,7 @@
* @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 @@
* {@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 @@
}
/**
+ * 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 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
}
/**
+ * 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 0182975..815cc5c 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 @@
@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 @@
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 @@
+ " 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 @@
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 @@
}
if (attributes.mTextSize != -1) {
+ mTextSizeUnit = attributes.mTextSizeUnit;
setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
}
@@ -4295,6 +4301,7 @@
r = c.getResources();
}
+ mTextSizeUnit = unit;
setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()),
shouldRequestLayout);
}
@@ -4315,6 +4322,17 @@
}
/**
+ * 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 @@
| 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 @@
@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 @@
}
}
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/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 61d22d1..a201a33 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -3100,7 +3100,7 @@
final ViewGroup viewGroup = (ViewGroup) holder.itemView;
int start = getListPosition(position);
int startType = getRowType(start);
- if (viewGroup.getForeground() == null) {
+ if (viewGroup.getForeground() == null && position > 0) {
viewGroup.setForeground(
getResources().getDrawable(R.drawable.chooser_row_layer_list, null));
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f168215..96bfe73 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1566,6 +1566,7 @@
viewPager.setVisibility(View.VISIBLE);
tabHost.setCurrentTab(mMultiProfilePagerAdapter.getCurrentPage());
mMultiProfilePagerAdapter.setOnProfileSelectedListener(tabHost::setCurrentTab);
+ findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE);
}
private void resetTabsHeaderStyle(TabWidget tabWidget) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 3f81b11..741ce82 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 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 {
jclass clazz;
+ jmethodID ctor;
+} gDeviceProductInfoClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gDeviceProductInfoManufactureDateClassInfo;
+
+static struct {
+ jclass clazz;
jmethodID builder;
} gGraphicBufferClassInfo;
@@ -773,6 +794,41 @@
}
}
+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 @@
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 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 @@
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/proto/android/service/graphicsstats.proto b/core/proto/android/service/graphicsstats.proto
index 2de5b7f..bb654f0 100644
--- a/core/proto/android/service/graphicsstats.proto
+++ b/core/proto/android/service/graphicsstats.proto
@@ -33,8 +33,9 @@
message GraphicsStatsProto {
enum PipelineType {
- GL = 0;
- VULKAN = 1;
+ UNKNOWN = 0;
+ GL = 1;
+ VULKAN = 2;
}
option (android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 488698a..6530036 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4953,16 +4953,6 @@
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|appPredictor" />
- <!-- Feature Id for Country Detector. -->
- <feature android:featureId="CountryDetector" android:label="@string/country_detector"/>
- <!-- Feature Id for Location service. -->
- <feature android:featureId="LocationService" android:label="@string/location_service"/>
- <!-- Feature Id for Sensor Notification service. -->
- <feature android:featureId="SensorNotificationService"
- android:label="@string/sensor_notification_service"/>
- <!-- Feature Id for Twilight service. -->
- <feature android:featureId="TwilightService" android:label="@string/twilight_service"/>
-
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 24a21eb..c0de693 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -78,6 +78,14 @@
android:layout_height="wrap_content"
android:visibility="gone">
</TabWidget>
+ <View
+ android:id="@+id/resolver_tab_divider"
+ android:visibility="gone"
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index b4e6286..4359b10 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -94,6 +94,13 @@
android:layout_height="wrap_content"
android:visibility="gone">
</TabWidget>
+ <View
+ android:id="@+id/resolver_tab_divider"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
@@ -102,10 +109,7 @@
android:id="@+id/profile_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:divider="?attr/dividerVertical"
- android:footerDividersEnabled="false"
- android:headerDividersEnabled="false"
- android:dividerHeight="1dp"/>
+ android:minHeight="@dimen/resolver_empty_state_height" />
</FrameLayout>
</LinearLayout>
</TabHost>
diff --git a/core/res/res/layout/resolver_list_per_profile.xml b/core/res/res/layout/resolver_list_per_profile.xml
index d481eff..c4f9ed9 100644
--- a/core/res/res/layout/resolver_list_per_profile.xml
+++ b/core/res/res/layout/resolver_list_per_profile.xml
@@ -29,7 +29,6 @@
android:elevation="@dimen/resolver_elevation"
android:nestedScrollingEnabled="true"
android:scrollbarStyle="outsideOverlay"
- android:scrollIndicators="top|bottom"
android:divider="@null"
android:footerDividersEnabled="false"
android:headerDividersEnabled="false"
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index b546738..72e8b0c 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -175,6 +175,14 @@
android:layout_height="wrap_content"
android:visibility="gone">
</TabWidget>
+ <View
+ android:id="@+id/resolver_tab_divider"
+ android:visibility="gone"
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
@@ -183,10 +191,7 @@
android:id="@+id/profile_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:dividerHeight="1dp"
- android:divider="?attr/dividerVertical"
- android:footerDividersEnabled="false"
- android:headerDividersEnabled="false"/>
+ android:minHeight="@dimen/resolver_empty_state_height" />
</FrameLayout>
</LinearLayout>
</TabHost>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e7ad8eb..e6a93e5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -421,14 +421,6 @@
[CHAR LIMIT=NONE] -->
<string name="location_changed_notification_text">Tap to see your location settings.</string>
- <!-- Feature Id for Country Detector. [CHAR LIMIT=NONE]-->
- <string name="country_detector">Country Detector</string>
- <!-- Feature Id for Location service. [CHAR LIMIT=NONE]-->
- <string name="location_service">Location Service</string>
- <!-- Feature Id for Sensor Notification service. [CHAR LIMIT=NONE]-->
- <string name="sensor_notification_service">Sensor Notification Service</string>
- <!-- Feature Id for Twilight service. [CHAR LIMIT=NONE]-->
- <string name="twilight_service">Twilight Service</string>
<!-- Factory reset warning dialog strings--> <skip />
<!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b8dd418..85c2a2a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3874,6 +3874,7 @@
<java-symbol type="id" name="resolver_empty_state_title" />
<java-symbol type="id" name="resolver_empty_state_subtitle" />
<java-symbol type="id" name="resolver_empty_state_button" />
+ <java-symbol type="id" name="resolver_tab_divider" />
<java-symbol type="string" name="resolver_cant_share_with_work_apps" />
<java-symbol type="string" name="resolver_cant_share_with_personal_apps" />
<java-symbol type="string" name="resolver_cant_share_cross_profile_explanation" />
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index ea0a0fd..37e8d59 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -244,6 +244,28 @@
}
@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/CutoutSpecificationTest.java b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
index 1f831bb..b41f90c 100644
--- a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
+++ b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
@@ -69,6 +69,13 @@
+ "z\n"
+ "@right\n"
+ "@bind_right_cutout\n"
+ + "@bottom\n"
+ + "M 0,0\n"
+ + "h -24\n"
+ + "v -48\n"
+ + "h 48\n"
+ + "v 48\n"
+ + "z\n"
+ "@dp";
private static final String CORNER_CUTOUT_SPECIFICATION = "M 0,0\n"
+ "h 1\n"
@@ -141,13 +148,66 @@
}
@Test
+ public void parse_withBindMarker_shouldHaveTopBound() {
+ CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getTopBound()).isEqualTo(new Rect(0, 0, 168, 168));
+ }
+
+ @Test
public void parse_withBindMarker_shouldHaveRightBound() {
CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
assertThat(cutoutSpecification.getRightBound()).isEqualTo(new Rect(912, 960, 1080, 1128));
}
@Test
- public void parse_tallCutout_shouldBeDone() {
+ public void parse_withBindMarker_shouldHaveBottomBound() {
+ CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getBottomBound()).isEqualTo(new Rect(456, 1752, 624, 1920));
+ }
+
+ @Test
+ public void parse_withBindMarker_shouldMatchExpectedSafeInset() {
+ CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getSafeInset()).isEqualTo(new Rect(168, 168, 168, 168));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveLeftBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getLeftBound()).isEqualTo(new Rect(0, 540, 168, 708));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveTopBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getTopBound()).isEqualTo(new Rect(0, 0, 168, 168));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveRightBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getRightBound()).isEqualTo(new Rect(1752, 540, 1920, 708));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldHaveBottomBound() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getBottomBound()).isEqualTo(new Rect(876, 912, 1044, 1080));
+ }
+
+ @Test
+ public void parse_withBindMarker_tabletLikeDevice_shouldMatchExpectedSafeInset() {
+ CutoutSpecification cutoutSpecification = new CutoutSpecification.Parser(3.5f, 1920, 1080)
+ .parse(WITH_BIND_CUTOUT_SPECIFICATION);
+ assertThat(cutoutSpecification.getSafeInset()).isEqualTo(new Rect(168, 0, 168, 168));
+ }
+
+ @Test
+ public void parse_tallCutout_topBoundShouldMatchExpectedHeight() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -48, 0\n"
+ "L -44.3940446283, 36.0595537175\n"
@@ -162,7 +222,7 @@
}
@Test
- public void parse_wideCutout_shouldBeDone() {
+ public void parse_wideCutout_topBoundShouldMatchExpectedWidth() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -72, 0\n"
+ "L -69.9940446283, 20.0595537175\n"
@@ -177,7 +237,7 @@
}
@Test
- public void parse_narrowCutout_shouldBeDone() {
+ public void parse_narrowCutout_topBoundShouldHaveExpectedWidth() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -24, 0\n"
+ "L -21.9940446283, 20.0595537175\n"
@@ -192,7 +252,7 @@
}
@Test
- public void parse_doubleCutout_shouldBeDone() {
+ public void parse_doubleCutout_topBoundShouldHaveExpectedHeight() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -72, 0\n"
+ "L -69.9940446283, 20.0595537175\n"
@@ -217,7 +277,7 @@
}
@Test
- public void parse_cornerCutout_shouldBeDone() {
+ public void parse_cornerCutout_topBoundShouldHaveExpectedHeight() {
CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+ "L -48, 0\n"
+ "C -48,48 -48,48 0,48\n"
@@ -229,7 +289,7 @@
}
@Test
- public void parse_holeCutout_shouldBeDone() {
+ public void parse_holeCutout_shouldMatchExpectedInset() {
CutoutSpecification cutoutSpecification = mParser.parse("M 20.0,20.0\n"
+ "h 136\n"
+ "v 136\n"
@@ -259,4 +319,38 @@
assertThat(cutoutSpecification.getSafeInset())
.isEqualTo(new Rect(6, 0, 8, 0));
}
+
+ @Test
+ public void parse_bottomLeftSpec_withBindLeftMarker_shouldBeLeftBound() {
+ CutoutSpecification cutoutSpecification =
+ new CutoutSpecification.Parser(2f, 400, 200)
+ .parse("@bottom"
+ + "M 0,0\n"
+ + "v -10\n"
+ + "h 10\n"
+ + "v 10\n"
+ + "z\n"
+ + "@left\n"
+ + "@bind_left_cutout");
+
+ assertThat(cutoutSpecification.getLeftBound())
+ .isEqualTo(new Rect(0, 190, 10, 200));
+ }
+
+ @Test
+ public void parse_bottomRightSpec_withBindRightMarker_shouldBeRightBound() {
+ CutoutSpecification cutoutSpecification =
+ new CutoutSpecification.Parser(2f, 400, 200)
+ .parse("@bottom"
+ + "M 0,0\n"
+ + "v -10\n"
+ + "h 10\n"
+ + "v 10\n"
+ + "z\n"
+ + "@right\n"
+ + "@bind_right_cutout");
+
+ assertThat(cutoutSpecification.getRightBound())
+ .isEqualTo(new Rect(390, 190, 400, 200));
+ }
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index ade1e0d..79e7c50 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 @@
// 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/libs/hwui/protos/graphicsstats.proto b/libs/hwui/protos/graphicsstats.proto
index dd5676c..745393c 100644
--- a/libs/hwui/protos/graphicsstats.proto
+++ b/libs/hwui/protos/graphicsstats.proto
@@ -30,8 +30,9 @@
message GraphicsStatsProto {
enum PipelineType {
- GL = 0;
- VULKAN = 1;
+ UNKNOWN = 0;
+ GL = 1;
+ VULKAN = 2;
}
// The package name of the app
diff --git a/packages/SystemUI/res/layout/qs_carrier.xml b/packages/SystemUI/res/layout/qs_carrier.xml
index 28b2d21..a5b8cfa 100644
--- a/packages/SystemUI/res/layout/qs_carrier.xml
+++ b/packages/SystemUI/res/layout/qs_carrier.xml
@@ -14,7 +14,7 @@
~ limitations under the License
-->
-<com.android.systemui.qs.QSCarrier
+<com.android.systemui.qs.carrier.QSCarrier
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linear_carrier"
android:layout_width="wrap_content"
@@ -46,4 +46,4 @@
android:singleLine="true"
android:maxEms="7"/>
-</com.android.systemui.qs.QSCarrier>
\ No newline at end of file
+</com.android.systemui.qs.carrier.QSCarrier>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_carrier_group.xml b/packages/SystemUI/res/layout/qs_carrier_group.xml
index f2b0606..fd53a8b 100644
--- a/packages/SystemUI/res/layout/qs_carrier_group.xml
+++ b/packages/SystemUI/res/layout/qs_carrier_group.xml
@@ -15,7 +15,7 @@
-->
<!-- Extends LinearLayout -->
-<com.android.systemui.qs.QSCarrierGroup
+<com.android.systemui.qs.carrier.QSCarrierGroup
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/qs_mobile"
android:layout_width="0dp"
@@ -71,4 +71,4 @@
android:layout_weight="1"
android:visibility="gone"/>
-</com.android.systemui.qs.QSCarrierGroup>
\ No newline at end of file
+</com.android.systemui.qs.carrier.QSCarrierGroup>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 019cb14..17aaff1 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.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.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 @@
}
}
+ @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/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 8cd70cf..d422dd7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -60,6 +60,7 @@
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.qs.QSDetail.Callback;
+import com.android.systemui.qs.carrier.QSCarrierGroup;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
@@ -435,23 +436,22 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ // Handle padding of SystemIconsView
DisplayCutout cutout = insets.getDisplayCutout();
-
- // Handle padding of QuickStatusBarHeader
Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
cutout, getDisplay());
Pair<Integer, Integer> padding =
StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
cutout, cornerCutoutPadding, mRoundedCornerPadding);
- setPadding(padding.first, 0, padding.second, getPaddingBottom());
-
- // Handle padding of SystemIconsView
final int waterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
- mSystemIconsView.setPaddingRelative(
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start),
- waterfallTopInset,
- getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end),
- 0);
+ int statusBarPaddingLeft = isLayoutRtl()
+ ? getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end)
+ : getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start);
+ int statusBarPaddingRight = isLayoutRtl()
+ ? getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start)
+ : getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end);
+ mSystemIconsView.setPadding(padding.first + statusBarPaddingLeft, waterfallTopInset,
+ padding.second + statusBarPaddingRight, 0);
return super.onApplyWindowInsets(insets);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 867677a..d899acb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs;
import com.android.systemui.R;
+import com.android.systemui.qs.carrier.QSCarrierGroupController;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
new file mode 100644
index 0000000..663f3f0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.qs.carrier
+
+/**
+ * Represents the state of cell signal for a particular slot.
+ *
+ * To be used between [QSCarrierGroupController] and [QSCarrier].
+ */
+data class CellSignalState(
+ @JvmField val visible: Boolean = false,
+ @JvmField val mobileSignalIconId: Int = 0,
+ @JvmField val contentDescription: String? = null,
+ @JvmField val typeContentDescription: String? = null,
+ @JvmField val roaming: Boolean = false
+) {
+ /**
+ * Changes the visibility of this state by returning a copy with the visibility changed.
+ *
+ * If the visibility would not change, the same state is returned.
+ *
+ * @param visible the new visibility state
+ * @return `this` if `this.visible == visible`. Else, a new copy with the visibility changed.
+ */
+ fun changeVisibility(visible: Boolean): CellSignalState {
+ if (this.visible == visible) return this
+ else return copy(visible = visible)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
rename to packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 5a9c360..ad275f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -29,6 +29,7 @@
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.DualToneHandler;
import com.android.systemui.R;
+import com.android.systemui.qs.QuickStatusBarHeader;
import java.util.Objects;
@@ -41,7 +42,7 @@
private DualToneHandler mDualToneHandler;
private ColorStateList mColorForegroundStateList;
private float mColorForegroundIntensity;
- private QSCarrierGroupController.CellSignalState mLastSignalState;
+ private CellSignalState mLastSignalState;
public QSCarrier(Context context) {
super(context);
@@ -76,8 +77,13 @@
mColorForegroundIntensity = QuickStatusBarHeader.getColorIntensity(colorForeground);
}
- public void updateState(QSCarrierGroupController.CellSignalState state) {
- if (Objects.equals(state, mLastSignalState)) return;
+ /**
+ * Update the state of this view
+ * @param state the current state of the signal for this view
+ * @return true if the state was actually changed
+ */
+ public boolean updateState(CellSignalState state) {
+ if (Objects.equals(state, mLastSignalState)) return false;
mLastSignalState = state;
mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
if (state.visible) {
@@ -103,6 +109,7 @@
}
mMobileSignal.setContentDescription(contentDescription);
}
+ return true;
}
private boolean hasValidTypeContentDescription(String typeContentDescription) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
rename to packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java
index 346c75d..d03563f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroup.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java
rename to packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index eb5b4cc..f9b1473 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
@@ -29,7 +29,6 @@
import android.view.View;
import android.widget.TextView;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.keyguard.CarrierTextController;
@@ -38,7 +37,6 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.NetworkController;
-import java.util.Objects;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -82,11 +80,13 @@
Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
return;
}
- mInfos[slotIndex].visible = statusIcon.visible;
- mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
- mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
- mInfos[slotIndex].typeContentDescription = typeContentDescription.toString();
- mInfos[slotIndex].roaming = roaming;
+ mInfos[slotIndex] = new CellSignalState(
+ statusIcon.visible,
+ statusIcon.icon,
+ statusIcon.contentDescription,
+ typeContentDescription.toString(),
+ roaming
+ );
mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
}
@@ -94,7 +94,7 @@
public void setNoSims(boolean hasNoSims, boolean simDetected) {
if (hasNoSims) {
for (int i = 0; i < SIM_SLOTS; i++) {
- mInfos[i].visible = false;
+ mInfos[i] = mInfos[i].changeVisibility(false);
}
}
mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
@@ -236,7 +236,7 @@
+ info.subscriptionIds[i]);
continue;
}
- mInfos[slot].visible = true;
+ mInfos[slot] = mInfos[slot].changeVisibility(true);
slotSeen[slot] = true;
mCarrierGroups[slot].setCarrierText(
info.listOfCarriers[i].toString().trim());
@@ -244,7 +244,7 @@
}
for (int i = 0; i < SIM_SLOTS; i++) {
if (!slotSeen[i]) {
- mInfos[i].visible = false;
+ mInfos[i] = mInfos[i].changeVisibility(false);
mCarrierGroups[i].setVisibility(View.GONE);
}
}
@@ -255,7 +255,7 @@
// No sims or airplane mode (but not WFC). Do not show QSCarrierGroup, instead just show
// info.carrierText in a different view.
for (int i = 0; i < SIM_SLOTS; i++) {
- mInfos[i].visible = false;
+ mInfos[i] = mInfos[i].changeVisibility(false);
mCarrierGroups[i].setCarrierText("");
mCarrierGroups[i].setVisibility(View.GONE);
}
@@ -295,35 +295,6 @@
}
}
- static final class CellSignalState {
- boolean visible;
- int mobileSignalIconId;
- String contentDescription;
- String typeContentDescription;
- boolean roaming;
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj) return true;
- if (!(obj instanceof CellSignalState)) return false;
- CellSignalState other = (CellSignalState) obj;
- return this.visible == other.visible
- && this.mobileSignalIconId == other.mobileSignalIconId
- && Objects.equals(this.contentDescription, other.contentDescription)
- && Objects.equals(this.typeContentDescription, other.typeContentDescription)
- && this.roaming == other.roaming;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(visible,
- mobileSignalIconId,
- contentDescription,
- typeContentDescription,
- roaming);
- }
- }
-
public static class Builder {
private QSCarrierGroup mView;
private final ActivityStarter mActivityStarter;
@@ -343,7 +314,7 @@
mCarrierTextControllerBuilder = carrierTextControllerBuilder;
}
- Builder setQSCarrierGroup(QSCarrierGroup view) {
+ public Builder setQSCarrierGroup(QSCarrierGroup view) {
mView = view;
return this;
}
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 2981252..e612c07 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.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 @@
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 @@
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 @@
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 4672de0..e15fa2e 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 @@
interface PeopleNotificationIdentifier {
fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking): Boolean
+ fun isImportantPeopleNotification(sbn: StatusBarNotification, ranking: Ranking): Boolean
}
@Singleton
@@ -32,4 +33,7 @@
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 55173182..234ab93 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 @@
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/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 248e5fe..60eda06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -22,6 +22,7 @@
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
+import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -112,7 +113,8 @@
boolean mSkipPost = false;
@Retention(SOURCE)
- @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE})
+ @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE,
+ ACTION_UNBUBBLE, ACTION_SETTINGS})
private @interface Action {}
static final int ACTION_BUBBLE = 0;
static final int ACTION_HOME = 1;
@@ -128,8 +130,6 @@
mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
} else {
mBubbleController.onUserCreatedBubbleFromNotification(mEntry);
- Settings.Global.putInt(
- mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES, 1);
}
closeControls(v, true);
};
@@ -225,7 +225,9 @@
mShortcutInfo = shortcuts.get(0);
}
- mIsBubbleable = mEntry.getBubbleMetadata() != null;
+ mIsBubbleable = mEntry.getBubbleMetadata() != null
+ && Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.NOTIFICATION_BUBBLES, 0) == 1;
mStartedAsBubble = mEntry.isBubble();
createConversationChannelIfNeeded();
@@ -382,6 +384,11 @@
((TextView) findViewById(R.id.pkg_name)).setText(mAppName);
}
+ private boolean bubbleImportantConversations() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ BUBBLE_IMPORTANT_CONVERSATIONS, 1) == 1;
+ }
+
private void bindDelegate() {
TextView delegateView = findViewById(R.id.delegate_name);
@@ -579,6 +586,10 @@
case ACTION_FAVORITE:
mChannelToUpdate.setImportantConversation(
!mChannelToUpdate.isImportantConversation());
+ if (mChannelToUpdate.isImportantConversation()
+ && bubbleImportantConversations()) {
+ mChannelToUpdate.setAllowBubbles(true);
+ }
break;
case ACTION_MUTE:
if (mChannelToUpdate.getImportance() == IMPORTANCE_UNSPECIFIED
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 fa4bc2a..52f7c2c 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 @@
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 @@
/**
* 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 @@
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 @@
if (mImageCache != null) {
mImageCache.setImageResolver(this);
}
+
+ updateMaxImageSizes();
}
/**
@@ -66,14 +84,49 @@
return mImageCache != null && !ActivityManager.isLowRamDeviceStatic();
}
+ private boolean isLowRam() {
+ return ActivityManager.isLowRamDeviceStatic();
+ }
+
/**
- * To resolve image from specified uri directly.
+ * 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. 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/qs/carrier/CellSignalStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt
new file mode 100644
index 0000000..75be74b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.qs.carrier
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertNotSame
+import org.junit.Assert.assertSame
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class CellSignalStateTest : SysuiTestCase() {
+
+ @Test
+ fun testChangeVisibility_sameObject() {
+ val c = CellSignalState()
+
+ val other = c.changeVisibility(c.visible)
+
+ assertSame(c, other)
+ }
+
+ @Test
+ fun testChangeVisibility_otherObject() {
+ val c = CellSignalState()
+
+ val other = c.changeVisibility(!c.visible)
+
+ assertNotSame(c, other)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 715087d..fa02231 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs;
+package com.android.systemui.qs.carrier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
new file mode 100644
index 0000000..022dc84
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.qs.carrier;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class QSCarrierTest extends SysuiTestCase {
+
+ private QSCarrier mQSCarrier;
+ private TestableLooper mTestableLooper;
+
+ @Before
+ public void setUp() throws Exception {
+ mTestableLooper = TestableLooper.get(this);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ mTestableLooper.runWithLooper(() ->
+ mQSCarrier = (QSCarrier) inflater.inflate(R.layout.qs_carrier, null));
+ }
+
+ @Test
+ public void testUpdateState_first() {
+ CellSignalState c = new CellSignalState(true, 0, "", "", false);
+
+ assertTrue(mQSCarrier.updateState(c));
+ }
+
+ @Test
+ public void testUpdateState_same() {
+ CellSignalState c = new CellSignalState(true, 0, "", "", false);
+
+ assertTrue(mQSCarrier.updateState(c));
+ assertFalse(mQSCarrier.updateState(c));
+ }
+
+ @Test
+ public void testUpdateState_changed() {
+ CellSignalState c = new CellSignalState(true, 0, "", "", false);
+
+ assertTrue(mQSCarrier.updateState(c));
+
+ CellSignalState other = c.changeVisibility(false);
+
+ assertTrue(mQSCarrier.updateState(other));
+ }
+}
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 c6b496d..8e330c6 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 @@
}
@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/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 27b263f..138ea39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -21,6 +21,8 @@
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
+import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
+import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -55,6 +57,7 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -449,6 +452,7 @@
@Test
public void testBindNotification_bubbleActionVisibleWhenCanBubble() {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -469,7 +473,8 @@
}
@Test
- public void testBindNotification_bubbleActionVisibleWhenCannotBubble() {
+ public void testBindNotification_bubbleAction_noBubbleMetadata() {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -490,6 +495,28 @@
}
@Test
+ public void testBindNotification_bubbleActionGloballyOff() {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 0);
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mLauncherApps,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mBubbleEntry,
+ null,
+ null,
+ null,
+ mIconFactory,
+ true);
+
+ View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
+ assertEquals(View.GONE, bubbleView.getVisibility());
+ }
+
+ @Test
public void testAddToHome() throws Exception {
when(mShortcutManager.isRequestPinShortcutSupported()).thenReturn(true);
@@ -550,6 +577,7 @@
@Test
public void testBubble_promotesBubble() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationChannel.setAllowBubbles(false);
mConversationChannel.setAllowBubbles(false);
@@ -584,6 +612,7 @@
@Test
public void testBubble_demotesBubble() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
mNotificationInfo.bindNotification(
@@ -617,6 +646,7 @@
@Test
public void testBubble_noChannelChange() throws Exception {
+ Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -645,7 +675,11 @@
}
@Test
- public void testFavorite_favorite() throws Exception {
+ public void testFavorite_favorite_noBubble() throws Exception {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ BUBBLE_IMPORTANT_CONVERSATIONS, 0);
+ mNotificationChannel.setAllowBubbles(false);
+ mConversationChannel.setAllowBubbles(false);
mNotificationInfo.bindNotification(
mShortcutManager,
mLauncherApps,
@@ -673,6 +707,44 @@
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
anyString(), anyInt(), captor.capture());
assertTrue(captor.getValue().isImportantConversation());
+ assertFalse(captor.getValue().canBubble());
+ verify(mBubbleController, never()).onUserCreatedBubbleFromNotification(mEntry);
+ }
+
+ @Test
+ public void testFavorite_favorite_bubble() throws Exception {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ BUBBLE_IMPORTANT_CONVERSATIONS, 1);
+ mNotificationChannel.setAllowBubbles(false);
+ mConversationChannel.setAllowBubbles(false);
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mLauncherApps,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mEntry,
+ null,
+ null,
+ null,
+ mIconFactory,
+ true);
+
+ ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
+ assertEquals(mContext.getString(R.string.notification_conversation_unfavorite),
+ fave.getContentDescription().toString());
+
+ fave.performClick();
+ mTestableLooper.processAllMessages();
+
+ ArgumentCaptor<NotificationChannel> captor =
+ ArgumentCaptor.forClass(NotificationChannel.class);
+ verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+ anyString(), anyInt(), captor.capture());
+ assertTrue(captor.getValue().isImportantConversation());
+ assertTrue(captor.getValue().canBubble());
}
@Test
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 0000000..7f48cd1
--- /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 e5ee439..d81b8c2 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.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 @@
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 416f448..c77e75d 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -127,6 +127,16 @@
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 0000000..0b8658c
--- /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/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7083281..f7eabac 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3430,16 +3430,16 @@
// there is hope for it to become one if it validated, then it is needed.
ensureRunningOnConnectivityServiceThread();
if (nri.request.isRequest() && nai.satisfies(nri.request) &&
- (nai.isSatisfyingRequest(nri.request.requestId) ||
- // Note that this catches two important cases:
- // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
- // is currently satisfying the request. This is desirable when
- // cellular ends up validating but WiFi does not.
- // 2. Unvalidated WiFi will not be reaped when validated cellular
- // is currently satisfying the request. This is desirable when
- // WiFi ends up validating and out scoring cellular.
- nri.mSatisfier.getCurrentScore()
- < nai.getCurrentScoreAsValidated())) {
+ (nai.isSatisfyingRequest(nri.request.requestId)
+ // Note that canPossiblyBeat catches two important cases:
+ // 1. Unvalidated slow networks will not be reaped when an unvalidated fast
+ // network is currently satisfying the request. This is desirable for example
+ // when cellular ends up validating but WiFi/Ethernet does not.
+ // 2. Fast networks will not be reaped when a validated slow network is
+ // currently satisfying the request. This is desirable for example when
+ // Ethernet ends up validating and out scoring WiFi, or WiFi/Ethernet ends
+ // up validating and out scoring cellular.
+ || nai.canPossiblyBeat(nri.mSatisfier))) {
return false;
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 207a6aa..5db5115 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -194,8 +194,6 @@
// time
private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
- private static final String FEATURE_ID = "LocationService";
-
private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
private final Object mLock = new Object();
@@ -215,11 +213,6 @@
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;
@@ -245,7 +238,7 @@
private int mBatterySaverMode;
private LocationManagerService(Context context) {
- mContext = context.createFeatureContext(FEATURE_ID);
+ mContext = context;
mHandler = FgThread.getHandler();
mLocalService = new LocalService();
@@ -287,8 +280,6 @@
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 =
@@ -665,6 +656,8 @@
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;
@@ -682,6 +675,7 @@
private LocationProviderManager(String name) {
mName = name;
+ mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
mEnabled = new SparseArray<>(2);
mLastLocation = new SparseArray<>(2);
mLastCoarseLocation = new SparseArray<>(2);
@@ -706,7 +700,9 @@
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);
@@ -721,6 +717,8 @@
mLastCoarseLocation.setValueAt(i, null);
}
}
+
+ mLocationFudger.resetOffsets();
}
}
}
diff --git a/services/core/java/com/android/server/SensorNotificationService.java b/services/core/java/com/android/server/SensorNotificationService.java
index 9082dca..7f5befa 100644
--- a/services/core/java/com/android/server/SensorNotificationService.java
+++ b/services/core/java/com/android/server/SensorNotificationService.java
@@ -16,8 +16,10 @@
package com.android.server;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.GeomagneticField;
import android.hardware.Sensor;
import android.hardware.SensorAdditionalInfo;
@@ -29,7 +31,9 @@
import android.location.LocationManager;
import android.os.Bundle;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
public class SensorNotificationService extends SystemService
@@ -48,8 +52,6 @@
private static final long MILLIS_2010_1_1 = 1262358000000l;
- private static final String FEATURE_ID = "SensorNotificationService";
-
private Context mContext;
private SensorManager mSensorManager;
private LocationManager mLocationManager;
@@ -59,8 +61,8 @@
private long mLocalGeomagneticFieldUpdateTime = -LOCATION_MIN_TIME;
public SensorNotificationService(Context context) {
- super(context.createFeatureContext(FEATURE_ID));
- mContext = getContext();
+ super(context);
+ mContext = context;
}
public void onStart() {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 53dbb93..3b0a1a3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4409,6 +4409,25 @@
}
}
+ @Override
+ public void prepareAppDataAfterInstall(String packageName, int uid) {
+ int userId = UserHandle.getUserId(uid);
+ final Environment.UserEnvironment userEnv = new Environment.UserEnvironment(userId);
+
+ // The installer may have downloaded OBBs for this newly installed application;
+ // make sure the OBB dir for the application is setup correctly, if it exists.
+ File[] packageObbDirs = userEnv.buildExternalStorageAppObbDirs(packageName);
+ for (File packageObbDir : packageObbDirs) {
+ try {
+ mVold.fixupAppDir(packageObbDir.getCanonicalPath() + "/", uid);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to get canonical path for " + packageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to fixup app dir for " + packageName);
+ }
+ }
+ }
+
public boolean hasExternalStorage(int uid, String packageName) {
// No need to check for system uid. This avoids a deadlock between
// PackageManagerService and AppOpsService.
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 430a5b9..50f43b5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -49,6 +49,7 @@
import android.app.PendingIntent;
import android.app.Service;
import android.app.ServiceStartArgs;
+import android.app.admin.DevicePolicyEventLogger;
import android.appwidget.AppWidgetManagerInternal;
import android.content.ComponentName;
import android.content.ComponentName.WithComponentName;
@@ -79,6 +80,7 @@
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
import android.provider.Settings;
+import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -1912,6 +1914,8 @@
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
+ maybeLogBindCrossProfileService(userId, callingPackage, callerApp.info.uid);
+
getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);
} finally {
@@ -1921,6 +1925,21 @@
return 1;
}
+ private void maybeLogBindCrossProfileService(
+ int userId, String callingPackage, int callingUid) {
+ if (UserHandle.isCore(callingUid)) {
+ return;
+ }
+ final int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId == userId
+ || !mAm.mUserController.isSameProfileGroup(callingUserId, userId)) {
+ return;
+ }
+ DevicePolicyEventLogger.createEvent(DevicePolicyEnums.BIND_CROSS_PROFILE_SERVICE)
+ .setStrings(callingPackage)
+ .write();
+ }
+
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
final long origId = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 4612cfd..3860904 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.transportNamesOf;
import android.annotation.NonNull;
@@ -475,24 +477,16 @@
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
}
- private int getCurrentScore(boolean pretendValidated) {
- // TODO: We may want to refactor this into a NetworkScore class that takes a base score from
- // the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the
- // score. The NetworkScore class would provide a nice place to centralize score constants
- // so they are not scattered about the transports.
-
+ /** Gets the current score */
+ public int getCurrentScore() {
// If this network is explicitly selected and the user has decided to use it even if it's
- // unvalidated, give it the maximum score. Also give it the maximum score if it's explicitly
- // selected and we're trying to see what its score could be. This ensures that we don't tear
- // down an explicitly selected network before the user gets a chance to prefer it when
- // a higher-scoring network (e.g., Ethernet) is available.
- if (networkAgentConfig.explicitlySelected
- && (networkAgentConfig.acceptUnvalidated || pretendValidated)) {
+ // unvalidated, give it the maximum score.
+ if (networkAgentConfig.explicitlySelected && networkAgentConfig.acceptUnvalidated) {
return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
}
int score = mNetworkScore.getLegacyScore();
- if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
+ if (!lastValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
}
if (score < 0) score = 0;
@@ -508,18 +502,6 @@
return isWifi && !avoidBadWifi && everValidated;
}
- // Get the current score for this Network. This may be modified from what the
- // NetworkAgent sent, as it has modifiers applied to it.
- public int getCurrentScore() {
- return getCurrentScore(false);
- }
-
- // Get the current score for this Network as if it was validated. This may be modified from
- // what the NetworkAgent sent, as it has modifiers applied to it.
- public int getCurrentScoreAsValidated() {
- return getCurrentScore(true);
- }
-
public void setNetworkScore(@NonNull NetworkScore ns) {
mNetworkScore = ns;
}
@@ -629,6 +611,41 @@
mLingering = false;
}
+ /**
+ * Returns whether this NAI has any chance of ever beating this other agent.
+ *
+ * The chief use case of this is the decision to tear down this network. ConnectivityService
+ * tears down networks that don't satisfy any request, unless they have a chance to beat any
+ * existing satisfier.
+ *
+ * @param other the agent to beat
+ * @return whether this should be given more time to try and beat the other agent
+ * TODO : remove this and migrate to a ranker-based approach
+ */
+ public boolean canPossiblyBeat(@NonNull final NetworkAgentInfo other) {
+ // Any explicitly selected network should be held on.
+ if (networkAgentConfig.explicitlySelected) return true;
+ // An outscored exiting network should be torn down.
+ if (mNetworkScore.isExiting()) return false;
+ // If this network is validated it can be torn down as it can't hope to be better than
+ // it already is.
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) return false;
+ // If neither network is validated, keep both until at least one does.
+ if (!other.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) return true;
+ // If this network is not metered but the other is, it should be preferable if it validates.
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
+ && !other.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+ return true;
+ }
+
+ // If the control comes here :
+ // • This network is neither exiting or explicitly selected
+ // • This network is not validated, but the other is
+ // • This network is metered, or both networks are unmetered
+ // Keep it if it's expected to be faster than the other., should it validate.
+ return mNetworkScore.probablyFasterThan(other.mNetworkScore);
+ }
+
public void dumpLingerTimers(PrintWriter pw) {
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
index c536ab2..80d46e0 100644
--- a/services/core/java/com/android/server/connectivity/NetworkRanker.java
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -16,6 +16,9 @@
package com.android.server.connectivity;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkScore.POLICY_IGNORE_ON_WIFI;
import static com.android.internal.util.FunctionalUtils.findFirst;
@@ -42,13 +45,20 @@
@NonNull final Collection<NetworkAgentInfo> nais) {
final ArrayList<NetworkAgentInfo> candidates = new ArrayList<>(nais);
candidates.removeIf(nai -> !nai.satisfies(request));
- // Enforce policy.
- filterBadWifiAvoidancePolicy(candidates);
+
+ // Enforce policy. The order in which the policy is computed is essential, because each
+ // step may remove some of the candidates. For example, filterValidated drops non-validated
+ // networks in presence of validated networks for INTERNET requests, but the bad wifi
+ // avoidance policy takes priority over this, so it must be done before.
+ filterVpn(candidates);
+ filterExplicitlySelected(candidates);
+ filterBadWifiAvoidance(candidates);
+ filterValidated(request, candidates);
NetworkAgentInfo bestNetwork = null;
int bestScore = Integer.MIN_VALUE;
for (final NetworkAgentInfo nai : candidates) {
- final int score = nai.getCurrentScore();
+ final int score = nai.getNetworkScore().getLegacyScore();
if (score > bestScore) {
bestNetwork = nai;
bestScore = score;
@@ -57,9 +67,27 @@
return bestNetwork;
}
- // If some network with wifi transport is present, drop all networks with POLICY_IGNORE_ON_WIFI.
- private void filterBadWifiAvoidancePolicy(
+ // If a network is a VPN it has priority.
+ private void filterVpn(@NonNull final ArrayList<NetworkAgentInfo> candidates) {
+ final NetworkAgentInfo vpn = findFirst(candidates,
+ nai -> nai.networkCapabilities.hasTransport(TRANSPORT_VPN));
+ if (null == vpn) return; // No VPN : this policy doesn't apply.
+ candidates.removeIf(nai -> !nai.networkCapabilities.hasTransport(TRANSPORT_VPN));
+ }
+
+ // If some network is explicitly selected and set to accept unvalidated connectivity, then
+ // drop all networks that are not explicitly selected.
+ private void filterExplicitlySelected(
@NonNull final ArrayList<NetworkAgentInfo> candidates) {
+ final NetworkAgentInfo explicitlySelected = findFirst(candidates,
+ nai -> nai.networkAgentConfig.explicitlySelected
+ && nai.networkAgentConfig.acceptUnvalidated);
+ if (null == explicitlySelected) return; // No explicitly selected network accepting unvalid
+ candidates.removeIf(nai -> !nai.networkAgentConfig.explicitlySelected);
+ }
+
+ // If some network with wifi transport is present, drop all networks with POLICY_IGNORE_ON_WIFI.
+ private void filterBadWifiAvoidance(@NonNull final ArrayList<NetworkAgentInfo> candidates) {
final NetworkAgentInfo wifi = findFirst(candidates,
nai -> nai.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
&& nai.everValidated
@@ -71,4 +99,16 @@
if (null == wifi) return; // No wifi : this policy doesn't apply
candidates.removeIf(nai -> nai.getNetworkScore().hasPolicy(POLICY_IGNORE_ON_WIFI));
}
+
+ // If some network is validated and the request asks for INTERNET, drop all networks that are
+ // not validated.
+ private void filterValidated(@NonNull final NetworkRequest request,
+ @NonNull final ArrayList<NetworkAgentInfo> candidates) {
+ if (!request.hasCapability(NET_CAPABILITY_INTERNET)) return;
+ final NetworkAgentInfo validated = findFirst(candidates,
+ nai -> nai.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED));
+ if (null == validated) return; // No validated network
+ candidates.removeIf(nai ->
+ !nai.networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED));
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index ac41434..18adc0b 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 @@
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 @@
|| 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 @@
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 @@
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 4ebbdda..e578ac1 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 @@
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 @@
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 @@
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 0c9445a..ac81a6c 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 @@
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 6174e54..b84d322 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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/CountryDetectorBase.java b/services/core/java/com/android/server/location/CountryDetectorBase.java
index b158388..8326ef9 100644
--- a/services/core/java/com/android/server/location/CountryDetectorBase.java
+++ b/services/core/java/com/android/server/location/CountryDetectorBase.java
@@ -31,15 +31,13 @@
* @hide
*/
public abstract class CountryDetectorBase {
- private static final String FEATURE_ID = "CountryDetector";
-
protected final Handler mHandler;
protected final Context mContext;
protected CountryListener mListener;
protected Country mDetectedCountry;
- public CountryDetectorBase(Context context) {
- mContext = context.createFeatureContext(FEATURE_ID);
+ public CountryDetectorBase(Context ctx) {
+ mContext = ctx;
mHandler = new Handler();
}
@@ -47,7 +45,7 @@
* Start detecting the country that the user is in.
*
* @return the country if it is available immediately, otherwise null should
- * be returned.
+ * be returned.
*/
public abstract Country detectCountry();
diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java
index a069e7a..1f458ed 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 @@
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/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 05867ba..f7e1398 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -214,6 +214,8 @@
* Updates the mSessionInfo. Returns true if the session info is changed.
*/
boolean updateSessionInfosIfNeededLocked() {
+ // Prevent to execute this method before mBtRouteProvider is created.
+ if (mBtRouteProvider == null) return false;
RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0);
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fd86f1d..0d402e5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3440,6 +3440,27 @@
}
@Override
+ public ParceledListSlice<ConversationChannelWrapper> getConversations(
+ boolean onlyImportant) {
+ enforceSystemOrSystemUI("getConversations");
+ ArrayList<ConversationChannelWrapper> conversations =
+ mPreferencesHelper.getConversations(onlyImportant);
+ for (ConversationChannelWrapper conversation : conversations) {
+ LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
+ .setPackage(conversation.getPkg())
+ .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+ .setShortcutIds(Arrays.asList(
+ conversation.getNotificationChannel().getConversationId()));
+ List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
+ query, UserHandle.of(UserHandle.getUserId(conversation.getUid())));
+ if (shortcuts != null && !shortcuts.isEmpty()) {
+ conversation.setShortcutInfo(shortcuts.get(0));
+ }
+ }
+ return new ParceledListSlice<>(conversations);
+ }
+
+ @Override
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
String pkg, int uid, boolean includeDeleted) {
enforceSystemOrSystemUI("getNotificationChannelGroupsForPackage");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 20c8625..b8186ed 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1186,6 +1186,44 @@
return groups;
}
+ public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
+ synchronized (mPackagePreferences) {
+ ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
+
+ for (PackagePreferences p : mPackagePreferences.values()) {
+ int N = p.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = p.channels.valueAt(i);
+ if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()
+ && (nc.isImportantConversation() || !onlyImportant)) {
+ ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+ conversation.setPkg(p.pkg);
+ conversation.setUid(p.uid);
+ conversation.setNotificationChannel(nc);
+ conversation.setParentChannelLabel(
+ p.channels.get(nc.getParentChannelId()).getName());
+ boolean blockedByGroup = false;
+ if (nc.getGroup() != null) {
+ NotificationChannelGroup group = p.groups.get(nc.getGroup());
+ if (group != null) {
+ if (group.isBlocked()) {
+ blockedByGroup = true;
+ } else {
+ conversation.setGroupLabel(group.getName());
+ }
+ }
+ }
+ if (!blockedByGroup) {
+ conversations.add(conversation);
+ }
+ }
+ }
+ }
+
+ return conversations;
+ }
+ }
+
public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) {
Objects.requireNonNull(pkg);
synchronized (mPackagePreferences) {
@@ -1199,6 +1237,8 @@
final NotificationChannel nc = r.channels.valueAt(i);
if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()) {
ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+ conversation.setPkg(r.pkg);
+ conversation.setUid(r.uid);
conversation.setNotificationChannel(nc);
conversation.setParentChannelLabel(
r.channels.get(nc.getParentChannelId()).getName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f6eb76b..2a3f7ed 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15158,10 +15158,13 @@
// We purposefully exclude FLAG_STORAGE_EXTERNAL here, since
// this task was only focused on moving data on internal storage.
+ // We don't want ART profiles cleared, because they don't move,
+ // so we would be deleting the only copy (b/149200535).
+ final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES;
for (int userId : userIds) {
try {
- mInstaller.destroyAppData(volumeUuid, move.packageName, userId,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE, 0);
+ mInstaller.destroyAppData(volumeUuid, move.packageName, userId, flags, 0);
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
@@ -21733,6 +21736,7 @@
}
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
+ StorageManagerInternal smInternal = mInjector.getStorageManagerInternal();
for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
final int flags;
if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
@@ -21746,6 +21750,13 @@
if (ps.getInstalled(user.id)) {
// TODO: when user data is locked, mark that we're still dirty
prepareAppDataLIF(pkg, user.id, flags);
+
+ if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+ // Prepare app data on external storage; currently this is used to
+ // setup any OBB dirs that were created by the installer correctly.
+ int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid()));
+ smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 377fd16..12f7d5c 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 @@
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 491c5ab..da3cbf9 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.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 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.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 @@
/** 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 @@
onTemperatureChanged(temperatures.get(i), false);
}
onTemperatureMapChangedLocked();
+ mTemperatureWatcher.updateSevereThresholds();
mHalReady.set(true);
}
}
@@ -462,6 +472,15 @@
}
}
+ @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 @@
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 @@
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
@@ -857,6 +886,12 @@
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
synchronized (mHalLock) {
try {
@@ -975,6 +1010,32 @@
}
@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 @@
}
}
+ 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/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index 761fbf8..e72ba8d 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -50,7 +50,6 @@
implements AlarmManager.OnAlarmListener, Handler.Callback, LocationListener {
private static final String TAG = "TwilightService";
- private static final String FEATURE_ID = "TwilightService";
private static final boolean DEBUG = false;
private static final int MSG_START_LISTENING = 1;
@@ -74,7 +73,7 @@
protected TwilightState mLastTwilightState;
public TwilightService(Context context) {
- super(context.createFeatureContext(FEATURE_ID));
+ super(context);
mHandler = new Handler(Looper.getMainLooper(), this);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 840abb1..1b21117 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_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.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 @@
}
/**
+ * 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 251d0f1..53dd326 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.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 @@
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 @@
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(
mTargetActivityRecord.token);
}
+ if (mTargetActivityRecord.hasFixedRotationTransform()) {
+ mTargetActivityRecord.clearFixedRotationTransform();
+ }
}
// Notify that the animation has ended
@@ -828,6 +837,19 @@
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 d23bf97..1e22141 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 @@
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 1180566..48c7812 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 @@
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 663bf4f..2499614 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 @@
@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 eaa0781..28612f1 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 @@
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 dd9cbd0..6b97c98 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.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 @@
*/
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 @@
private ShortcutServiceInternal mShortcutServiceInternal;
private PackageManagerInternal mPackageManagerInternal;
- private ShortcutManager mShortcutManager;
private UserManager mUserManager;
public DataManager(Context context) {
@@ -125,7 +121,6 @@
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 @@
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 @@
* 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 @@
* 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 @@
@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 @@
@Override
public void onReceive(Context context, Intent intent) {
- forAllPackages(PackageData::saveToDisk);
+ forAllUnlockedUsers(userData -> userData.forAllPackages(PackageData::saveToDisk));
}
}
@@ -809,9 +805,5 @@
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 44f3e35..19cf8af 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 @@
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 @@
/** 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 280ced3..90d8216 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 @@
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 @@
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 @@
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 2a78b6f..2c84f26 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.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 @@
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 69ca643..ae8d554 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.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 @@
@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 @@
.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 4123556..85b8fcb 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 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 @@
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 @@
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 e90cb46..ac0cac1 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.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 @@
@Mock
private IPowerManager mMockIPowerManager;
@Mock
+ private IThermalService mMockIThermalService;
+ @Mock
Context mContext;
@Before
@@ -84,7 +87,7 @@
// 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 9e98427..fa19814 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.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 @@
private HdmiPortInfo[] mHdmiPortInfo;
@Mock private IPowerManager mIPowerManagerMock;
+ @Mock private IThermalService mIThermalServiceMock;
@Before
public void setUp() throws Exception {
@@ -130,7 +132,8 @@
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 3ecd319..5e104a5 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.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.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 @@
@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 @@
private NotificationChannel mNotificationChannel;
private DataManager mDataManager;
- private int mCallingUserId;
private CancellationSignal mCancellationSignal;
private TestInjector mInjector;
@@ -145,14 +143,11 @@
}).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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 808906e..f498a94 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.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 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 @@
.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 ccf7ca9..624cb83 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.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 @@
@Mock
private IPowerManager mIPowerManagerMock;
@Mock
+ private IThermalService mIThermalServiceMock;
+ @Mock
private IThermalEventListener mEventListener1;
@Mock
private IThermalEventListener mEventListener2;
@@ -133,6 +137,12 @@
}
@Override
+ protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
+ int type) {
+ return new ArrayList<>();
+ }
+
+ @Override
protected boolean connectToHal() {
return true;
}
@@ -153,7 +163,7 @@
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 d5cdbeb..035a2f1 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.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 @@
private RecoverySystemService.UncryptSocket mUncryptSocket;
private Context mContext;
private IPowerManager mIPowerManager;
+ private IThermalService mIThermalService;
private FileWriter mUncryptUpdateFileWriter;
private LockSettingsInternal mLockSettingsInternal;
@@ -77,8 +79,9 @@
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 604fcd3..ccce043 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 @@
@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/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 0adf15c..7f9732b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -108,6 +108,7 @@
private static final int UID_N_MR1 = 0;
private static final UserHandle USER = UserHandle.of(0);
private static final int UID_O = 1111;
+ private static final int UID_P = 2222;
private static final String SYSTEM_PKG = "android";
private static final int SYSTEM_UID = 1000;
private static final UserHandle USER2 = UserHandle.of(10);
@@ -141,9 +142,11 @@
upgrade.targetSdkVersion = Build.VERSION_CODES.O;
when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
when(mPm.getApplicationInfoAsUser(eq(PKG_O), anyInt(), anyInt())).thenReturn(upgrade);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_P), anyInt(), anyInt())).thenReturn(upgrade);
when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
when(mPm.getPackageUidAsUser(eq(PKG_N_MR1), anyInt())).thenReturn(UID_N_MR1);
when(mPm.getPackageUidAsUser(eq(PKG_O), anyInt())).thenReturn(UID_O);
+ when(mPm.getPackageUidAsUser(eq(PKG_P), anyInt())).thenReturn(UID_P);
when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
PackageInfo info = mock(PackageInfo.class);
info.signatures = new Signature[] {mock(Signature.class)};
@@ -2945,6 +2948,92 @@
}
@Test
+ public void testGetConversations_all() {
+ String convoId = "convo";
+ NotificationChannel messages =
+ new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+ NotificationChannel calls =
+ new NotificationChannel("calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+ NotificationChannel p =
+ new NotificationChannel("p calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_P, UID_P, p, true, false);
+
+ NotificationChannel channel =
+ new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+ channel.setConversationId(messages.getId(), convoId);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+ NotificationChannel diffConvo =
+ new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+ diffConvo.setConversationId(p.getId(), "different convo");
+ mHelper.createNotificationChannel(PKG_P, UID_P, diffConvo, true, false);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("A person calls", "calls from A", IMPORTANCE_DEFAULT);
+ channel2.setConversationId(calls.getId(), convoId);
+ channel2.setImportantConversation(true);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+ List<ConversationChannelWrapper> convos = mHelper.getConversations(false);
+
+ assertEquals(3, convos.size());
+ assertTrue(conversationWrapperContainsChannel(convos, channel));
+ assertTrue(conversationWrapperContainsChannel(convos, diffConvo));
+ assertTrue(conversationWrapperContainsChannel(convos, channel2));
+ }
+
+ @Test
+ public void testGetConversations_onlyImportant() {
+ String convoId = "convo";
+ NotificationChannel messages =
+ new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+ NotificationChannel calls =
+ new NotificationChannel("calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+ NotificationChannel p =
+ new NotificationChannel("p calls", "Calls", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_P, UID_P, p, true, false);
+
+ NotificationChannel channel =
+ new NotificationChannel("A person msgs", "messages from A", IMPORTANCE_DEFAULT);
+ channel.setConversationId(messages.getId(), convoId);
+ channel.setImportantConversation(true);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+ NotificationChannel diffConvo =
+ new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+ diffConvo.setConversationId(p.getId(), "different convo");
+ diffConvo.setImportantConversation(true);
+ mHelper.createNotificationChannel(PKG_P, UID_P, diffConvo, true, false);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("A person calls", "calls from A", IMPORTANCE_DEFAULT);
+ channel2.setConversationId(calls.getId(), convoId);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+ List<ConversationChannelWrapper> convos = mHelper.getConversations(true);
+
+ assertEquals(2, convos.size());
+ assertTrue(conversationWrapperContainsChannel(convos, channel));
+ assertTrue(conversationWrapperContainsChannel(convos, diffConvo));
+ assertFalse(conversationWrapperContainsChannel(convos, channel2));
+ }
+
+ private boolean conversationWrapperContainsChannel(List<ConversationChannelWrapper> list,
+ NotificationChannel expected) {
+ for (ConversationChannelWrapper ccw : list) {
+ if (ccw.getNotificationChannel().equals(expected)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Test
public void testGetConversations_invalidPkg() {
assertThat(mHelper.getConversations("bad", 1)).isEmpty();
}
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 7753a32..6a8917c 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_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.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 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 @@
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/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b5663bd..851b052 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -260,4 +260,9 @@
public SurfaceControl.Transaction unsetColor(SurfaceControl sc) {
return this;
}
+
+ @Override
+ public SurfaceControl.Transaction setShadowRadius(SurfaceControl sc, float shadowRadius) {
+ return this;
+ }
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 0886975..220cdce 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2054,14 +2054,15 @@
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- // Bring up wifi with a score of 70.
+ // Bring up validated wifi.
// Cell is lingered because it would not satisfy any request, even if it validated.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- mWiFiNetworkAgent.adjustScore(50);
- mWiFiNetworkAgent.connect(false); // Score: 70
+ mWiFiNetworkAgent.connect(true); // Score: 60
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ // TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -5845,7 +5846,7 @@
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ trustedCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
verify(mNetworkManagementService).setDefaultNetId(eq(mWiFiNetworkAgent.getNetwork().netId));
reset(mNetworkManagementService);
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
index a6b371a..d2532c2 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
+++ b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -16,14 +16,18 @@
package com.android.server.connectivity
+import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.LinkProperties
+import android.net.Network
+import android.net.NetworkAgentConfig
import android.net.NetworkCapabilities
+import android.net.NetworkInfo
import android.net.NetworkRequest
+import android.net.NetworkScore
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import kotlin.test.assertEquals
import kotlin.test.assertNull
@@ -33,10 +37,24 @@
class NetworkRankerTest {
private val ranker = NetworkRanker()
- private fun makeNai(satisfy: Boolean, score: Int) = mock(NetworkAgentInfo::class.java).also {
- doReturn(satisfy).`when`(it).satisfies(any())
- doReturn(score).`when`(it).currentScore
- it.networkCapabilities = NetworkCapabilities()
+ private fun makeNai(satisfy: Boolean, score: Int) = object : NetworkAgentInfo(
+ null /* messenger */,
+ null /* asyncChannel*/,
+ Network(100),
+ NetworkInfo(TYPE_WIFI, 0 /* subtype */, "" /* typename */, "" /* subtypename */),
+ LinkProperties(),
+ NetworkCapabilities(),
+ NetworkScore.Builder().setLegacyScore(score).build(),
+ null /* context */,
+ null /* handler */,
+ NetworkAgentConfig(),
+ null /* connectivityService */,
+ null /* netd */,
+ null /* dnsResolver */,
+ null /* networkManagementService */,
+ 0 /* factorySerialNumber */) {
+ override fun satisfies(request: NetworkRequest?): Boolean = satisfy
+ override fun getCurrentScore(): Int = score
}
@Test
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1c330e2..3025caf 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -125,6 +125,8 @@
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 0db3313..ae5bf7d 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 @@
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 @@
/** 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 @@
mChannel = channel;
mSecurityType = securityType;
mMaxNumberOfClients = maxNumberOfClients;
+ mAutoShutdownEnabled = shutdownTimeoutEnabled;
mShutdownTimeoutMillis = shutdownTimeoutMillis;
mClientControlByUser = clientControlByUser;
mBlockedClientList = new ArrayList<>(blockedList);
@@ -274,6 +281,7 @@
&& 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 @@
@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 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 @@
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 @@
mChannel = 0;
mMaxNumberOfClients = 0;
mSecurityType = SECURITY_TYPE_OPEN;
+ mAutoShutdownEnabled = true; // enabled by default.
mShutdownTimeoutMillis = 0;
mClientControlByUser = false;
mBlockedClientList = new ArrayList<>();
@@ -588,6 +613,7 @@
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 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 @@
* @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 @@
}
/**
+ * 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 @@
*
* <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 f693315..af2f246 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2755,6 +2755,26 @@
}
/**
+ * 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 22d7786..44dbb98 100755
--- a/wifi/java/android/net/wifi/WifiOemMigrationHook.java
+++ b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
@@ -21,8 +21,10 @@
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 @@
*/
@SystemApi
public final class WifiOemMigrationHook {
+
+ private WifiOemMigrationHook() { }
+
/**
* Container for all the wifi config data to migrate.
*/
@@ -152,8 +157,6 @@
}
}
- private WifiOemMigrationHook() { }
-
/**
* Load data from OEM's config store.
* <p>
@@ -178,4 +181,263 @@
// 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 d958488..060ddf0 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -125,6 +125,7 @@
.setChannel(149, SoftApConfiguration.BAND_5GHZ)
.setHiddenSsid(true)
.setMaxNumberOfClients(10)
+ .setAutoShutdownEnabled(true)
.setShutdownTimeoutMillis(500000)
.enableClientControlByUser(true)
.setClientList(testBlockedClientList, testAllowedClientList)
@@ -136,6 +137,7 @@
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 853212a..234d929 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -2401,4 +2401,15 @@
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();
+ }
}