summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt36
-rw-r--r--api/system-current.txt23
-rw-r--r--api/test-current.txt12
-rw-r--r--core/java/android/app/ActivityManager.java2
-rw-r--r--core/java/android/app/ActivityThread.java3
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java21
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java29
-rw-r--r--core/java/android/content/ContentResolver.java25
-rw-r--r--core/java/android/content/om/OverlayInfo.java56
-rw-r--r--core/java/android/content/pm/PackageManager.java2
-rw-r--r--core/java/android/hardware/camera2/CaptureFailure.java18
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java4
-rw-r--r--core/java/android/hardware/camera2/impl/CaptureResultExtras.java19
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java4
-rw-r--r--core/java/android/net/IpPrefix.java20
-rw-r--r--core/java/android/net/LinkAddress.java15
-rw-r--r--core/java/android/net/NetworkCapabilities.aidl2
-rw-r--r--core/java/android/net/RouteInfo.java63
-rw-r--r--core/java/android/net/apf/ApfCapabilities.java22
-rw-r--r--core/java/android/provider/MediaStore.java33
-rw-r--r--core/java/android/text/format/Formatter.java7
-rw-r--r--core/java/android/view/GestureDetector.java78
-rw-r--r--core/java/android/view/InsetsState.java79
-rw-r--r--core/java/android/view/View.java68
-rw-r--r--core/java/android/view/ViewRootImpl.java16
-rw-r--r--core/java/android/view/WindowInsets.java7
-rw-r--r--core/java/android/webkit/WebViewFactory.java46
-rw-r--r--core/java/android/webkit/WebViewZygote.java46
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java48
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java52
-rw-r--r--core/proto/android/os/incident.proto7
-rw-r--r--core/proto/android/service/restricted_image.proto58
-rw-r--r--core/res/AndroidManifest.xml3
-rw-r--r--core/res/res/values/config.xml11
-rw-r--r--core/res/res/values/dimens.xml3
-rw-r--r--core/res/res/values/strings.xml4
-rw-r--r--core/res/res/values/symbols.xml5
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java21
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp17
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp1
-rw-r--r--libs/hwui/renderthread/VulkanManager.h3
-rw-r--r--media/java/android/media/AudioAttributes.java4
-rw-r--r--packages/CaptivePortalLogin/AndroidManifest.xml2
-rw-r--r--packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml3
-rw-r--r--packages/CarSystemUI/res/layout/notification_center_activity.xml3
-rw-r--r--packages/CarSystemUI/res/layout/super_status_bar.xml8
-rw-r--r--packages/CarSystemUI/res/values/config.xml35
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java469
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java36
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java259
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java14
-rw-r--r--packages/ExtServices/AndroidManifest.xml2
-rw-r--r--packages/NetworkStack/AndroidManifest.xml14
-rw-r--r--packages/NetworkStack/src/android/net/ip/IpClient.java4
-rw-r--r--packages/NetworkStack/src/android/net/util/NetworkStackUtils.java16
-rw-r--r--packages/NetworkStack/src/com/android/server/NetworkStackService.java14
-rw-r--r--packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java82
-rw-r--r--packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java55
-rw-r--r--packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java180
-rw-r--r--packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java5
-rw-r--r--packages/PackageInstaller/AndroidManifest.xml2
-rw-r--r--packages/SettingsLib/AdaptiveIcon/Android.bp13
-rw-r--r--packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml23
-rw-r--r--packages/SettingsLib/AdaptiveIcon/res/values/colors.xml21
-rw-r--r--packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml24
-rw-r--r--packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java130
-rw-r--r--packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java59
-rw-r--r--packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java87
-rw-r--r--packages/SettingsLib/Android.bp1
-rw-r--r--packages/SettingsLib/Tile/Android.bp11
-rw-r--r--packages/SettingsLib/Tile/AndroidManifest.xml23
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java (renamed from packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java)22
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java (renamed from packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java)29
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java (renamed from packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java)8
-rw-r--r--packages/SettingsLib/res/drawable/ic_media_device.xml33
-rw-r--r--packages/SettingsLib/res/values/arrays.xml22
-rw-r--r--packages/SettingsLib/res/values/colors.xml16
-rw-r--r--packages/SettingsLib/res/values/dimens.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java76
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java56
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java20
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java87
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java41
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java20
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java139
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java44
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/res/drawable/ic_camera.xml25
-rw-r--r--packages/SystemUI/res/drawable/stat_sys_camera.xml13
-rw-r--r--packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml2
-rw-r--r--packages/SystemUI/res/values/colors.xml1
-rw-r--r--packages/SystemUI/res/values/config_car.xml31
-rw-r--r--packages/SystemUI/res/values/dimens.xml6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java8
-rw-r--r--packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java28
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java27
-rw-r--r--services/core/java/com/android/server/ExplicitHealthCheckController.java192
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java298
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java55
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java7
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java5
-rw-r--r--services/core/java/com/android/server/biometrics/face/FaceService.java78
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java23
-rw-r--r--services/core/java/com/android/server/incident/IncidentCompanionService.java46
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java4
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java4
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java12
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java2
-rw-r--r--services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java3
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java36
-rw-r--r--services/core/java/com/android/server/wm/BarController.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java74
-rw-r--r--services/core/java/com/android/server/wm/StatusBarController.java19
-rw-r--r--services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java34
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp14
-rw-r--r--services/net/java/android/net/INetworkMonitor.aidl8
-rw-r--r--services/net/java/android/net/ipmemorystore/NetworkAttributes.java53
-rw-r--r--services/net/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl1
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java5
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java238
-rw-r--r--tests/net/java/android/net/ipmemorystore/ParcelableTests.java5
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java2
-rw-r--r--tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java1
-rw-r--r--tests/testables/src/android/testing/TestableContext.java4
-rw-r--r--tools/aapt2/ResourceParser_test.cpp23
-rw-r--r--tools/aapt2/cmd/Compile.cpp8
-rw-r--r--tools/aapt2/cmd/Compile_test.cpp92
144 files changed, 3417 insertions, 1457 deletions
diff --git a/api/current.txt b/api/current.txt
index db175427139c..e6b44f1bc169 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -102,7 +102,7 @@ package android {
field public static final String NFC_TRANSACTION_EVENT = "android.permission.NFC_TRANSACTION_EVENT";
field public static final String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
field @Deprecated public static final String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
- field public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
+ field @Deprecated public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR";
field public static final String READ_CALL_LOG = "android.permission.READ_CALL_LOG";
field public static final String READ_CONTACTS = "android.permission.READ_CONTACTS";
@@ -15389,8 +15389,8 @@ package android.graphics.drawable {
method public boolean setState(@NonNull int[]);
method public void setTint(@ColorInt int);
method public void setTintList(@Nullable android.content.res.ColorStateList);
- method @Deprecated public void setTintMode(@NonNull android.graphics.PorterDuff.Mode);
- method public void setTintMode(@NonNull android.graphics.BlendMode);
+ method @Deprecated public void setTintMode(@Nullable android.graphics.PorterDuff.Mode);
+ method public void setTintMode(@Nullable android.graphics.BlendMode);
method public boolean setVisible(boolean, boolean);
method public void unscheduleSelf(@NonNull Runnable);
}
@@ -17058,6 +17058,7 @@ package android.hardware.camera2 {
public class CaptureFailure {
method public long getFrameNumber();
+ method @Nullable public String getPhysicalCameraId();
method public int getReason();
method @NonNull public android.hardware.camera2.CaptureRequest getRequest();
method public int getSequenceId();
@@ -28792,11 +28793,11 @@ package android.net {
}
public final class IpPrefix implements android.os.Parcelable {
- method public boolean contains(java.net.InetAddress);
+ method public boolean contains(@NonNull java.net.InetAddress);
method public int describeContents();
- method public java.net.InetAddress getAddress();
- method public int getPrefixLength();
- method public byte[] getRawAddress();
+ method @NonNull public java.net.InetAddress getAddress();
+ method @IntRange(from=0, to=128) public int getPrefixLength();
+ method @NonNull public byte[] getRawAddress();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
}
@@ -28869,7 +28870,7 @@ package android.net {
method public int describeContents();
method public java.net.InetAddress getAddress();
method public int getFlags();
- method public int getPrefixLength();
+ method @IntRange(from=0, to=128) public int getPrefixLength();
method public int getScope();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkAddress> CREATOR;
@@ -29139,9 +29140,9 @@ package android.net {
public final class RouteInfo implements android.os.Parcelable {
method public int describeContents();
- method public android.net.IpPrefix getDestination();
- method public java.net.InetAddress getGateway();
- method public String getInterface();
+ method @NonNull public android.net.IpPrefix getDestination();
+ method @Nullable public java.net.InetAddress getGateway();
+ method @Nullable public String getInterface();
method public boolean hasGateway();
method public boolean isDefaultRoute();
method public boolean matches(java.net.InetAddress);
@@ -42534,6 +42535,7 @@ package android.system {
method public static int getpid();
method public static int getppid();
method public static java.net.SocketAddress getsockname(java.io.FileDescriptor) throws android.system.ErrnoException;
+ method @NonNull public static android.system.StructTimeval getsockoptTimeval(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
method public static int gettid();
method public static int getuid();
method public static byte[] getxattr(String, String) throws android.system.ErrnoException;
@@ -42584,6 +42586,7 @@ package android.system {
method @Deprecated public static void setgid(int) throws android.system.ErrnoException;
method public static int setsid() throws android.system.ErrnoException;
method public static void setsockoptInt(java.io.FileDescriptor, int, int, int) throws android.system.ErrnoException;
+ method public static void setsockoptTimeval(@NonNull java.io.FileDescriptor, int, int, @NonNull android.system.StructTimeval) throws android.system.ErrnoException;
method @Deprecated public static void setuid(int) throws android.system.ErrnoException;
method public static void setxattr(String, String, byte[], int) throws android.system.ErrnoException;
method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException;
@@ -42789,6 +42792,10 @@ package android.system {
field public static final int F_SETOWN;
field public static final int F_UNLCK;
field public static final int F_WRLCK;
+ field public static final int ICMP6_ECHO_REPLY;
+ field public static final int ICMP6_ECHO_REQUEST;
+ field public static final int ICMP_ECHO;
+ field public static final int ICMP_ECHOREPLY;
field public static final int IFA_F_DADFAILED;
field public static final int IFA_F_DEPRECATED;
field public static final int IFA_F_HOMEADDRESS;
@@ -43161,6 +43168,13 @@ package android.system {
field public final long tv_sec;
}
+ public final class StructTimeval {
+ method @NonNull public static android.system.StructTimeval fromMillis(long);
+ method public long toMillis();
+ field public final long tv_sec;
+ field public final long tv_usec;
+ }
+
public final class StructUtsname {
ctor public StructUtsname(String, String, String, String, String);
field public final String machine;
diff --git a/api/system-current.txt b/api/system-current.txt
index 8ee1e0ab5867..ba6e348d375d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1258,9 +1258,9 @@ package android.bluetooth {
public final class BluetoothDevice implements android.os.Parcelable {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public String getMetadata(int);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean getSilenceMode();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, String);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
@@ -1269,7 +1269,6 @@ package android.bluetooth {
field public static final int ACCESS_REJECTED = 2; // 0x2
field public static final int ACCESS_UNKNOWN = 0; // 0x0
field public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED";
- field public static final String EXTRA_SILENCE_ENABLED = "android.bluetooth.device.extra.SILENCE_ENABLED";
field public static final int METADATA_COMPANION_APP = 4; // 0x4
field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10
field public static final int METADATA_HARDWARE_VERSION = 3; // 0x3
@@ -1458,14 +1457,14 @@ package android.content.om {
public final class OverlayInfo implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public String getCategory();
+ method @NonNull public String getPackageName();
+ method @Nullable public String getTargetOverlayableName();
+ method @Nullable public String getTargetPackageName();
+ method public int getUserId();
method public boolean isEnabled();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.om.OverlayInfo> CREATOR;
- field public final String category;
- field public final String packageName;
- field public final String targetOverlayableName;
- field public final String targetPackageName;
- field public final int userId;
}
public class OverlayManager {
@@ -4061,7 +4060,7 @@ package android.net {
}
public final class IpPrefix implements android.os.Parcelable {
- ctor public IpPrefix(@NonNull java.net.InetAddress, int);
+ ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
ctor public IpPrefix(@NonNull String);
}
@@ -4082,8 +4081,8 @@ package android.net {
}
public class LinkAddress implements android.os.Parcelable {
- ctor public LinkAddress(java.net.InetAddress, int, int, int);
- ctor public LinkAddress(@NonNull java.net.InetAddress, int);
+ ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
+ ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
ctor public LinkAddress(@NonNull String);
ctor public LinkAddress(@NonNull String, int, int);
method public boolean isGlobalPreferred();
@@ -4264,8 +4263,8 @@ package android.net.apf {
public final class ApfCapabilities implements android.os.Parcelable {
ctor public ApfCapabilities(int, int, int);
method public int describeContents();
- method public static boolean getApfDrop8023Frames(@NonNull android.content.Context);
- method @NonNull public static int[] getApfEthTypeBlackList(@NonNull android.content.Context);
+ method public static boolean getApfDrop8023Frames();
+ method @NonNull public static int[] getApfEtherTypeBlackList();
method public boolean hasDataAccess();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR;
diff --git a/api/test-current.txt b/api/test-current.txt
index 7762baabc272..84e641e0145e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -680,9 +680,11 @@ package android.content.pm {
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final int FLAG_PERMISSION_HIDDEN = 1024; // 0x400
+ field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4
field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
+ field public static final int FLAG_PERMISSION_SYSTEM_FIXED = 16; // 0x10
field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
@@ -1239,7 +1241,7 @@ package android.net {
}
public final class IpPrefix implements android.os.Parcelable {
- ctor public IpPrefix(@NonNull java.net.InetAddress, int);
+ ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
ctor public IpPrefix(@NonNull String);
}
@@ -1248,8 +1250,8 @@ package android.net {
}
public class LinkAddress implements android.os.Parcelable {
- ctor public LinkAddress(java.net.InetAddress, int, int, int);
- ctor public LinkAddress(@NonNull java.net.InetAddress, int);
+ ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
+ ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
ctor public LinkAddress(@NonNull String);
ctor public LinkAddress(@NonNull String, int, int);
method public boolean isGlobalPreferred();
@@ -1358,8 +1360,8 @@ package android.net.apf {
public final class ApfCapabilities implements android.os.Parcelable {
ctor public ApfCapabilities(int, int, int);
method public int describeContents();
- method public static boolean getApfDrop8023Frames(@NonNull android.content.Context);
- method @NonNull public static int[] getApfEthTypeBlackList(@NonNull android.content.Context);
+ method public static boolean getApfDrop8023Frames();
+ method @NonNull public static int[] getApfEtherTypeBlackList();
method public boolean hasDataAccess();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 023371d3b443..80b6349ca284 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4037,7 +4037,7 @@ public class ActivityManager {
* continues running even if the process is killed and restarted. To remove the watch,
* use {@link #clearWatchHeapLimit()}.
*
- * <p>This API only work if the calling process has been marked as
+ * <p>This API only works if the calling process has been marked as
* {@link ApplicationInfo#FLAG_DEBUGGABLE} or this is running on a debuggable
* (userdebug or eng) build.</p>
*
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2441672fb048..2a603d270993 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6937,9 +6937,6 @@ public final class ActivityThread extends ClientTransactionHandler {
// If feature is disabled, we don't need to install
if (!DEPRECATE_DATA_COLUMNS) return;
- // If app is modern enough, we don't need to install
- if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) return;
-
// Install interception and make sure it sticks!
Os def = null;
do {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4b0c05f323cd..ddae34c64358 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5440,13 +5440,14 @@ public class DevicePolicyManager {
}
/**
- * Called by a device or profile owner to set whether auto time is required. If auto time is
- * required, no user will be able set the date and time and network date and time will be used.
+ * Called by a device owner, or alternatively a profile owner from Android 8.0 (API level 26) or
+ * higher, to set whether auto time is required. If auto time is required, no user will be able
+ * set the date and time and network date and time will be used.
* <p>
* Note: if auto time is required the user can still manually set the time zone.
* <p>
- * The calling device admin must be a device or profile owner. If it is not, a security
- * exception will be thrown.
+ * The calling device admin must be a device owner, or alternatively a profile owner from
+ * Android 8.0 (API level 26) or higher. If it is not, a security exception will be thrown.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param required Whether auto time is set required or not.
@@ -10789,8 +10790,8 @@ public class DevicePolicyManager {
}
/**
- * Returns whether the device is being used as a managed kiosk, as defined in the CDD. As of
- * this release, these requirements are as follows:
+ * Returns whether the device is being used as a managed kiosk. These requirements are as
+ * follows:
* <ul>
* <li>The device is in Lock Task (therefore there is also a Device Owner app on the
* device)</li>
@@ -10829,11 +10830,11 @@ public class DevicePolicyManager {
}
/**
- * Returns whether the device is being used as an unattended managed kiosk, as defined in the
- * CDD. As of this release, these requirements are as follows:
+ * Returns whether the device is being used as an unattended managed kiosk. These requirements
+ * are as follows:
* <ul>
- * <li>The device is being used as a managed kiosk, as defined in the CDD and verified at
- * {@link #isManagedKiosk()}</li>
+ * <li>The device is being used as a managed kiosk, as defined at {@link
+ * #isManagedKiosk()}</li>
* <li>The device has not received user input for at least 30 minutes</li>
* </ul>
*
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index fa2c9f8a52ae..204d7e3ceca6 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -535,7 +535,6 @@ public final class BluetoothDevice implements Parcelable {
/**
* Intent to broadcast silence mode changed.
* Alway contains the extra field {@link #EXTRA_DEVICE}
- * Alway contains the extra field {@link #EXTRA_SILENCE_ENABLED}
*
* @hide
*/
@@ -545,16 +544,6 @@ public final class BluetoothDevice implements Parcelable {
"android.bluetooth.device.action.SILENCE_MODE_CHANGED";
/**
- * Used as an extra field in {@link #ACTION_SILENCE_MODE_CHANGED} intent,
- * contains whether device is in silence mode as boolean.
- *
- * @hide
- */
- @SystemApi
- public static final String EXTRA_SILENCE_ENABLED =
- "android.bluetooth.device.extra.SILENCE_ENABLED";
-
- /**
* Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent.
*
* @hide
@@ -1615,7 +1604,8 @@ public final class BluetoothDevice implements Parcelable {
}
/**
- * Set the Bluetooth device silence mode.
+ * Sets whether the {@link BluetoothDevice} enters silence mode. Audio will not
+ * be routed to the {@link BluetoothDevice} if set to {@code true}.
*
* When the {@link BluetoothDevice} enters silence mode, and the {@link BluetoothDevice}
* is an active device (for A2DP or HFP), the active device for that profile
@@ -1635,6 +1625,7 @@ public final class BluetoothDevice implements Parcelable {
*
* @param silence true to enter silence mode, false to exit
* @return true on success, false on error.
+ * @throws IllegalStateException if Bluetooth is not turned ON.
* @hide
*/
@SystemApi
@@ -1642,12 +1633,9 @@ public final class BluetoothDevice implements Parcelable {
public boolean setSilenceMode(boolean silence) {
final IBluetooth service = sService;
if (service == null) {
- return false;
+ throw new IllegalStateException("Bluetooth is not turned ON");
}
try {
- if (getSilenceMode() == silence) {
- return true;
- }
return service.setSilenceMode(this, silence);
} catch (RemoteException e) {
Log.e(TAG, "setSilenceMode fail", e);
@@ -1656,24 +1644,25 @@ public final class BluetoothDevice implements Parcelable {
}
/**
- * Get the device silence mode status
+ * Check whether the {@link BluetoothDevice} is in silence mode
*
* <p> Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
*
* @return true on device in silence mode, otherwise false.
+ * @throws IllegalStateException if Bluetooth is not turned ON.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
- public boolean getSilenceMode() {
+ public boolean isInSilenceMode() {
final IBluetooth service = sService;
if (service == null) {
- return false;
+ throw new IllegalStateException("Bluetooth is not turned ON");
}
try {
return service.getSilenceMode(this);
} catch (RemoteException e) {
- Log.e(TAG, "getSilenceMode fail", e);
+ Log.e(TAG, "isInSilenceMode fail", e);
return false;
}
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index fa85f0ae1670..791c55196ecf 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1313,6 +1313,12 @@ public abstract class ContentResolver implements ContentInterface {
public final @Nullable ParcelFileDescriptor openFileDescriptor(@NonNull Uri uri,
@NonNull String mode, @Nullable CancellationSignal cancellationSignal)
throws FileNotFoundException {
+ try {
+ if (mWrapped != null) return mWrapped.openFile(uri, mode, cancellationSignal);
+ } catch (RemoteException e) {
+ return null;
+ }
+
AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode, cancellationSignal);
if (afd == null) {
return null;
@@ -1455,6 +1461,12 @@ public abstract class ContentResolver implements ContentInterface {
Preconditions.checkNotNull(uri, "uri");
Preconditions.checkNotNull(mode, "mode");
+ try {
+ if (mWrapped != null) return mWrapped.openAssetFile(uri, mode, cancellationSignal);
+ } catch (RemoteException e) {
+ return null;
+ }
+
String scheme = uri.getScheme();
if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
if (!"r".equals(mode)) {
@@ -1634,6 +1646,12 @@ public abstract class ContentResolver implements ContentInterface {
Preconditions.checkNotNull(uri, "uri");
Preconditions.checkNotNull(mimeType, "mimeType");
+ try {
+ if (mWrapped != null) return mWrapped.openTypedAssetFile(uri, mimeType, opts, cancellationSignal);
+ } catch (RemoteException e) {
+ return null;
+ }
+
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
@@ -3525,12 +3543,7 @@ public abstract class ContentResolver implements ContentInterface {
*/
public @NonNull Bitmap loadThumbnail(@NonNull Uri uri, @NonNull Size size,
@Nullable CancellationSignal signal) throws IOException {
- Objects.requireNonNull(uri);
- Objects.requireNonNull(size);
-
- try (ContentProviderClient client = acquireContentProviderClient(uri)) {
- return loadThumbnail(client, uri, size, signal, ImageDecoder.ALLOCATOR_SOFTWARE);
- }
+ return loadThumbnail(this, uri, size, signal, ImageDecoder.ALLOCATOR_SOFTWARE);
}
/** {@hide} */
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index b8c6d87acd5c..597c08392d04 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
import android.os.Parcel;
import android.os.Parcelable;
@@ -135,7 +136,6 @@ public final class OverlayInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
public final String packageName;
/**
@@ -143,7 +143,6 @@ public final class OverlayInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
public final String targetPackageName;
/**
@@ -151,7 +150,6 @@ public final class OverlayInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
public final String targetOverlayableName;
/**
@@ -159,7 +157,6 @@ public final class OverlayInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
public final String category;
/**
@@ -178,7 +175,6 @@ public final class OverlayInfo implements Parcelable {
* User handle for which this overlay applies
* @hide
*/
- @SystemApi
public final int userId;
/**
@@ -243,6 +239,56 @@ public final class OverlayInfo implements Parcelable {
ensureValidState();
}
+ /**
+ * Returns package name of the current overlay.
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public String getPackageName() {
+ return packageName;
+ }
+
+ /**
+ * Returns the target package name of the current overlay.
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public String getTargetPackageName() {
+ return targetPackageName;
+ }
+
+ /**
+ * Returns the category of the current overlay.
+ * @hide\
+ */
+ @SystemApi
+ @Nullable
+ public String getCategory() {
+ return category;
+ }
+
+ /**
+ * Returns user handle for which this overlay applies to.
+ * @hide
+ */
+ @SystemApi
+ @UserIdInt
+ public int getUserId() {
+ return userId;
+ }
+
+ /**
+ * Returns name of the target overlayable declaration.
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public String getTargetOverlayableName() {
+ return targetOverlayableName;
+ }
+
private void ensureValidState() {
if (packageName == null) {
throw new IllegalArgumentException("packageName must not be null");
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8f7df2517580..de10bb00bfdf 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2998,6 +2998,7 @@ public abstract class PackageManager {
* @hide
*/
@SystemApi
+ @TestApi
public static final int FLAG_PERMISSION_POLICY_FIXED = 1 << 2;
/**
@@ -3021,6 +3022,7 @@ public abstract class PackageManager {
* @hide
*/
@SystemApi
+ @TestApi
public static final int FLAG_PERMISSION_SYSTEM_FIXED = 1 << 4;
/**
diff --git a/core/java/android/hardware/camera2/CaptureFailure.java b/core/java/android/hardware/camera2/CaptureFailure.java
index cd2bc5f943e7..20ca4a338f01 100644
--- a/core/java/android/hardware/camera2/CaptureFailure.java
+++ b/core/java/android/hardware/camera2/CaptureFailure.java
@@ -17,6 +17,7 @@ package android.hardware.camera2;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -61,17 +62,19 @@ public class CaptureFailure {
private final boolean mDropped;
private final int mSequenceId;
private final long mFrameNumber;
+ private final String mErrorPhysicalCameraId;
/**
* @hide
*/
public CaptureFailure(CaptureRequest request, int reason,
- boolean dropped, int sequenceId, long frameNumber) {
+ boolean dropped, int sequenceId, long frameNumber, String errorPhysicalCameraId) {
mRequest = request;
mReason = reason;
mDropped = dropped;
mSequenceId = sequenceId;
mFrameNumber = frameNumber;
+ mErrorPhysicalCameraId = errorPhysicalCameraId;
}
/**
@@ -155,4 +158,17 @@ public class CaptureFailure {
public int getSequenceId() {
return mSequenceId;
}
+
+ /**
+ * The physical camera device ID in case the capture failure comes from a {@link CaptureRequest}
+ * with configured physical camera streams for a logical camera.
+ *
+ * @return String The physical camera device ID of the respective failing output.
+ * {@code null} in case the capture request has no associated physical camera device.
+ * @see CaptureRequest.Builder#setPhysicalCameraKey
+ * @see android.hardware.camera2.params.OutputConfiguration#setPhysicalCameraId
+ */
+ public @Nullable String getPhysicalCameraId() {
+ return mErrorPhysicalCameraId;
+ }
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 57b608f0fd84..fc12b090b2f3 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -2232,6 +2232,7 @@ public class CameraDeviceImpl extends CameraDevice
final int requestId = resultExtras.getRequestId();
final int subsequenceId = resultExtras.getSubsequenceId();
final long frameNumber = resultExtras.getFrameNumber();
+ final String errorPhysicalCameraId = resultExtras.getErrorPhysicalCameraId();
final CaptureCallbackHolder holder =
CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
@@ -2287,7 +2288,8 @@ public class CameraDeviceImpl extends CameraDevice
reason,
/*dropped*/ mayHaveBuffers,
requestId,
- frameNumber);
+ frameNumber,
+ errorPhysicalCameraId);
failureDispatch = new Runnable() {
@Override
diff --git a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
index edc3d91eaf12..1ff5bd562f2e 100644
--- a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
+++ b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
@@ -29,6 +29,7 @@ public class CaptureResultExtras implements Parcelable {
private long frameNumber;
private int partialResultCount;
private int errorStreamId;
+ private String errorPhysicalCameraId;
public static final @android.annotation.NonNull Parcelable.Creator<CaptureResultExtras> CREATOR =
new Parcelable.Creator<CaptureResultExtras>() {
@@ -49,7 +50,8 @@ public class CaptureResultExtras implements Parcelable {
public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId,
int precaptureTriggerId, long frameNumber,
- int partialResultCount, int errorStreamId) {
+ int partialResultCount, int errorStreamId,
+ String errorPhysicalCameraId) {
this.requestId = requestId;
this.subsequenceId = subsequenceId;
this.afTriggerId = afTriggerId;
@@ -57,6 +59,7 @@ public class CaptureResultExtras implements Parcelable {
this.frameNumber = frameNumber;
this.partialResultCount = partialResultCount;
this.errorStreamId = errorStreamId;
+ this.errorPhysicalCameraId = errorPhysicalCameraId;
}
@Override
@@ -73,6 +76,12 @@ public class CaptureResultExtras implements Parcelable {
dest.writeLong(frameNumber);
dest.writeInt(partialResultCount);
dest.writeInt(errorStreamId);
+ if ((errorPhysicalCameraId != null) && !errorPhysicalCameraId.isEmpty()) {
+ dest.writeBoolean(true);
+ dest.writeString(errorPhysicalCameraId);
+ } else {
+ dest.writeBoolean(false);
+ }
}
public void readFromParcel(Parcel in) {
@@ -83,6 +92,14 @@ public class CaptureResultExtras implements Parcelable {
frameNumber = in.readLong();
partialResultCount = in.readInt();
errorStreamId = in.readInt();
+ boolean errorPhysicalCameraIdPresent = in.readBoolean();
+ if (errorPhysicalCameraIdPresent) {
+ errorPhysicalCameraId = in.readString();
+ }
+ }
+
+ public String getErrorPhysicalCameraId() {
+ return errorPhysicalCameraId;
}
public int getRequestId() {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index aff09f2a45f2..908ed0913ffc 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -109,11 +109,11 @@ public class LegacyCameraDevice implements AutoCloseable {
}
if (holder == null) {
return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
- ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
+ ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, null);
}
return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
/*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
- /*partialResultCount*/1, errorStreamId);
+ /*partialResultCount*/1, errorStreamId, null);
}
/**
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index 402bffdc2a97..8cfe6df678c7 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -71,7 +72,7 @@ public final class IpPrefix implements Parcelable {
*
* @hide
*/
- public IpPrefix(@NonNull byte[] address, int prefixLength) {
+ public IpPrefix(@NonNull byte[] address, @IntRange(from = 0, to = 128) int prefixLength) {
this.address = address.clone();
this.prefixLength = prefixLength;
checkAndMaskAddressAndPrefixLength();
@@ -88,7 +89,7 @@ public final class IpPrefix implements Parcelable {
*/
@SystemApi
@TestApi
- public IpPrefix(@NonNull InetAddress address, int prefixLength) {
+ public IpPrefix(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength) {
// We don't reuse the (byte[], int) constructor because it calls clone() on the byte array,
// which is unnecessary because getAddress() already returns a clone.
this.address = address.getAddress();
@@ -150,13 +151,13 @@ public final class IpPrefix implements Parcelable {
*
* @return the address in the form of a byte array.
*/
- public InetAddress getAddress() {
+ public @NonNull InetAddress getAddress() {
try {
return InetAddress.getByAddress(address);
} catch (UnknownHostException e) {
// Cannot happen. InetAddress.getByAddress can only throw an exception if the byte
// array is the wrong length, but we check that in the constructor.
- return null;
+ throw new IllegalArgumentException("Address is invalid");
}
}
@@ -166,7 +167,7 @@ public final class IpPrefix implements Parcelable {
*
* @return the address in the form of a byte array.
*/
- public byte[] getRawAddress() {
+ public @NonNull byte[] getRawAddress() {
return address.clone();
}
@@ -175,6 +176,7 @@ public final class IpPrefix implements Parcelable {
*
* @return the prefix length.
*/
+ @IntRange(from = 0, to = 128)
public int getPrefixLength() {
return prefixLength;
}
@@ -183,10 +185,10 @@ public final class IpPrefix implements Parcelable {
* Determines whether the prefix contains the specified address.
*
* @param address An {@link InetAddress} to test.
- * @return {@code true} if the prefix covers the given address.
+ * @return {@code true} if the prefix covers the given address. {@code false} otherwise.
*/
- public boolean contains(InetAddress address) {
- byte[] addrBytes = (address == null) ? null : address.getAddress();
+ public boolean contains(@NonNull InetAddress address) {
+ byte[] addrBytes = address.getAddress();
if (addrBytes == null || addrBytes.length != this.address.length) {
return false;
}
@@ -201,7 +203,7 @@ public final class IpPrefix implements Parcelable {
* @param otherPrefix the prefix to test
* @hide
*/
- public boolean containsPrefix(IpPrefix otherPrefix) {
+ public boolean containsPrefix(@NonNull IpPrefix otherPrefix) {
if (otherPrefix.getPrefixLength() < prefixLength) return false;
final byte[] otherAddress = otherPrefix.getRawAddress();
NetworkUtils.maskRawAddress(otherAddress, prefixLength);
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 333603f3a0f2..93dd2e4d7717 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -25,6 +25,7 @@ import static android.system.OsConstants.RT_SCOPE_LINK;
import static android.system.OsConstants.RT_SCOPE_SITE;
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -170,7 +171,7 @@ public class LinkAddress implements Parcelable {
* Constructs a new {@code LinkAddress} from an {@code InetAddress} and prefix length, with
* the specified flags and scope. Flags and scope are not checked for validity.
* @param address The IP address.
- * @param prefixLength The prefix length.
+ * @param prefixLength The prefix length. Must be &gt;= 0 and &lt;= (32 or 128) (IPv4 or IPv6).
* @param flags A bitmask of {@code IFA_F_*} values representing properties of the address.
* @param scope An integer defining the scope in which the address is unique (e.g.,
* {@link OsConstants#RT_SCOPE_LINK} or {@link OsConstants#RT_SCOPE_SITE}).
@@ -178,7 +179,8 @@ public class LinkAddress implements Parcelable {
*/
@SystemApi
@TestApi
- public LinkAddress(InetAddress address, int prefixLength, int flags, int scope) {
+ public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength,
+ int flags, int scope) {
init(address, prefixLength, flags, scope);
}
@@ -186,12 +188,13 @@ public class LinkAddress implements Parcelable {
* Constructs a new {@code LinkAddress} from an {@code InetAddress} and a prefix length.
* The flags are set to zero and the scope is determined from the address.
* @param address The IP address.
- * @param prefixLength The prefix length.
+ * @param prefixLength The prefix length. Must be &gt;= 0 and &lt;= (32 or 128) (IPv4 or IPv6).
* @hide
*/
@SystemApi
@TestApi
- public LinkAddress(@NonNull InetAddress address, int prefixLength) {
+ public LinkAddress(@NonNull InetAddress address,
+ @IntRange(from = 0, to = 128) int prefixLength) {
this(address, prefixLength, 0, 0);
this.scope = scopeForUnicastAddress(address);
}
@@ -202,7 +205,7 @@ public class LinkAddress implements Parcelable {
* @param interfaceAddress The interface address.
* @hide
*/
- public LinkAddress(InterfaceAddress interfaceAddress) {
+ public LinkAddress(@NonNull InterfaceAddress interfaceAddress) {
this(interfaceAddress.getAddress(),
interfaceAddress.getNetworkPrefixLength());
}
@@ -306,6 +309,7 @@ public class LinkAddress implements Parcelable {
/**
* Returns the prefix length of this {@code LinkAddress}.
*/
+ @IntRange(from = 0, to = 128)
public int getPrefixLength() {
return prefixLength;
}
@@ -316,6 +320,7 @@ public class LinkAddress implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
+ @IntRange(from = 0, to = 128)
public int getNetworkPrefixLength() {
return getPrefixLength();
}
diff --git a/core/java/android/net/NetworkCapabilities.aidl b/core/java/android/net/NetworkCapabilities.aidl
index cd7d71cad978..01d328605de4 100644
--- a/core/java/android/net/NetworkCapabilities.aidl
+++ b/core/java/android/net/NetworkCapabilities.aidl
@@ -17,5 +17,5 @@
package android.net;
-parcelable NetworkCapabilities;
+@JavaOnlyStableParcelable parcelable NetworkCapabilities;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index b0239c839348..52d3fc48a3a5 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -16,6 +16,8 @@
package android.net;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -24,6 +26,8 @@ import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -51,20 +55,32 @@ import java.util.Objects;
* (IPv4 or IPv6).
*/
public final class RouteInfo implements Parcelable {
+ /** @hide */
+ @IntDef(value = {
+ RTN_UNICAST,
+ RTN_UNREACHABLE,
+ RTN_THROW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RouteType {}
+
/**
* The IP destination address for this route.
*/
+ @NonNull
private final IpPrefix mDestination;
/**
* The gateway address for this route.
*/
@UnsupportedAppUsage
+ @Nullable
private final InetAddress mGateway;
/**
* The interface for this route.
*/
+ @Nullable
private final String mInterface;
@@ -108,13 +124,14 @@ public final class RouteInfo implements Parcelable {
* @param destination the destination prefix
* @param gateway the IP address to route packets through
* @param iface the interface name to send packets on
+ * @param type the type of this route
*
* @hide
*/
@SystemApi
@TestApi
public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway,
- @Nullable String iface, int type) {
+ @Nullable String iface, @RouteType int type) {
switch (type) {
case RTN_UNICAST:
case RTN_UNREACHABLE:
@@ -173,10 +190,24 @@ public final class RouteInfo implements Parcelable {
}
/**
- * @hide
+ * Constructs a {@code RouteInfo} object.
+ *
+ * If destination is null, then gateway must be specified and the
+ * constructed route is either the IPv4 default route <code>0.0.0.0</code>
+ * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
+ * route <code>::/0</code> if gateway is an instance of {@link Inet6Address}.
+ * <p>
+ * Destination and gateway may not both be null.
+ *
+ * @param destination the destination address and prefix in an {@link IpPrefix}
+ * @param gateway the {@link InetAddress} to route packets through
+ * @param iface the interface name to send packets on
+ *
+ * @hide
*/
@UnsupportedAppUsage
- public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
+ public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway,
+ @Nullable String iface) {
this(destination, gateway, iface, RTN_UNICAST);
}
@@ -184,7 +215,8 @@ public final class RouteInfo implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
- public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
+ public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway,
+ @Nullable String iface) {
this(destination == null ? null :
new IpPrefix(destination.getAddress(), destination.getPrefixLength()),
gateway, iface);
@@ -205,7 +237,7 @@ public final class RouteInfo implements Parcelable {
*
* @hide
*/
- public RouteInfo(IpPrefix destination, InetAddress gateway) {
+ public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway) {
this(destination, gateway, null);
}
@@ -215,7 +247,7 @@ public final class RouteInfo implements Parcelable {
* TODO: Remove this.
*/
@UnsupportedAppUsage
- public RouteInfo(LinkAddress destination, InetAddress gateway) {
+ public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway) {
this(destination, gateway, null);
}
@@ -227,7 +259,7 @@ public final class RouteInfo implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
- public RouteInfo(InetAddress gateway) {
+ public RouteInfo(@NonNull InetAddress gateway) {
this((IpPrefix) null, gateway, null);
}
@@ -239,35 +271,36 @@ public final class RouteInfo implements Parcelable {
*
* @hide
*/
- public RouteInfo(IpPrefix destination) {
+ public RouteInfo(@NonNull IpPrefix destination) {
this(destination, null, null);
}
/**
* @hide
*/
- public RouteInfo(LinkAddress destination) {
+ public RouteInfo(@NonNull LinkAddress destination) {
this(destination, null, null);
}
/**
* @hide
*/
- public RouteInfo(IpPrefix destination, int type) {
+ public RouteInfo(@NonNull IpPrefix destination, @RouteType int type) {
this(destination, null, null, type);
}
/**
* @hide
*/
- public static RouteInfo makeHostRoute(InetAddress host, String iface) {
+ public static RouteInfo makeHostRoute(@NonNull InetAddress host, @Nullable String iface) {
return makeHostRoute(host, null, iface);
}
/**
* @hide
*/
- public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway, String iface) {
+ public static RouteInfo makeHostRoute(@Nullable InetAddress host, @Nullable InetAddress gateway,
+ @Nullable String iface) {
if (host == null) return null;
if (host instanceof Inet4Address) {
@@ -290,6 +323,7 @@ public final class RouteInfo implements Parcelable {
*
* @return {@link IpPrefix} specifying the destination. This is never {@code null}.
*/
+ @NonNull
public IpPrefix getDestination() {
return mDestination;
}
@@ -298,6 +332,7 @@ public final class RouteInfo implements Parcelable {
* TODO: Convert callers to use IpPrefix and then remove.
* @hide
*/
+ @NonNull
public LinkAddress getDestinationLinkAddress() {
return new LinkAddress(mDestination.getAddress(), mDestination.getPrefixLength());
}
@@ -308,6 +343,7 @@ public final class RouteInfo implements Parcelable {
* @return {@link InetAddress} specifying the gateway or next hop. This may be
* {@code null} for a directly-connected route."
*/
+ @Nullable
public InetAddress getGateway() {
return mGateway;
}
@@ -317,6 +353,7 @@ public final class RouteInfo implements Parcelable {
*
* @return The name of the interface used for this route.
*/
+ @Nullable
public String getInterface() {
return mInterface;
}
@@ -330,6 +367,7 @@ public final class RouteInfo implements Parcelable {
*/
@TestApi
@SystemApi
+ @RouteType
public int getType() {
return mType;
}
@@ -401,6 +439,7 @@ public final class RouteInfo implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
+ @Nullable
public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
if ((routes == null) || (dest == null)) return null;
diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java
index 17a03c7c8933..4dd2ace59c62 100644
--- a/core/java/android/net/apf/ApfCapabilities.java
+++ b/core/java/android/net/apf/ApfCapabilities.java
@@ -19,14 +19,17 @@ package android.net.apf;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.content.Context;
+import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.R;
/**
- * APF program support capabilities.
+ * APF program support capabilities. APF stands for Android Packet Filtering and it is a flexible
+ * way to drop unwanted network packets to save power.
+ *
+ * See documentation at hardware/google/apf/apf.h
*
* This class is immutable.
* @hide
@@ -104,10 +107,11 @@ public final class ApfCapabilities implements Parcelable {
}
/**
- * Returns true if the APF interpreter advertises support for the data buffer access opcodes
- * LDDW and STDW.
+ * Determines whether the APF interpreter advertises support for the data buffer access opcodes
+ * LDDW (LoaD Data Word) and STDW (STore Data Word). Full LDDW (LoaD Data Word) and
+ * STDW (STore Data Word) support is present from APFv4 on.
*
- * Full LDDW and STDW support is present from APFv4 on.
+ * @return {@code true} if the IWifiStaIface#readApfPacketFilterData is supported.
*/
public boolean hasDataAccess() {
return apfVersionSupported >= 4;
@@ -116,14 +120,14 @@ public final class ApfCapabilities implements Parcelable {
/**
* @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames.
*/
- public static boolean getApfDrop8023Frames(@NonNull Context context) {
- return context.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
+ public static boolean getApfDrop8023Frames() {
+ return Resources.getSystem().getBoolean(R.bool.config_apfDrop802_3Frames);
}
/**
* @return An array of blacklisted EtherType, packets with EtherTypes within it will be dropped.
*/
- public static @NonNull int[] getApfEthTypeBlackList(@NonNull Context context) {
- return context.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
+ public static @NonNull int[] getApfEtherTypeBlackList() {
+ return Resources.getSystem().getIntArray(R.array.config_apfEthTypeBlackList);
}
}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 7feeeee0f790..bda6ed19d3c1 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -31,6 +31,7 @@ import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.AppGlobals;
import android.content.ClipData;
+import android.content.ContentInterface;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -884,9 +885,7 @@ public final class MediaStore {
* access this path. Instead of trying to open this path
* directly, apps should use
* {@link ContentResolver#openFileDescriptor(Uri, String)}
- * to gain access. This value will always be {@code NULL}
- * for apps targeting
- * {@link android.os.Build.VERSION_CODES#Q} or higher.
+ * to gain access.
*/
@Deprecated
@Column(Cursor.FIELD_TYPE_STRING)
@@ -1714,11 +1713,9 @@ public final class MediaStore {
url = cr.insert(EXTERNAL_CONTENT_URI, values);
if (source != null) {
- OutputStream imageOut = cr.openOutputStream(url);
- try {
- source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
- } finally {
- imageOut.close();
+ try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
+ cr.openFile(url, "w", null))) {
+ source.compress(Bitmap.CompressFormat.JPEG, 50, out);
}
long id = ContentUris.parseId(url);
@@ -1959,9 +1956,7 @@ public final class MediaStore {
* access this path. Instead of trying to open this path
* directly, apps should use
* {@link ContentResolver#loadThumbnail}
- * to gain access. This value will always be
- * {@code NULL} for apps targeting
- * {@link android.os.Build.VERSION_CODES#Q} or higher.
+ * to gain access.
*/
@Deprecated
@Column(Cursor.FIELD_TYPE_STRING)
@@ -2442,9 +2437,7 @@ public final class MediaStore {
* access this path. Instead of trying to open this path
* directly, apps should use
* {@link ContentResolver#openFileDescriptor(Uri, String)}
- * to gain access. This value will always be
- * {@code NULL} for apps targeting
- * {@link android.os.Build.VERSION_CODES#Q} or higher.
+ * to gain access.
*/
@Deprecated
@Column(Cursor.FIELD_TYPE_STRING)
@@ -2734,9 +2727,7 @@ public final class MediaStore {
* access this path. Instead of trying to open this path
* directly, apps should use
* {@link ContentResolver#loadThumbnail}
- * to gain access. This value will always be
- * {@code NULL} for apps targeting
- * {@link android.os.Build.VERSION_CODES#Q} or higher.
+ * to gain access.
*/
@Deprecated
@Column(Cursor.FIELD_TYPE_STRING)
@@ -2823,9 +2814,7 @@ public final class MediaStore {
* access this path. Instead of trying to open this path
* directly, apps should use
* {@link ContentResolver#loadThumbnail}
- * to gain access. This value will always be
- * {@code NULL} for apps targeting
- * {@link android.os.Build.VERSION_CODES#Q} or higher.
+ * to gain access.
*/
@Deprecated
@Column(Cursor.FIELD_TYPE_STRING)
@@ -3193,9 +3182,7 @@ public final class MediaStore {
* access this path. Instead of trying to open this path
* directly, apps should use
* {@link ContentResolver#openFileDescriptor(Uri, String)}
- * to gain access. This value will always be
- * {@code NULL} for apps targeting
- * {@link android.os.Build.VERSION_CODES#Q} or higher.
+ * to gain access.
*/
@Deprecated
@Column(Cursor.FIELD_TYPE_STRING)
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 077d12de4d59..d7baa1086b2f 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -92,10 +92,15 @@ public final class Formatter {
* @return formatted string with the number
*/
public static String formatFileSize(@Nullable Context context, long sizeBytes) {
+ return formatFileSize(context, sizeBytes, FLAG_SI_UNITS);
+ }
+
+ /** @hide */
+ public static String formatFileSize(@Nullable Context context, long sizeBytes, int flags) {
if (context == null) {
return "";
}
- final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SI_UNITS);
+ final BytesResult res = formatBytes(context.getResources(), sizeBytes, flags);
return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix,
res.value, res.units));
}
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index c794a69d3680..8fbbcf4b88c6 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -16,11 +16,20 @@
package android.view;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
+
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
+import android.os.SystemClock;
+import android.util.StatsLog;
/**
* Detects various gestures and events using the supplied {@link MotionEvent}s.
@@ -251,8 +260,12 @@ public class GestureDetector {
private boolean mAlwaysInTapRegion;
private boolean mAlwaysInBiggerTapRegion;
private boolean mIgnoreNextUpEvent;
+ // Whether a classification has been recorded by statsd for the current event stream. Reset on
+ // ACTION_DOWN.
+ private boolean mHasRecordedClassification;
private MotionEvent mCurrentDownEvent;
+ private MotionEvent mCurrentMotionEvent;
private MotionEvent mPreviousUpEvent;
/**
@@ -297,6 +310,7 @@ public class GestureDetector {
break;
case LONG_PRESS:
+ recordGestureClassification(msg.arg1);
dispatchLongPress();
break;
@@ -304,6 +318,8 @@ public class GestureDetector {
// If the user's finger is still down, do not count it as a tap
if (mDoubleTapListener != null) {
if (!mStillDown) {
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
} else {
mDeferConfirmSingleTap = true;
@@ -501,6 +517,11 @@ public class GestureDetector {
final int action = ev.getAction();
+ if (mCurrentMotionEvent != null) {
+ mCurrentMotionEvent.recycle();
+ }
+ mCurrentMotionEvent = MotionEvent.obtain(ev);
+
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
@@ -569,6 +590,8 @@ public class GestureDetector {
&& isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
// This is a second tap
mIsDoubleTapping = true;
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP);
// Give a callback with the first tap of the double-tap
handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
// Give a callback with down event of the double-tap
@@ -590,11 +613,17 @@ public class GestureDetector {
mStillDown = true;
mInLongPress = false;
mDeferConfirmSingleTap = false;
+ mHasRecordedClassification = false;
if (mIsLongpressEnabled) {
mHandler.removeMessages(LONG_PRESS);
- mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
- + ViewConfiguration.getLongPressTimeout());
+ mHandler.sendMessageAtTime(
+ mHandler.obtainMessage(
+ LONG_PRESS,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS,
+ 0 /* arg2 */),
+ mCurrentDownEvent.getDownTime()
+ + ViewConfiguration.getLongPressTimeout());
}
mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
@@ -613,6 +642,8 @@ public class GestureDetector {
final float scrollY = mLastFocusY - focusY;
if (mIsDoubleTapping) {
// Give the move events of the double-tap
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP);
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mAlwaysInTapRegion) {
final int deltaX = (int) (focusX - mDownFocusX);
@@ -635,8 +666,12 @@ public class GestureDetector {
// reschedule long press with a modified timeout.
mHandler.removeMessages(LONG_PRESS);
final long longPressTimeout = ViewConfiguration.getLongPressTimeout();
- mHandler.sendEmptyMessageAtTime(LONG_PRESS, ev.getDownTime()
- + (long) (longPressTimeout * multiplier));
+ mHandler.sendMessageAtTime(
+ mHandler.obtainMessage(
+ LONG_PRESS,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS,
+ 0 /* arg2 */),
+ ev.getDownTime() + (long) (longPressTimeout * multiplier));
}
// Inhibit default scroll. If a gesture is ambiguous, we prevent scroll
// until the gesture is resolved.
@@ -646,6 +681,8 @@ public class GestureDetector {
}
if (distance > slopSquare) {
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL);
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
mLastFocusY = focusY;
@@ -659,6 +696,7 @@ public class GestureDetector {
mAlwaysInBiggerTapRegion = false;
}
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
+ recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL);
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
mLastFocusY = focusY;
@@ -667,7 +705,11 @@ public class GestureDetector {
motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS;
if (deepPress && hasPendingLongPress) {
mHandler.removeMessages(LONG_PRESS);
- mHandler.sendEmptyMessage(LONG_PRESS);
+ mHandler.sendMessage(
+ mHandler.obtainMessage(
+ LONG_PRESS,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS,
+ 0 /* arg2 */));
}
break;
@@ -676,11 +718,15 @@ public class GestureDetector {
MotionEvent currentUpEvent = MotionEvent.obtain(ev);
if (mIsDoubleTapping) {
// Finally, give the up event of the double-tap
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP);
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mInLongPress) {
mHandler.removeMessages(TAP);
mInLongPress = false;
} else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) {
+ recordGestureClassification(
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
handled = mListener.onSingleTapUp(ev);
if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
mDoubleTapListener.onSingleTapConfirmed(ev);
@@ -821,4 +867,26 @@ public class GestureDetector {
mInLongPress = true;
mListener.onLongPress(mCurrentDownEvent);
}
+
+ private void recordGestureClassification(int classification) {
+ if (mHasRecordedClassification
+ || classification
+ == TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION) {
+ // Only record the first classification for an event stream.
+ return;
+ }
+ if (mCurrentDownEvent == null || mCurrentMotionEvent == null) {
+ // If the complete event stream wasn't seen, don't record anything.
+ mHasRecordedClassification = true;
+ return;
+ }
+ StatsLog.write(
+ StatsLog.TOUCH_GESTURE_CLASSIFIED,
+ getClass().getName(),
+ classification,
+ (int) (SystemClock.uptimeMillis() - mCurrentMotionEvent.getDownTime()),
+ (float) Math.hypot(mCurrentMotionEvent.getRawX() - mCurrentDownEvent.getRawX(),
+ mCurrentMotionEvent.getRawY() - mCurrentDownEvent.getRawY()));
+ mHasRecordedClassification = true;
+ }
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 13b0cc038fce..b76f2a175346 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -17,7 +17,10 @@
package android.view;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SIZE;
+import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.indexOf;
import android.annotation.IntDef;
@@ -55,6 +58,12 @@ public class InsetsState implements Parcelable {
TYPE_SIDE_BAR_1,
TYPE_SIDE_BAR_2,
TYPE_SIDE_BAR_3,
+ TYPE_TOP_GESTURES,
+ TYPE_BOTTOM_GESTURES,
+ TYPE_LEFT_GESTURES,
+ TYPE_RIGHT_GESTURES,
+ TYPE_TOP_TAPPABLE_ELEMENT,
+ TYPE_BOTTOM_TAPPABLE_ELEMENT,
TYPE_IME
})
public @interface InternalInsetType {}
@@ -73,8 +82,16 @@ public class InsetsState implements Parcelable {
public static final int TYPE_SIDE_BAR_2 = 2;
public static final int TYPE_SIDE_BAR_3 = 3;
+ public static final int TYPE_TOP_GESTURES = 4;
+ public static final int TYPE_BOTTOM_GESTURES = 5;
+ public static final int TYPE_LEFT_GESTURES = 6;
+ public static final int TYPE_RIGHT_GESTURES = 7;
+ public static final int TYPE_TOP_TAPPABLE_ELEMENT = 8;
+ public static final int TYPE_BOTTOM_TAPPABLE_ELEMENT = 9;
+
/** Input method window. */
- public static final int TYPE_IME = 4;
+ public static final int TYPE_IME = 10;
+
static final int LAST_TYPE = TYPE_IME;
// Derived types
@@ -137,17 +154,6 @@ public class InsetsState implements Parcelable {
&& legacyContentInsets != null && legacyStableInsets != null) {
WindowInsets.assignCompatInsets(typeInsetsMap, legacyContentInsets);
WindowInsets.assignCompatInsets(typeMaxInsetsMap, legacyStableInsets);
-
- // TODO: set system gesture insets based on actual system gesture area.
- typeInsetsMap[Type.indexOf(Type.systemGestures())] = Insets.of(legacyContentInsets);
- typeInsetsMap[Type.indexOf(Type.mandatorySystemGestures())] =
- Insets.of(legacyContentInsets);
- typeInsetsMap[Type.indexOf(Type.tappableElement())] = Insets.of(legacyContentInsets);
-
- typeMaxInsetsMap[Type.indexOf(Type.systemGestures())] = Insets.of(legacyStableInsets);
- typeMaxInsetsMap[Type.indexOf(Type.mandatorySystemGestures())] =
- Insets.of(legacyStableInsets);
- typeMaxInsetsMap[Type.indexOf(Type.tappableElement())] = Insets.of(legacyStableInsets);
}
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
InsetsSource source = mSources.get(type);
@@ -159,7 +165,9 @@ public class InsetsState implements Parcelable {
&& (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR);
boolean skipIme = source.getType() == TYPE_IME
&& (legacySoftInputMode & LayoutParams.SOFT_INPUT_ADJUST_RESIZE) == 0;
- if (skipSystemBars || skipIme) {
+ boolean skipLegacyTypes = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
+ && (toPublicType(type) & Type.compatSystemInsets()) != 0;
+ if (skipSystemBars || skipIme || skipLegacyTypes) {
typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible();
continue;
}
@@ -183,7 +191,25 @@ public class InsetsState implements Parcelable {
@Nullable boolean[] typeVisibilityMap) {
Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility);
- int index = indexOf(toPublicType(source.getType()));
+ int type = toPublicType(source.getType());
+ processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
+ insets, type);
+
+ if (type == MANDATORY_SYSTEM_GESTURES) {
+ // Mandatory system gestures are also system gestures.
+ // TODO: find a way to express this more generally. One option would be to define
+ // Type.systemGestureInsets() as NORMAL | MANDATORY, but then we lose the
+ // ability to set systemGestureInsets() independently from
+ // mandatorySystemGestureInsets() in the Builder.
+ processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
+ insets, SYSTEM_GESTURES);
+ }
+ }
+
+ private void processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap,
+ @InsetSide @Nullable SparseIntArray typeSideMap,
+ @Nullable boolean[] typeVisibilityMap, Insets insets, int type) {
+ int index = indexOf(type);
Insets existing = typeInsetsMap[index];
if (existing == null) {
typeInsetsMap[index] = insets;
@@ -300,6 +326,15 @@ public class InsetsState implements Parcelable {
return Type.SIDE_BARS;
case TYPE_IME:
return Type.IME;
+ case TYPE_TOP_GESTURES:
+ case TYPE_BOTTOM_GESTURES:
+ return Type.MANDATORY_SYSTEM_GESTURES;
+ case TYPE_LEFT_GESTURES:
+ case TYPE_RIGHT_GESTURES:
+ return Type.SYSTEM_GESTURES;
+ case TYPE_TOP_TAPPABLE_ELEMENT:
+ case TYPE_BOTTOM_TAPPABLE_ELEMENT:
+ return Type.TAPPABLE_ELEMENT;
default:
throw new IllegalArgumentException("Unknown type: " + type);
}
@@ -336,10 +371,20 @@ public class InsetsState implements Parcelable {
return "TYPE_SIDE_BAR_2";
case TYPE_SIDE_BAR_3:
return "TYPE_SIDE_BAR_3";
- case TYPE_IME:
- return "TYPE_IME";
+ case TYPE_TOP_GESTURES:
+ return "TYPE_TOP_GESTURES";
+ case TYPE_BOTTOM_GESTURES:
+ return "TYPE_BOTTOM_GESTURES";
+ case TYPE_LEFT_GESTURES:
+ return "TYPE_LEFT_GESTURES";
+ case TYPE_RIGHT_GESTURES:
+ return "TYPE_RIGHT_GESTURES";
+ case TYPE_TOP_TAPPABLE_ELEMENT:
+ return "TYPE_TOP_TAPPABLE_ELEMENT";
+ case TYPE_BOTTOM_TAPPABLE_ELEMENT:
+ return "TYPE_BOTTOM_TAPPABLE_ELEMENT";
default:
- return "TYPE_UNKNOWN";
+ return "TYPE_UNKNOWN_" + type;
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 24f4c1431214..65fe87fa8ca0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,6 +17,10 @@
package android.view;
import static android.content.res.Resources.ID_NULL;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
+import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
@@ -96,6 +100,7 @@ import android.util.Property;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.StateSet;
+import android.util.StatsLog;
import android.util.SuperNotCalledException;
import android.util.TypedValue;
import android.view.AccessibilityIterators.CharacterTextSegmentIterator;
@@ -4062,11 +4067,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}, formatToHexString = true)
/* @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769414)
public int mPrivateFlags;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768943)
int mPrivateFlags2;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 129147060)
int mPrivateFlags3;
private int mPrivateFlags4;
@@ -14571,7 +14576,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (clickable) {
setPressed(true, x, y);
}
- checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y);
+ checkForLongClick(
+ ViewConfiguration.getLongPressTimeout(),
+ x,
+ y,
+ // This is not a touch gesture -- do not classify it as one.
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION);
return true;
}
}
@@ -15312,7 +15322,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mHasPerformedLongPress = false;
if (!clickable) {
- checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y);
+ checkForLongClick(
+ ViewConfiguration.getLongPressTimeout(),
+ x,
+ y,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
break;
}
@@ -15336,7 +15350,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
- checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y);
+ checkForLongClick(
+ ViewConfiguration.getLongPressTimeout(),
+ x,
+ y,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
break;
@@ -15373,7 +15391,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* ambiguousMultiplier);
// Subtract the time already spent
delay -= event.getEventTime() - event.getDownTime();
- checkForLongClick(delay, x, y);
+ checkForLongClick(
+ delay,
+ x,
+ y,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
touchSlop *= ambiguousMultiplier;
}
@@ -15395,7 +15417,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (deepPress && hasPendingLongPressCallback()) {
// process the long click action immediately
removeLongPressCallback();
- checkForLongClick(0 /* send immediately */, x, y);
+ checkForLongClick(
+ 0 /* send immediately */,
+ x,
+ y,
+ TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS);
}
break;
@@ -26143,7 +26169,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
- private void checkForLongClick(long delay, float x, float y) {
+ private void checkForLongClick(long delay, float x, float y, int classification) {
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) {
mHasPerformedLongPress = false;
@@ -26153,6 +26179,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPendingCheckForLongPress.setAnchor(x, y);
mPendingCheckForLongPress.rememberWindowAttachCount();
mPendingCheckForLongPress.rememberPressedState();
+ mPendingCheckForLongPress.setClassification(classification);
postDelayed(mPendingCheckForLongPress, delay);
}
}
@@ -27710,11 +27737,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private float mX;
private float mY;
private boolean mOriginalPressedState;
+ /**
+ * The classification of the long click being checked: one of the
+ * StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__* constants.
+ */
+ private int mClassification;
@Override
public void run() {
if ((mOriginalPressedState == isPressed()) && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
+ recordGestureClassification(mClassification);
if (performLongClick(mX, mY)) {
mHasPerformedLongPress = true;
}
@@ -27733,6 +27766,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public void rememberPressedState() {
mOriginalPressedState = isPressed();
}
+
+ public void setClassification(int classification) {
+ mClassification = classification;
+ }
}
private final class CheckForTap implements Runnable {
@@ -27745,17 +27782,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setPressed(true, x, y);
final long delay =
ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout();
- checkForLongClick(delay, x, y);
+ checkForLongClick(delay, x, y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
}
}
private final class PerformClick implements Runnable {
@Override
public void run() {
+ recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
performClickInternal();
}
}
+ /** Records a classification for the current event stream. */
+ private void recordGestureClassification(int classification) {
+ if (classification == TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION) {
+ return;
+ }
+ // To avoid negatively impacting View performance, the latency and displacement metrics
+ // are omitted.
+ StatsLog.write(StatsLog.TOUCH_GESTURE_CLASSIFIED, getClass().getName(), classification);
+ }
+
/**
* This method returns a ViewPropertyAnimator object, which can be used to animate
* specific properties on this View.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 49166ade34ce..ca6496903590 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1916,16 +1916,10 @@ public final class ViewRootImpl implements ViewParent,
}
contentInsets = ensureInsetsNonNegative(contentInsets, "content");
stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
- if (sNewInsetsMode != NEW_INSETS_MODE_NONE) {
- mLastWindowInsets = mInsetsController.calculateInsets(
- mContext.getResources().getConfiguration().isScreenRound(),
- mAttachInfo.mAlwaysConsumeSystemBars, displayCutout,
- contentInsets, stableInsets, mWindowAttributes.softInputMode);
- } else {
- mLastWindowInsets = new WindowInsets(contentInsets, stableInsets,
- mContext.getResources().getConfiguration().isScreenRound(),
- mAttachInfo.mAlwaysConsumeSystemBars, displayCutout);
- }
+ mLastWindowInsets = mInsetsController.calculateInsets(
+ mContext.getResources().getConfiguration().isScreenRound(),
+ mAttachInfo.mAlwaysConsumeSystemBars, displayCutout,
+ contentInsets, stableInsets, mWindowAttributes.softInputMode);
}
return mLastWindowInsets;
}
@@ -3986,7 +3980,7 @@ public final class ViewRootImpl implements ViewParent,
void systemGestureExclusionChanged() {
final List<Rect> rectsForWindowManager = mGestureExclusionTracker.computeChangedRects();
- if (rectsForWindowManager != null) {
+ if (rectsForWindowManager != null && mView != null) {
try {
mWindowSession.reportSystemGestureExclusionChanged(mWindow, rectsForWindowManager);
} catch (RemoteException e) {
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index ffa769a424a9..2d292ef7b25c 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -29,9 +29,6 @@ import static android.view.WindowInsets.Type.TOP_BAR;
import static android.view.WindowInsets.Type.all;
import static android.view.WindowInsets.Type.compatSystemInsets;
import static android.view.WindowInsets.Type.indexOf;
-import static android.view.WindowInsets.Type.mandatorySystemGestures;
-import static android.view.WindowInsets.Type.systemGestures;
-import static android.view.WindowInsets.Type.tappableElement;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -225,10 +222,6 @@ public final class WindowInsets {
}
Insets[] typeInsetMap = new Insets[SIZE];
assignCompatInsets(typeInsetMap, insets);
- // TODO: set system gesture insets based on actual system gesture area.
- typeInsetMap[indexOf(systemGestures())] = Insets.of(insets);
- typeInsetMap[indexOf(mandatorySystemGestures())] = Insets.of(insets);
- typeInsetMap[indexOf(tappableElement())] = Insets.of(insets);
return typeInsetMap;
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 4413585535f2..678a25223ef5 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -321,45 +321,6 @@ public final class WebViewFactory {
}
}
- /**
- * If the ApplicationInfo provided is for a stub WebView, fix up the object to include the
- * required values from the donor package. If the ApplicationInfo is for a full WebView,
- * leave it alone. Throws MissingWebViewPackageException if the donor is missing.
- */
- private static void fixupStubApplicationInfo(ApplicationInfo ai, PackageManager pm)
- throws MissingWebViewPackageException {
- String donorPackageName = null;
- if (ai.metaData != null) {
- donorPackageName = ai.metaData.getString("com.android.webview.WebViewDonorPackage");
- }
- if (donorPackageName != null) {
- PackageInfo donorPackage;
- try {
- donorPackage = pm.getPackageInfo(
- donorPackageName,
- PackageManager.GET_SHARED_LIBRARY_FILES
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
- | PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_FACTORY_ONLY);
- } catch (PackageManager.NameNotFoundException e) {
- throw new MissingWebViewPackageException("Failed to find donor package: " +
- donorPackageName);
- }
- ApplicationInfo donorInfo = donorPackage.applicationInfo;
-
- // Replace the stub's code locations with the donor's.
- ai.sourceDir = donorInfo.sourceDir;
- ai.splitSourceDirs = donorInfo.splitSourceDirs;
- ai.nativeLibraryDir = donorInfo.nativeLibraryDir;
- ai.secondaryNativeLibraryDir = donorInfo.secondaryNativeLibraryDir;
-
- // Copy the donor's primary and secondary ABIs, since the stub doesn't have native code
- // and so they are unset.
- ai.primaryCpuAbi = donorInfo.primaryCpuAbi;
- ai.secondaryCpuAbi = donorInfo.secondaryCpuAbi;
- }
- }
-
@UnsupportedAppUsage
private static Context getWebViewContextAndSetProvider() throws MissingWebViewPackageException {
Application initialApplication = AppGlobals.getInitialApplication();
@@ -411,7 +372,6 @@ public final class WebViewFactory {
verifyPackageInfo(response.packageInfo, newPackageInfo);
ApplicationInfo ai = newPackageInfo.applicationInfo;
- fixupStubApplicationInfo(ai, pm);
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"initialApplication.createApplicationContext");
@@ -494,18 +454,14 @@ public final class WebViewFactory {
*/
public static int onWebViewProviderChanged(PackageInfo packageInfo) {
int startedRelroProcesses = 0;
- ApplicationInfo originalAppInfo = new ApplicationInfo(packageInfo.applicationInfo);
try {
- fixupStubApplicationInfo(packageInfo.applicationInfo,
- AppGlobals.getInitialApplication().getPackageManager());
-
startedRelroProcesses = WebViewLibraryLoader.prepareNativeLibraries(packageInfo);
} catch (Throwable t) {
// Log and discard errors at this stage as we must not crash the system server.
Log.e(LOGTAG, "error preparing webview native library", t);
}
- WebViewZygote.onWebViewProviderChanged(packageInfo, originalAppInfo);
+ WebViewZygote.onWebViewProviderChanged(packageInfo);
return startedRelroProcesses;
}
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 09aa066549cb..62f54b943e11 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -16,8 +16,6 @@
package android.webkit;
-import android.app.LoadedApk;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.AsyncTask;
import android.os.Build;
@@ -29,10 +27,6 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
/** @hide */
public class WebViewZygote {
private static final String LOGTAG = "WebViewZygote";
@@ -56,13 +50,6 @@ public class WebViewZygote {
private static PackageInfo sPackage;
/**
- * Original ApplicationInfo for the selected WebView package before stub fixup. This is set from
- * #onWebViewProviderChanged().
- */
- @GuardedBy("sLock")
- private static ApplicationInfo sPackageOriginalAppInfo;
-
- /**
* Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote
* will not be started.
*/
@@ -110,11 +97,9 @@ public class WebViewZygote {
}
}
- public static void onWebViewProviderChanged(PackageInfo packageInfo,
- ApplicationInfo originalAppInfo) {
+ static void onWebViewProviderChanged(PackageInfo packageInfo) {
synchronized (sLock) {
sPackage = packageInfo;
- sPackageOriginalAppInfo = originalAppInfo;
// If multi-process is not enabled, then do not start the zygote service.
if (!sMultiprocessEnabled) {
@@ -165,34 +150,7 @@ public class WebViewZygote {
Process.FIRST_ISOLATED_UID,
Integer.MAX_VALUE); // TODO(b/123615476) deal with user-id ranges properly
ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress());
-
- if (sPackageOriginalAppInfo.sourceDir.equals(sPackage.applicationInfo.sourceDir)) {
- // No stub WebView is involved here, so we can preload the package the "clean" way
- // using the ApplicationInfo.
- sZygote.preloadApp(sPackage.applicationInfo, abi);
- } else {
- // Legacy path to support the stub WebView.
- // Reuse the logic from LoadedApk to determine the correct paths and pass them to
- // the zygote as strings.
- final List<String> zipPaths = new ArrayList<>(10);
- final List<String> libPaths = new ArrayList<>(10);
- LoadedApk.makePaths(null, false, sPackage.applicationInfo, zipPaths, libPaths);
- final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
- final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) :
- TextUtils.join(File.pathSeparator, zipPaths);
-
- String libFileName = WebViewFactory.getWebViewLibrary(sPackage.applicationInfo);
-
- // Use the original ApplicationInfo to determine what the original classpath would
- // have been to use as a cache key.
- LoadedApk.makePaths(null, false, sPackageOriginalAppInfo, zipPaths, null);
- final String cacheKey = (zipPaths.size() == 1) ? zipPaths.get(0) :
- TextUtils.join(File.pathSeparator, zipPaths);
-
- Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath);
- sZygote.preloadPackageForAbi(zip, librarySearchPath, libFileName, cacheKey,
- Build.SUPPORTED_ABIS[0]);
- }
+ sZygote.preloadApp(sPackage.applicationInfo, abi);
} catch (Exception e) {
Log.e(LOGTAG, "Error connecting to webview zygote", e);
stopZygoteLocked();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index f14b50dcd04d..07d28eba793d 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -24,7 +24,6 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
-import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.prediction.AppPredictionContext;
@@ -197,6 +196,11 @@ public class ChooserActivity extends ResolverActivity {
private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT = 2;
private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT = 3;
private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED = 4;
+ private static final int LIST_VIEW_UPDATE_MESSAGE = 5;
+
+ private static final int LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS = 250;
+
+ private boolean mListViewDataChanged = false;
@Retention(SOURCE)
@IntDef({CONTENT_PREVIEW_FILE, CONTENT_PREVIEW_IMAGE, CONTENT_PREVIEW_TEXT})
@@ -213,10 +217,13 @@ public class ChooserActivity extends ResolverActivity {
private final Handler mChooserHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
+ if (mChooserListAdapter == null || isDestroyed()) {
+ return;
+ }
+
switch (msg.what) {
case CHOOSER_TARGET_SERVICE_RESULT:
if (DEBUG) Log.d(TAG, "CHOOSER_TARGET_SERVICE_RESULT");
- if (isDestroyed()) break;
final ServiceResultInfo sri = (ServiceResultInfo) msg.obj;
if (!mServiceConnections.contains(sri.connection)) {
Log.w(TAG, "ChooserTargetServiceConnection " + sri.connection
@@ -240,17 +247,22 @@ public class ChooserActivity extends ResolverActivity {
if (DEBUG) {
Log.d(TAG, "CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT; unbinding services");
}
- if (mChooserListAdapter == null || isDestroyed()) {
- break;
- }
+
unbindRemainingServices();
sendVoiceChoicesIfNeeded();
mChooserListAdapter.completeServiceTargetLoading();
break;
+ case LIST_VIEW_UPDATE_MESSAGE:
+ if (DEBUG) {
+ Log.d(TAG, "LIST_VIEW_UPDATE_MESSAGE; ");
+ }
+
+ mChooserListAdapter.refreshListView();
+ break;
+
case SHORTCUT_MANAGER_SHARE_TARGET_RESULT:
if (DEBUG) Log.d(TAG, "SHORTCUT_MANAGER_SHARE_TARGET_RESULT");
- if (isDestroyed()) break;
final ServiceResultInfo resultInfo = (ServiceResultInfo) msg.obj;
if (resultInfo.resultTargets != null) {
mChooserListAdapter.addServiceResults(resultInfo.originalTarget,
@@ -829,6 +841,7 @@ public class ChooserActivity extends ResolverActivity {
mRefinementResultReceiver = null;
}
unbindRemainingServices();
+ mChooserHandler.removeMessages(LIST_VIEW_UPDATE_MESSAGE);
mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT);
if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) {
@@ -1872,6 +1885,23 @@ public class ChooserActivity extends ResolverActivity {
}
}
+ @Override
+ public void notifyDataSetChanged() {
+ if (!mListViewDataChanged) {
+ mChooserHandler.sendEmptyMessageDelayed(LIST_VIEW_UPDATE_MESSAGE,
+ LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+ mListViewDataChanged = true;
+ }
+ }
+
+ private void refreshListView() {
+ if (mListViewDataChanged) {
+ super.notifyDataSetChanged();
+ }
+ mListViewDataChanged = false;
+ }
+
+
private void createPlaceHolders() {
mServiceTargets.clear();
for (int i = 0; i < MAX_SERVICE_TARGETS; i++) {
@@ -1893,7 +1923,7 @@ public class ChooserActivity extends ResolverActivity {
}
if (mServiceTargets != null) {
- if (getDisplayInfoCount() == 0) {
+ if (getDisplayResolveInfoCount() == 0) {
// b/109676071: When packages change, onListRebuilt() is called before
// ResolverActivity.mDisplayList is re-populated; pruning now would cause the
// list to disappear briefly, so instead we detect this case (the
@@ -1906,12 +1936,14 @@ public class ChooserActivity extends ResolverActivity {
if (DEBUG) {
Log.d(TAG, "querying direct share targets from ShortcutManager");
}
+
queryDirectShareTargets(this);
}
if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) {
if (DEBUG) {
Log.d(TAG, "List built querying services");
}
+
queryTargetServices(this);
}
}
@@ -2007,7 +2039,7 @@ public class ChooserActivity extends ResolverActivity {
offset += callerTargetCount;
return filtered ? super.getItem(position - offset)
- : getDisplayInfoAt(position - offset);
+ : getDisplayResolveInfo(position - offset);
}
public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets) {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 9f9e083f1fb1..f47469a0e1b3 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1230,7 +1230,7 @@ public class ResolverActivity extends Activity {
final ImageView iconView = findViewById(R.id.icon);
final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem();
if (iconView != null && iconInfo != null) {
- new LoadIconIntoViewTask(iconInfo, iconView).execute();
+ new LoadIconTask(iconInfo, iconView).execute();
}
}
@@ -1871,14 +1871,6 @@ public class ResolverActivity extends Activity {
return mDisplayList.size();
}
- public int getDisplayInfoCount() {
- return mDisplayList.size();
- }
-
- public DisplayResolveInfo getDisplayInfoAt(int index) {
- return mDisplayList.get(index);
- }
-
@Nullable
public TargetInfo getItem(int position) {
if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) {
@@ -1966,9 +1958,10 @@ public class ResolverActivity extends Activity {
if (info instanceof DisplayResolveInfo
&& !((DisplayResolveInfo) info).hasDisplayIcon()) {
- new LoadAdapterIconTask((DisplayResolveInfo) info).execute();
+ new LoadIconTask((DisplayResolveInfo) info, holder.icon).execute();
+ } else {
+ holder.icon.setImageDrawable(info.getDisplayIcon());
}
- holder.icon.setImageDrawable(info.getDisplayIcon());
}
}
@@ -2087,13 +2080,15 @@ public class ResolverActivity extends Activity {
}
- abstract class LoadIconTask extends AsyncTask<Void, Void, Drawable> {
+ class LoadIconTask extends AsyncTask<Void, Void, Drawable> {
protected final DisplayResolveInfo mDisplayResolveInfo;
private final ResolveInfo mResolveInfo;
+ private final ImageView mTargetView;
- public LoadIconTask(DisplayResolveInfo dri) {
+ LoadIconTask(DisplayResolveInfo dri, ImageView target) {
mDisplayResolveInfo = dri;
mResolveInfo = dri.getResolveInfo();
+ mTargetView = target;
}
@Override
@@ -2103,37 +2098,12 @@ public class ResolverActivity extends Activity {
@Override
protected void onPostExecute(Drawable d) {
- mDisplayResolveInfo.setDisplayIcon(d);
- }
- }
-
- class LoadAdapterIconTask extends LoadIconTask {
- public LoadAdapterIconTask(DisplayResolveInfo dri) {
- super(dri);
- }
-
- @Override
- protected void onPostExecute(Drawable d) {
- super.onPostExecute(d);
if (mProfileView != null && mAdapter.getOtherProfile() == mDisplayResolveInfo) {
bindProfileView();
+ } else {
+ mDisplayResolveInfo.setDisplayIcon(d);
+ mTargetView.setImageDrawable(d);
}
- mAdapter.notifyDataSetChanged();
- }
- }
-
- class LoadIconIntoViewTask extends LoadIconTask {
- private final ImageView mTargetView;
-
- public LoadIconIntoViewTask(DisplayResolveInfo dri, ImageView target) {
- super(dri);
- mTargetView = target;
- }
-
- @Override
- protected void onPostExecute(Drawable d) {
- super.onPostExecute(d);
- mTargetView.setImageDrawable(d);
}
}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index dfb6c0817043..9a9c9d14154b 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -48,6 +48,7 @@ import "frameworks/base/core/proto/android/service/notification.proto";
import "frameworks/base/core/proto/android/service/package.proto";
import "frameworks/base/core/proto/android/service/print.proto";
import "frameworks/base/core/proto/android/service/procstats.proto";
+import "frameworks/base/core/proto/android/service/restricted_image.proto";
import "frameworks/base/core/proto/android/service/usb.proto";
import "frameworks/base/core/proto/android/util/event_log_tags.proto";
import "frameworks/base/core/proto/android/util/log.proto";
@@ -314,6 +315,12 @@ message IncidentProto {
(section).args = "role --proto"
];
+ optional android.service.restricted_image.RestrictedImagesDumpProto restricted_images = 3025 [
+ (section).type = SECTION_DUMPSYS,
+ (section).userdebug_and_eng_only = true,
+ (section).args = "incidentcompanion --restricted_image"
+ ];
+
// Reserved for OEMs.
extensions 50000 to 100000;
}
diff --git a/core/proto/android/service/restricted_image.proto b/core/proto/android/service/restricted_image.proto
new file mode 100644
index 000000000000..4a33d478fbde
--- /dev/null
+++ b/core/proto/android/service/restricted_image.proto
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+package android.service.restricted_image;
+
+option java_multiple_files = true;
+option java_outer_classname = "RestrictedImage";
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+// Restricted Image proto is for collecting images from the user with their
+// permission for the purpose of debugging photos.
+message RestrictedImagesDumpProto {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ repeated RestrictedImageSetProto sets = 1;
+}
+
+message RestrictedImageSetProto {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ // Name of the service producing the data.
+ optional string category = 1;
+
+ // The images
+ repeated RestrictedImageProto images = 2;
+
+ // Additional metadata
+ optional bytes metadata = 3;
+}
+
+message RestrictedImageProto {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ // Type of image data
+ optional string mime_type = 1;
+
+ // The image data
+ optional bytes image_data = 2;
+
+ // Metadata about the image. Typically this has another proto schema,
+ // but it is undefined exactly what that is in AOSP code.
+ optional bytes metadata = 3;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ba7a93f52939..ab86c42ac12f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1014,6 +1014,9 @@
call with the option to redirect the call to a different number or
abort the call altogether.
<p>Protection level: dangerous
+
+ @deprecated Applications should use {@link android.telecom.CallRedirectionService} instead
+ of the {@link android.content.Intent#ACTION_NEW_OUTGOING_CALL} broadcast.
-->
<permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
android:permissionGroup="android.permission-group.UNDEFINED"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 77ce8c382fad..5cbe0038f871 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3241,6 +3241,12 @@
Only applies if the device display is not square. -->
<bool name="config_navBarCanMove">true</bool>
+ <!-- Controls whether the navigation bar lets through taps. -->
+ <bool name="config_navBarTapThrough">false</bool>
+
+ <!-- Controls the size of the back gesture inset. -->
+ <dimen name="config_backGestureInset">0dp</dimen>
+
<!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
These values are in DPs and will be converted to pixel sizes internally. -->
<string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string>
@@ -3983,6 +3989,9 @@
<!-- Whether or not to enable automatic heap dumps for the system server on debuggable builds. -->
<bool name="config_debugEnableAutomaticSystemServerHeapDumps">false</bool>
+ <!-- Trigger a heap dump if the system server pss usage exceeds this threshold. 400 MB -->
+ <integer name="config_debugSystemServerPssThresholdBytes">419430400</integer>
+
<!-- See DropBoxManagerService.
The minimum period in milliseconds between broadcasts for entries with low priority
dropbox tags. -->
@@ -4003,4 +4012,6 @@
<item>system_server_wtf</item>
</string-array>
+ <!-- Which binder services to include in incident reports containing restricted images. -->
+ <string-array name="config_restrictedImagesServices" translatable="false"/>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index feecd020b087..c646fefad9d6 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -47,6 +47,9 @@
<dimen name="navigation_bar_height_landscape">48dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
<dimen name="navigation_bar_width">48dp</dimen>
+ <!-- How much we expand the touchable region of the status bar below the notch to catch touches
+ that just start below the notch. -->
+ <dimen name="display_cutout_touchable_region_size">12dp</dimen>
<!-- EXPERIMENT BEGIN -->
<!-- Height of the bottom navigation bar frame; this is different than navigation_bar_height
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 03259ed2c0c6..4320bf41f467 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4325,10 +4325,10 @@
<!-- Activity starter -->
<!-- Toast message for blocking background activity starts feature running in permissive mode -->
- <string name="activity_starter_block_bg_activity_starts_permissive">This background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> will be blocked in future Q builds. See go/q-bg-block.</string>
+ <string name="activity_starter_block_bg_activity_starts_permissive">This background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> will be blocked in future Q builds. See g.co/dev/bgblock.</string>
<!-- Toast message for blocking background activity starts feature running in enforcing mode -->
- <string name="activity_starter_block_bg_activity_starts_enforcing">Background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> blocked. See go/q-bg-block. </string>
+ <string name="activity_starter_block_bg_activity_starts_enforcing">Background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> blocked. See g.co/dev/bgblock. </string>
<!-- Keyguard strings -->
<!-- Message shown in pattern unlock after some number of unsuccessful attempts -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 35fd88e64d5b..a29a4f8845f5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1750,6 +1750,7 @@
<java-symbol type="dimen" name="navigation_bar_height_landscape_car_mode" />
<java-symbol type="dimen" name="navigation_bar_width_car_mode" />
<java-symbol type="dimen" name="status_bar_height" />
+ <java-symbol type="dimen" name="display_cutout_touchable_region_size" />
<java-symbol type="dimen" name="quick_qs_offset_height" />
<java-symbol type="dimen" name="quick_qs_total_height" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
@@ -2844,6 +2845,8 @@
<java-symbol type="integer" name="config_navBarOpacityMode" />
<java-symbol type="integer" name="config_navBarInteractionMode" />
<java-symbol type="bool" name="config_navBarCanMove" />
+ <java-symbol type="bool" name="config_navBarTapThrough" />
+ <java-symbol type="dimen" name="config_backGestureInset" />
<java-symbol type="color" name="system_bar_background_semi_transparent" />
<!-- EditText suggestion popup. -->
@@ -3230,6 +3233,7 @@
<java-symbol type="bool" name="config_batterymeterDualTone" />
<java-symbol type="bool" name="config_debugEnableAutomaticSystemServerHeapDumps" />
+ <java-symbol type="integer" name="config_debugSystemServerPssThresholdBytes" />
<!-- Accessibility Shortcut -->
<java-symbol type="string" name="accessibility_shortcut_warning_dialog_title" />
@@ -3686,6 +3690,7 @@
<java-symbol type="integer" name="config_attentionApiTimeout" />
<java-symbol type="string" name="config_incidentReportApproverPackage" />
+ <java-symbol type="array" name="config_restrictedImagesServices" />
<!-- Display White-Balance -->
<java-symbol type="integer" name="config_displayWhiteBalanceBrightnessSensorRate" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 3c8794f7c5fa..ed198e60902b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -258,6 +258,7 @@ applications that come with the platform
<permission name="android.permission.ACTIVITY_EMBEDDING"/>
<permission name="android.permission.FORCE_STOP_PACKAGES"/>
<permission name="android.permission.GET_APP_OPS_STATS"/>
+ <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
<permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
<permission name="android.permission.INSTALL_PACKAGES"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index e4142a933f0f..adc04fb03e2a 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -688,20 +688,20 @@ public abstract class Drawable {
* {@link #setColorFilter(int, PorterDuff.Mode)} overrides tint.
* </p>
*
- * @param tintMode A Porter-Duff blending mode
+ * @param tintMode A Porter-Duff blending mode to apply to the drawable, a value of null sets
+ * the default Porter-Diff blending mode value
+ * of {@link PorterDuff.Mode#SRC_IN}
* @see #setTint(int)
* @see #setTintList(ColorStateList)
*
* @deprecated use {@link #setTintMode(BlendMode)} instead
*/
@Deprecated
- public void setTintMode(@NonNull PorterDuff.Mode tintMode) {
+ public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
if (!mSetTintModeInvoked) {
mSetTintModeInvoked = true;
- BlendMode mode = BlendMode.fromValue(tintMode.nativeInt);
- if (mode != null) {
- setTintMode(mode);
- }
+ BlendMode mode = tintMode != null ? BlendMode.fromValue(tintMode.nativeInt) : null;
+ setTintMode(mode != null ? mode : Drawable.DEFAULT_BLEND_MODE);
mSetTintModeInvoked = false;
}
}
@@ -716,17 +716,16 @@ public abstract class Drawable {
* {@link #setColorFilter(ColorFilter)}
* </p>
*
- * @param blendMode
+ * @param blendMode BlendMode to apply to the drawable, a value of null sets the default
+ * blend mode value of {@link BlendMode#SRC_IN}
* @see #setTint(int)
* @see #setTintList(ColorStateList)
*/
- public void setTintMode(@NonNull BlendMode blendMode) {
+ public void setTintMode(@Nullable BlendMode blendMode) {
if (!mSetBlendModeInvoked) {
mSetBlendModeInvoked = true;
PorterDuff.Mode mode = BlendMode.blendModeToPorterDuffMode(blendMode);
- if (mode != null) {
- setTintMode(mode);
- }
+ setTintMode(mode != null ? mode : Drawable.DEFAULT_TINT_MODE);
mSetBlendModeInvoked = false;
}
}
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 6cce31943d03..b76e49ce94a0 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -209,8 +209,8 @@ void RenderThread::requireVkContext() {
mVkManager->initialize();
GrContextOptions options;
initGrContextOptions(options);
- // TODO: get a string describing the SPIR-V compiler version and use it here
- cacheManager().configureContext(&options, nullptr, 0);
+ auto vkDriverVersion = mVkManager->getDriverVersion();
+ cacheManager().configureContext(&options, &vkDriverVersion, sizeof(vkDriverVersion));
sk_sp<GrContext> grContext = mVkManager->createContext(options);
LOG_ALWAYS_FATAL_IF(!grContext.get());
setGrContext(grContext);
@@ -408,12 +408,13 @@ bool RenderThread::isCurrent() {
}
void RenderThread::preload() {
- std::thread eglInitThread([]() {
- //TODO: don't load EGL drivers for Vulkan, when HW bitmap uploader is refactored.
- eglGetDisplay(EGL_DEFAULT_DISPLAY);
- });
- eglInitThread.detach();
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ // EGL driver is always preloaded only if HWUI renders with GL.
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+ std::thread eglInitThread([]() {
+ eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ });
+ eglInitThread.detach();
+ } else {
requireVkContext();
}
HardwareBitmapUploader::initialize();
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index c92909898652..ac62ff47f50a 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -170,6 +170,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe
VkPhysicalDeviceProperties physDeviceProperties;
mGetPhysicalDeviceProperties(mPhysicalDevice, &physDeviceProperties);
LOG_ALWAYS_FATAL_IF(physDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0));
+ mDriverVersion = physDeviceProperties.driverVersion;
// query to get the initial queue props size
uint32_t queueCount;
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index c2d18029c731..54333f326d4f 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -82,6 +82,8 @@ public:
sk_sp<GrContext> createContext(const GrContextOptions& options);
+ uint32_t getDriverVersion() const { return mDriverVersion; }
+
private:
friend class VulkanSurface;
// Sets up the VkInstance and VkDevice objects. Also fills out the passed in
@@ -178,6 +180,7 @@ private:
};
SwapBehavior mSwapBehavior = SwapBehavior::Discard;
GrVkExtensions mExtensions;
+ uint32_t mDriverVersion = 0;
};
} /* namespace renderthread */
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index a3eee0a67d56..bf1063d0907f 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -557,7 +557,7 @@ public final class AudioAttributes implements Parcelable {
private int mContentType = CONTENT_TYPE_UNKNOWN;
private int mSource = MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID;
private int mFlags = 0x0;
- private boolean mMuteHapticChannels = false;
+ private boolean mMuteHapticChannels = true;
private HashSet<String> mTags = new HashSet<String>();
private Bundle mBundle;
@@ -888,7 +888,7 @@ public final class AudioAttributes implements Parcelable {
/**
* Specifying if haptic should be muted or not when playing audio-haptic coupled data.
- * By default, haptic channels are enabled.
+ * By default, haptic channels are disabled.
* @param muted true to force muting haptic channels.
* @return the same Builder instance.
*/
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index 44e0a659212a..a5286364dc26 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -18,7 +18,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.captiveportallogin"
- android:versionCode="11"
+ android:versionCode="200000000"
android:versionName="Q-initial">
<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
index 395eac1d2ccb..2fe9d21b0489 100644
--- a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -18,8 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true"
- android:visibility="gone">
+ android:fitsSystemWindows="true">
<LinearLayout
android:id="@+id/container"
diff --git a/packages/CarSystemUI/res/layout/notification_center_activity.xml b/packages/CarSystemUI/res/layout/notification_center_activity.xml
index 383aba4e400a..5c915b874dde 100644
--- a/packages/CarSystemUI/res/layout/notification_center_activity.xml
+++ b/packages/CarSystemUI/res/layout/notification_center_activity.xml
@@ -19,7 +19,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/notification_view"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:background="@color/notification_shade_background_color">
<View
android:id="@+id/glass_pane"
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index 0594dce2ba22..9a074ddd7506 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -64,7 +64,6 @@
<include layout="@layout/car_top_navigation_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
/>
</LinearLayout>
@@ -80,6 +79,13 @@
android:layout_height="match_parent"
android:visibility="invisible"/>
+ <include layout="@layout/notification_center_activity"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginBottom="112dp"
+ android:visibility="invisible"
+ />
+
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index c9f9dead6e81..d946fbc9520a 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -21,6 +21,7 @@
<string name="config_systemUIFactoryComponent" translatable="false">
com.android.systemui.CarSystemUIFactory
</string>
+
<bool name="config_enableFullscreenUserSwitcher">true</bool>
<!-- configure which system ui bars should be displayed -->
@@ -28,30 +29,12 @@
<bool name="config_enableRightNavigationBar">false</bool>
<bool name="config_enableBottomNavigationBar">true</bool>
- <!-- SystemUI Services: The classes of the stuff to start. This is duplicated from core
- SystemUi b/c it can't be overlayed at this level for now
- -->
- <string-array name="config_systemUIServiceComponents" translatable="false">
- <item>com.android.systemui.Dependency$DependencyCreator</item>
- <item>com.android.systemui.util.NotificationChannels</item>
- <item>com.android.systemui.notifications.NotificationsUI</item>
- <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
- <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
- <item>com.android.systemui.recents.Recents</item>
- <item>com.android.systemui.volume.VolumeUI</item>
- <item>com.android.systemui.stackdivider.Divider</item>
- <item>com.android.systemui.SystemBars</item>
- <item>com.android.systemui.usb.StorageNotification</item>
- <item>com.android.systemui.power.PowerUI</item>
- <item>com.android.systemui.media.RingtonePlayer</item>
- <item>com.android.systemui.keyboard.KeyboardUI</item>
- <item>com.android.systemui.pip.PipUI</item>
- <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
- <item>@string/config_systemUIVendorServiceComponent</item>
- <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
- <item>com.android.systemui.LatencyTester</item>
- <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
- <item>com.android.systemui.ScreenDecorations</item>
- <item>com.android.systemui.SliceBroadcastRelayHandler</item>
- </string-array>
+ <bool name="config_hideNavWhenKeyguardBouncerShown">true</bool>
+ <bool name="config_enablePersistentDockedActivity">false</bool>
+ <string name="config_persistentDockedActivityIntentUri" translatable="false"></string>
+
+ <!-- How many icons may be shown at once in the system bar. Includes any
+ slots that may be reused for things like IME control. -->
+ <integer name="config_maxNotificationIcons">0</integer>
+
</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
deleted file mode 100644
index 2e2f3b7bea11..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * Copyright (C) 2018 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.notifications;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.car.Car;
-import android.car.CarNotConnectedException;
-import android.car.drivingstate.CarUxRestrictionsManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.ServiceConnection;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
-import android.os.IBinder;
-import android.os.ServiceManager;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.car.notification.CarNotificationListener;
-import com.android.car.notification.CarNotificationView;
-import com.android.car.notification.CarUxRestrictionManagerWrapper;
-import com.android.car.notification.NotificationClickHandlerFactory;
-import com.android.car.notification.NotificationViewController;
-import com.android.car.notification.PreprocessingManager;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.SystemUI;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.statusbar.car.CarStatusBar;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-
-/**
- * Standalone SystemUI for displaying Notifications that have been designed to be used in the car
- */
-public class NotificationsUI extends SystemUI
- implements ConfigurationController.ConfigurationListener {
-
- private static final String TAG = "NotificationsUI";
- // used to calculate how fast to open or close the window
- private static final float DEFAULT_FLING_VELOCITY = 0;
- // max time a fling animation takes
- private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
- // acceleration rate for the fling animation
- private static final float FLING_SPEED_UP_FACTOR = 0.6f;
- private CarNotificationListener mCarNotificationListener;
- private CarUxRestrictionsManager mCarUxRestrictionsManager;
- private NotificationClickHandlerFactory mClickHandlerFactory;
- private Car mCar;
- private ViewGroup mCarNotificationWindow;
- private NotificationViewController mNotificationViewController;
- private boolean mIsShowing;
- private boolean mIsTracking;
- private boolean mNotificationListAtBottom;
- private boolean mNotificationListAtBottomAtTimeOfTouch;
- private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper =
- new CarUxRestrictionManagerWrapper();
- // Used in the Notification panel touch listener
- private GestureDetector mGestureDetector;
- // Used in scrollable content of the notifications
- private GestureDetector mScrollUpDetector;
- private View mContent;
- private View.OnTouchListener mOnTouchListener;
- private FlingAnimationUtils mFlingAnimationUtils;
- private static int sSettleOpenPercentage;
- private static int sSettleClosePercentage;
- private CarStatusBar mCarStatusBar;
-
- /**
- * Inits the window that hosts the notifications and establishes the connections
- * to the car related services.
- */
- @Override
- public void start() {
- sSettleOpenPercentage = mContext.getResources().getInteger(
- R.integer.notification_settle_open_percentage);
- sSettleClosePercentage = mContext.getResources().getInteger(
- R.integer.notification_settle_close_percentage);
- WindowManager windowManager =
- (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mFlingAnimationUtils = new FlingAnimationUtils(mContext,
- FLING_ANIMATION_MAX_TIME, FLING_SPEED_UP_FACTOR);
- mCarNotificationListener = new CarNotificationListener();
- // create a notification click handler that closes the notification ui if the an activity
- // is launched successfully
- mClickHandlerFactory = new NotificationClickHandlerFactory(
- IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE)),
- launchResult -> {
- if (launchResult == ActivityManager.START_TASK_TO_FRONT
- || launchResult == ActivityManager.START_SUCCESS) {
- closeCarNotifications(DEFAULT_FLING_VELOCITY);
- }
- });
- mCarNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper,
- mClickHandlerFactory);
- mCar = Car.createCar(mContext, mCarConnectionListener);
- mCar.connect();
- NotificationGestureListener gestureListener = new NotificationGestureListener();
- mGestureDetector = new GestureDetector(mContext, gestureListener);
- mScrollUpDetector = new GestureDetector(mContext, new ScrollUpDetector());
- mOnTouchListener = new NotificationPanelTouchListener();
- mCarNotificationWindow = (ViewGroup) View.inflate(new ContextThemeWrapper(mContext,
- R.style.Theme_Notification),
- R.layout.navigation_bar_window, null);
- mCarNotificationWindow
- .setBackgroundColor(mContext.getColor(R.color.notification_shade_background_color));
- inflateNotificationContent();
-
- WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSLUCENT);
- layoutParams.setTitle("Car Notification Window");
- // start in the hidden state
- mCarNotificationWindow.setVisibility(View.GONE);
- windowManager.addView(mCarNotificationWindow, layoutParams);
-
- // Add this object to the SystemUI component registry such that the status bar
- // can get a reference to it.
- putComponent(NotificationsUI.class, this);
- Dependency.get(ConfigurationController.class).addCallback(this);
- }
-
- @SuppressLint("ClickableViewAccessibility")
- private void inflateNotificationContent() {
- if (mNotificationViewController != null) {
- mNotificationViewController.disable();
- }
- mCarNotificationWindow.removeAllViews();
-
- mContent = View.inflate(new ContextThemeWrapper(mContext,
- com.android.car.notification.R.style.Theme_Notification),
- R.layout.notification_center_activity,
- mCarNotificationWindow);
- // set the click handler such that we can dismiss the UI when a notification is clicked
- CarNotificationView noteView = mCarNotificationWindow.findViewById(R.id.notification_view);
- noteView.setClickHandlerFactory(mClickHandlerFactory);
-
- mContent.setOnTouchListener(mOnTouchListener);
- // set initial translation after size is calculated
- mContent.getViewTreeObserver().addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- mContent.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- if (!mIsShowing && !mIsTracking) {
- mContent.setTranslationY(mContent.getHeight() * -1);
- }
- }
- });
-
- RecyclerView notificationList = mCarNotificationWindow.findViewById(R.id.notifications);
- // register a scroll listener so we can figure out if we are at the bottom of the
- // list of notifications
- notificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
- super.onScrolled(recyclerView, dx, dy);
- if (!notificationList.canScrollVertically(1)) {
- mNotificationListAtBottom = true;
- return;
- }
- mNotificationListAtBottom = false;
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
- });
- // add a touch listener such that when the user scrolls up and they are at the bottom
- // of the list we can start the closing of the view.
- notificationList.setOnTouchListener(new NotificationListTouchListener());
-
- // There's a view installed at a higher z-order such that we can intercept the ACTION_DOWN
- // to set the initial click state.
- mCarNotificationWindow.findViewById(R.id.glass_pane).setOnTouchListener((v, event) -> {
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
- // register the down event with the gesture detectors so then know where the down
- // started. This is needed because at this point we don't know which listener
- // is going to handle scroll and fling events.
- mGestureDetector.onTouchEvent(event);
- mScrollUpDetector.onTouchEvent(event);
- }
- return false;
- });
-
- mNotificationViewController = new NotificationViewController(
- mCarNotificationWindow
- .findViewById(com.android.car.notification.R.id.notification_view),
- PreprocessingManager.getInstance(mContext),
- mCarNotificationListener,
- mCarUxRestrictionManagerWrapper);
- mNotificationViewController.enable();
- }
-
- // allows for day night switch
- @Override
- public void onConfigChanged(Configuration newConfig) {
- inflateNotificationContent();
- }
-
- public void setStatusBar(CarStatusBar carStatusBar) {
- mCarStatusBar = carStatusBar;
- }
-
- public View.OnTouchListener getDragDownListener() {
- return mOnTouchListener;
- }
-
- /**
- * This listener is attached to the notification list UI to intercept gestures if the user
- * is scrolling up when the notification list is at the bottom
- */
- private class ScrollUpDetector extends GestureDetector.SimpleOnGestureListener {
-
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- return distanceY > 0;
- }
-
- @Override
- public boolean onSingleTapUp(MotionEvent motionEvent) {
- closeCarNotifications(DEFAULT_FLING_VELOCITY);
- return false;
- }
- }
-
- private class NotificationListTouchListener implements View.OnTouchListener {
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- // reset mNotificationListAtBottomAtTimeOfTouch here since the "glass pane" will not
- // get the up event
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
- boolean wasScrolledUp = mScrollUpDetector.onTouchEvent(event);
-
- if (mIsTracking
- || (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom
- && wasScrolledUp)) {
- mOnTouchListener.onTouch(v, event);
- // touch event should not be propagated further
- return true;
- }
- return false;
- }
- }
-
- /**
- * Touch listener installed on the notification panel. It is also used by the Nav and StatusBar
- */
- private class NotificationPanelTouchListener implements View.OnTouchListener {
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- boolean consumed = mGestureDetector.onTouchEvent(event);
- if (consumed) {
- return true;
- }
- if (!mIsTracking || event.getActionMasked() != MotionEvent.ACTION_UP) {
- return false;
- }
-
- float percentFromBottom =
- Math.abs(mContent.getTranslationY() / mContent.getHeight()) * 100;
- if (mIsShowing) {
- if (percentFromBottom < sSettleOpenPercentage) {
- // panel started to close but did not cross minimum threshold thus we open
- // it back up
- openCarNotifications(DEFAULT_FLING_VELOCITY);
- return true;
- }
- // panel was lifted more than the threshold thus we close it the rest of the way
- closeCarNotifications(DEFAULT_FLING_VELOCITY);
- return true;
- }
-
- if (percentFromBottom > sSettleClosePercentage) {
- // panel was only peeked at thus close it back up
- closeCarNotifications(DEFAULT_FLING_VELOCITY);
- return true;
- }
- // panel has been open more than threshold thus open it the rest of the way
- openCarNotifications(DEFAULT_FLING_VELOCITY);
- return true;
-
- }
- }
-
- /**
- * Listener called by mGestureDetector. This will be initiated from the
- * NotificationPanelTouchListener
- */
- private class NotificationGestureListener extends GestureDetector.SimpleOnGestureListener {
- private static final int SWIPE_UP_MIN_DISTANCE = 75;
- private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
- private static final int SWIPE_MAX_OFF_PATH = 75;
- private static final int SWIPE_THRESHOLD_VELOCITY = 200;
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
- if (mCarStatusBar == null || !mCarStatusBar.getIsUserSetup()) {
- return true;
- }
- boolean isDown = event1.getY() - event2.getY() < 0;
- // CarStatusBar and NavigationBar are identical so avoid the touch if it
- // starts from NavigationBar to open.
- if (event1.getRawY() > mCarNotificationWindow.getHeight() && isDown
- && mCarNotificationWindow.getVisibility() == View.GONE) {
- mIsTracking = false;
- return true;
- }
- mIsTracking = true;
- mCarNotificationWindow.setVisibility(View.VISIBLE);
-
- mContent.setTranslationY(Math.min(mContent.getTranslationY() - distanceY, 0));
- if (mContent.getTranslationY() == 0) {
- mIsTracking = false;
- }
- return true;
- }
-
- @Override
- public boolean onFling(MotionEvent event1, MotionEvent event2,
- float velocityX, float velocityY) {
- if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
- || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
- // swipe was not vertical or was not fast enough
- return false;
- }
-
- boolean isUp = velocityY < 0;
- float distanceDelta = Math.abs(event1.getY() - event2.getY());
- if (isUp && distanceDelta > SWIPE_UP_MIN_DISTANCE) {
- // fling up
- mIsTracking = false;
- closeCarNotifications(Math.abs(velocityY));
- return true;
-
- } else if (!isUp && distanceDelta > SWIPE_DOWN_MIN_DISTANCE
- && (event1.getRawY() < mCarNotificationWindow.getHeight()
- || mCarNotificationWindow.getVisibility() == View.VISIBLE)) {
- // fling down
- mIsTracking = false;
- openCarNotifications(velocityY);
- return true;
- }
-
- return false;
- }
- }
-
- /**
- * Connection callback to establish UX Restrictions
- */
- private ServiceConnection mCarConnectionListener = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- mCarUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(
- Car.CAR_UX_RESTRICTION_SERVICE);
- mCarUxRestrictionManagerWrapper
- .setCarUxRestrictionsManager(mCarUxRestrictionsManager);
- PreprocessingManager preprocessingManager = PreprocessingManager.getInstance(
- mContext);
- preprocessingManager
- .setCarUxRestrictionManagerWrapper(mCarUxRestrictionManagerWrapper);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car not connected in CarConnectionListener", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.e(TAG, "Car service disconnected unexpectedly");
- }
- };
-
- /**
- * Toggles the visibility of the notifications
- */
- public void toggleShowingCarNotifications() {
- if (mCarNotificationWindow.getVisibility() == View.VISIBLE) {
- closeCarNotifications(DEFAULT_FLING_VELOCITY);
- return;
- }
- openCarNotifications(DEFAULT_FLING_VELOCITY);
- }
-
- /**
- * Hides the notifications
- */
- public void closeCarNotifications(float velocityY) {
- float closedTranslation = mContent.getHeight() * -1;
- ValueAnimator animator =
- ValueAnimator.ofFloat(mContent.getTranslationY(), closedTranslation);
- animator.addUpdateListener(
- animation -> mContent.setTranslationY((Float) animation.getAnimatedValue()));
- mFlingAnimationUtils.apply(
- animator, mContent.getTranslationY(), closedTranslation, velocityY);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mCarNotificationWindow.setVisibility(View.GONE);
- }
- });
- animator.start();
- mNotificationViewController.disable();
- mIsShowing = false;
- mIsTracking = false;
- RecyclerView notificationListView = mCarNotificationWindow.findViewById(R.id.notifications);
- notificationListView.scrollToPosition(0);
- }
-
- /**
- * Sets the notifications to visible
- */
- public void openCarNotifications(float velocityY) {
- if (mCarStatusBar == null || !mCarStatusBar.getIsUserSetup()) {
- return;
- }
- mCarNotificationWindow.setVisibility(View.VISIBLE);
-
- ValueAnimator animator = ValueAnimator.ofFloat(mContent.getTranslationY(), 0);
- animator.addUpdateListener(
- animation -> mContent.setTranslationY((Float) animation.getAnimatedValue()));
- mFlingAnimationUtils.apply(animator, mContent.getTranslationY(), 0, velocityY);
- animator.start();
-
- mNotificationViewController.enable();
- mIsShowing = true;
- mIsTracking = false;
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index afefa1b1fa56..a0f2367c65a4 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -38,6 +38,7 @@ class CarNavigationBarView extends LinearLayout {
private CarStatusBar mCarStatusBar;
private Context mContext;
private View mLockScreenButtons;
+ // used to wire in open/close gestures for notifications
private OnTouchListener mStatusBarWindowTouchListener;
@@ -65,26 +66,45 @@ class CarNavigationBarView extends LinearLayout {
mDarkIconManager.setShouldLog(true);
Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
}
+ // needs to be clickable so that it will receive ACTION_MOVE events
+ setClickable(true);
}
+ // Used to forward touch events even if the touch was initiated from a child component
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mStatusBarWindowTouchListener == null) {
- return false;
+ if (mStatusBarWindowTouchListener != null) {
+ // forward touch events to the status bar window so it can add a drag down
+ // windows if required (Notification shade)
+ mStatusBarWindowTouchListener.onTouch(this, ev);
}
- // forward touch events to the status bar window so it can add a drag down
- // windows if required (Notification shade)
- mStatusBarWindowTouchListener.onTouch(this, ev);
- return false;
+ return super.onInterceptTouchEvent(ev);
}
+
void setStatusBar(CarStatusBar carStatusBar) {
mCarStatusBar = carStatusBar;
- mStatusBarWindowTouchListener = carStatusBar.getStatusBarWindowTouchListener();
+ }
+
+ /**
+ * Set a touch listener that will be called from onInterceptTouchEvent and onTouchEvent
+ *
+ * @param statusBarWindowTouchListener The listener to call from touch and intercept touch
+ */
+ void setStatusBarWindowTouchListener(OnTouchListener statusBarWindowTouchListener) {
+ mStatusBarWindowTouchListener = statusBarWindowTouchListener;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mStatusBarWindowTouchListener != null) {
+ mStatusBarWindowTouchListener.onTouch(this, event);
+ }
+ return super.onTouchEvent(event);
}
protected void onNotificationsClick(View v) {
- mCarStatusBar.toggleCarNotifications();
+ mCarStatusBar.togglePanel();
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index abe9be87fe7b..9bcc1ab90e47 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -16,17 +16,29 @@
package com.android.systemui.statusbar.car;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.car.drivingstate.CarDrivingStateEvent;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.util.Log;
+import android.view.GestureDetector;
import android.view.Gravity;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.car.notification.CarNotificationListener;
+import com.android.car.notification.CarNotificationView;
+import com.android.car.notification.CarUxRestrictionManagerWrapper;
+import com.android.car.notification.NotificationClickHandlerFactory;
+import com.android.car.notification.NotificationViewController;
+import com.android.car.notification.PreprocessingManager;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.CarSystemUIFactory;
@@ -37,7 +49,6 @@ import com.android.systemui.SystemUIFactory;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.notifications.NotificationsUI;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.car.CarQSFragment;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -70,7 +81,6 @@ public class CarStatusBar extends StatusBar implements
private BatteryMeterView mBatteryMeterView;
private Drawable mNotificationPanelBackground;
- private ConnectedDeviceSignalController mConnectedDeviceSignalController;
private ViewGroup mNavigationBarWindow;
private ViewGroup mLeftNavigationBarWindow;
private ViewGroup mRightNavigationBarWindow;
@@ -90,6 +100,17 @@ public class CarStatusBar extends StatusBar implements
private DrivingStateHelper mDrivingStateHelper;
private SwitchToGuestTimer mSwitchToGuestTimer;
+ // The container for the notifications.
+ private CarNotificationView mNotificationView;
+ private RecyclerView mNotificationList;
+ // The state of if the notification list is currently showing the bottom.
+ private boolean mNotificationListAtBottom;
+ // Was the notification list at the bottom when the user first touched the screen
+ private boolean mNotificationListAtBottomAtTimeOfTouch;
+ // To be attached to the navigation bars such that they can close the notification panel if
+ // it's open.
+ private View.OnTouchListener mNavBarNotificationTouchListener;
+
@Override
public void start() {
// get the provisioned state before calling the parent class since it's that flow that
@@ -223,7 +244,6 @@ public class CarStatusBar extends StatusBar implements
* Switch to the keyguard applicable content contained in the nav bars
*/
private void updateNavBarForKeyguardContent() {
- getComponent(NotificationsUI.class).closeCarNotifications(0);
if (mNavigationBarView != null) {
mNavigationBarView.showKeyguardButtons();
}
@@ -255,17 +275,150 @@ public class CarStatusBar extends StatusBar implements
// when a device has connected by bluetooth.
mBatteryMeterView.setVisibility(View.GONE);
});
- addTemperatureViewToController(mStatusBarWindow);
+
+ connectNotificationsUI();
+ }
+
+ /**
+ * Attach the notification listeners and controllers to the UI as well as build all the
+ * touch listeners needed for opening and closing the notification panel
+ */
+ private void connectNotificationsUI() {
+ // Attached to the status bar to detect pull down of the notification shade.
+ GestureDetector openGestureDetector = new GestureDetector(mContext,
+ new OpenNotificationGestureListener() {
+ @Override
+ protected void openNotification() {
+ animateExpandNotificationsPanel();
+ }
+ });
+ // Attached to the notification ui to detect close request of the notification shade.
+ GestureDetector closeGestureDetector = new GestureDetector(mContext,
+ new CloseNotificationGestureListener() {
+ @Override
+ protected void close() {
+ animateCollapsePanels();
+ }
+ });
+ // Attached to the NavBars to close the notification shade
+ GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext,
+ new NavBarCloseNotificationGestureListener() {
+ @Override
+ protected void close() {
+ animateCollapsePanels();
+ }
+ });
+ mNavBarNotificationTouchListener =
+ (v, event) -> navBarCloseNotificationGestureDetector.onTouchEvent(event);
+
// The following are the ui elements that the user would call the status bar.
// This will set the status bar so it they can make call backs.
CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
topBar.setStatusBar(this);
- CarNavigationBarView qsTopBar = mStatusBarWindow.findViewById(R.id.qs_car_top_bar);
- qsTopBar.setStatusBar(this);
- getComponent(NotificationsUI.class).setStatusBar(this);
+ topBar.setStatusBarWindowTouchListener((v1, event1) ->
+ openGestureDetector.onTouchEvent(event1));
+
+ NotificationClickHandlerFactory clickHandlerFactory = new NotificationClickHandlerFactory(
+ mBarService,
+ launchResult -> {
+ if (launchResult == ActivityManager.START_TASK_TO_FRONT
+ || launchResult == ActivityManager.START_SUCCESS) {
+ animateCollapsePanels();
+ }
+ });
+ CarNotificationListener carNotificationListener = new CarNotificationListener();
+ CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
+ new CarUxRestrictionManagerWrapper();
+ carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper,
+ clickHandlerFactory);
+
+ mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
+ View glassPane = mStatusBarWindow.findViewById(R.id.glass_pane);
+ mNotificationView.setClickHandlerFactory(clickHandlerFactory);
+
+ // The glass pane is used to view touch events before passed to the notification list.
+ // This allows us to initialize gesture listeners and detect when to close the notifications
+ glassPane.setOnTouchListener((v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
+ // Pass the down event to gesture detector so that it knows where the touch event
+ // started.
+ closeGestureDetector.onTouchEvent(event);
+ }
+ return false;
+ });
+ mNotificationList = mNotificationView.findViewById(R.id.notifications);
+ mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+ if (!mNotificationList.canScrollVertically(1)) {
+ mNotificationListAtBottom = true;
+ return;
+ }
+ mNotificationListAtBottom = false;
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+ });
+ mNotificationList.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ boolean handled = false;
+ if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
+ handled = closeGestureDetector.onTouchEvent(event);
+ }
+ // Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after
+ // the event has been passed to the closeGestureDetector above, such that the
+ // closeGestureDetector sees the up event before the state has changed.
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+ return handled;
+ }
+ });
+
+ NotificationViewController mNotificationViewController = new NotificationViewController(
+ mNotificationView,
+ PreprocessingManager.getInstance(mContext),
+ carNotificationListener,
+ carUxRestrictionManagerWrapper);
+ mNotificationViewController.enable();
}
@Override
+ public void animateExpandNotificationsPanel() {
+ if (!mCommandQueue.panelsEnabled() || !mUserSetup) {
+ return;
+ }
+ // scroll to top
+ mNotificationList.scrollToPosition(0);
+ mStatusBarWindowController.setPanelVisible(true);
+ mNotificationView.setVisibility(View.VISIBLE);
+ // let the status bar know that the panel is open
+ setPanelExpanded(true);
+ }
+
+ @Override
+ public void animateCollapsePanels(int flags, boolean force, boolean delayed,
+ float speedUpFactor) {
+ super.animateCollapsePanels(flags, force, delayed, speedUpFactor);
+ if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) {
+ return;
+ }
+ mStatusBarWindowController.setStatusBarFocusable(false);
+ mStatusBarWindow.cancelExpandHelper();
+ mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
+ mStatusBarWindowController.setPanelVisible(false);
+ mNotificationView.setVisibility(View.INVISIBLE);
+ // let the status bar know that the panel is cloased
+ setPanelExpanded(false);
+ }
+
+
+ @Override
protected QS createDefaultQSFragment() {
return new CarQSFragment();
}
@@ -338,8 +491,8 @@ public class CarStatusBar extends StatusBar implements
lp.setTitle("CarNavigationBar");
lp.windowAnimations = 0;
mWindowManager.addView(mNavigationBarWindow, lp);
- mNavigationBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
}
+
if (mShowLeft) {
int width = mContext.getResources().getDimensionPixelSize(
R.dimen.car_left_navigation_bar_width);
@@ -389,6 +542,7 @@ public class CarStatusBar extends StatusBar implements
}
mNavigationBarView.setStatusBar(this);
addTemperatureViewToController(mNavigationBarView);
+ mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
}
private void buildLeft(int layout) {
@@ -400,6 +554,7 @@ public class CarStatusBar extends StatusBar implements
}
mLeftNavigationBarView.setStatusBar(this);
addTemperatureViewToController(mLeftNavigationBarView);
+ mLeftNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
}
@@ -412,6 +567,7 @@ public class CarStatusBar extends StatusBar implements
}
mRightNavigationBarView.setStatusBar(this);
addTemperatureViewToController(mRightNavigationBarView);
+ mRightNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
}
@Override
@@ -435,8 +591,6 @@ public class CarStatusBar extends StatusBar implements
pw.println(mCarBatteryController);
pw.print(" mBatteryMeterView=");
pw.println(mBatteryMeterView);
- pw.print(" mConnectedDeviceSignalController=");
- pw.println(mConnectedDeviceSignalController);
pw.print(" mNavigationBarView=");
pw.println(mNavigationBarView);
@@ -456,11 +610,6 @@ public class CarStatusBar extends StatusBar implements
}
}
- @Override
- protected View.OnTouchListener getStatusBarWindowTouchListener() {
- // Gets the car specific notification touch listener
- return getComponent(NotificationsUI.class).getDragDownListener();
- }
@Override
public void showBatteryView() {
@@ -585,15 +734,6 @@ public class CarStatusBar extends StatusBar implements
true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
}
- @Override
- public void animateExpandNotificationsPanel() {
- // Because space is usually constrained in the auto use-case, there should not be a
- // pinned notification when the shade has been expanded. Ensure this by removing all heads-
- // up notifications.
- mHeadsUpManager.releaseAllImmediately();
- super.animateExpandNotificationsPanel();
- }
-
/**
* Ensures that relevant child views are appropriately recreated when the device's density
* changes.
@@ -620,12 +760,73 @@ public class CarStatusBar extends StatusBar implements
return mUserSetup;
}
- public void toggleCarNotifications() {
- getComponent(NotificationsUI.class).toggleShowingCarNotifications();
+
+ // TODO: add settle down/up logic
+ private static final int SWIPE_UP_MIN_DISTANCE = 75;
+ private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
+ private static final int SWIPE_MAX_OFF_PATH = 75;
+ private static final int SWIPE_THRESHOLD_VELOCITY = 200;
+ // Only responsible for open hooks. Since once the panel opens it covers all elements
+ // there is no need to merge with close.
+ private abstract class OpenNotificationGestureListener extends
+ GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
+ || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
+ // swipe was not vertical or was not fast enough
+ return false;
+ }
+ boolean isDown = velocityY > 0;
+ float distanceDelta = Math.abs(event1.getY() - event2.getY());
+ if (isDown && distanceDelta > SWIPE_DOWN_MIN_DISTANCE) {
+ openNotification();
+ return true;
+ }
+
+ return false;
+ }
+ protected abstract void openNotification();
}
- @Override
- public void maybeEscalateHeadsUp() {
- // Never send full screen intent in car.
+ // to be installed on the open panel notification panel
+ private abstract class CloseNotificationGestureListener extends
+ GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
+ || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
+ // swipe was not vertical or was not fast enough
+ return false;
+ }
+ boolean isUp = velocityY < 0;
+ float distanceDelta = Math.abs(event1.getY() - event2.getY());
+ if (isUp && distanceDelta > SWIPE_UP_MIN_DISTANCE) {
+ close();
+ return true;
+ }
+ return false;
+ }
+ protected abstract void close();
+ }
+
+ // to be installed on the nav bars
+ private abstract class NavBarCloseNotificationGestureListener extends
+ CloseNotificationGestureListener {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ close();
+ return super.onSingleTapUp(e);
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ close();
+ super.onLongPress(e);
+ }
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index f896cf1bf10c..0a167d9acf98 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -38,7 +38,9 @@ public class FullscreenUserSwitcher {
public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
mStatusBar = statusBar;
mParent = containerStub.inflate();
- mParent.setVisibility(View.VISIBLE);
+ // Hide the user grid by default. It will only be made visible by clicking on a cancel
+ // button in a bouncer.
+ hide();
View container = mParent.findViewById(R.id.container);
// Initialize user grid.
@@ -49,10 +51,6 @@ public class FullscreenUserSwitcher {
mUserGridView.buildAdapter();
mUserGridView.setUserSelectionListener(this::onUserSelected);
- // Hide the user grid by default. It will only be made visible by clicking on a cancel
- // button in a bouncer.
- hide();
-
mShortAnimDuration = container.getResources()
.getInteger(android.R.integer.config_shortAnimTime);
}
@@ -61,21 +59,21 @@ public class FullscreenUserSwitcher {
* Makes user grid visible.
*/
public void show() {
- mUserGridView.setVisibility(View.VISIBLE);
+ mParent.setVisibility(View.VISIBLE);
}
/**
* Hides the user grid.
*/
public void hide() {
- mUserGridView.setVisibility(View.INVISIBLE);
+ mParent.setVisibility(View.INVISIBLE);
}
/**
* @return {@code true} if user grid is visible, {@code false} otherwise.
*/
public boolean isVisible() {
- return mUserGridView.getVisibility() == View.VISIBLE;
+ return mParent.getVisibility() == View.VISIBLE;
}
/**
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index d40975801ff8..45a59a32ec17 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="android.ext.services"
- android:versionCode="1"
+ android:versionCode="200000000"
android:versionName="1"
coreApp="true">
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 9b60dc3e952a..b4588e01dc79 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -17,10 +17,18 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.networkstack"
- android:sharedUserId="android.uid.networkstack">
+ package="com.android.networkstack"
+ android:sharedUserId="android.uid.networkstack"
+ android:versionCode="200000000"
+ android:versionName="29 system image"
+>
+
<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+ <!-- Permissions must be defined here, and not in the base manifest, as the network stack
+ running in the system server process does not need any permission, and having privileged
+ permissions added would cause crashes on startup unless they are also added to the
+ privileged permissions whitelist for that package. -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -30,7 +38,7 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
-
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<!-- Signature permission defined in NetworkStackStub -->
<uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
<application>
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
index 7a06af41f951..c1f178a7f5f1 100644
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ b/packages/NetworkStack/src/android/net/ip/IpClient.java
@@ -1389,8 +1389,8 @@ public class IpClient extends StateMachine {
apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
apfConfig.multicastFilter = mMulticastFiltering;
// Get the Configuration for ApfFilter from Context
- apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames(mContext);
- apfConfig.ethTypeBlackList = ApfCapabilities.getApfEthTypeBlackList(mContext);
+ apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames();
+ apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList();
mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
// TODO: investigate the effects of any multicast filtering racing/interfering with the
// rest of this IP configuration startup.
diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
index fedb8d110197..670563cf391e 100644
--- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
+++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
@@ -17,6 +17,7 @@
package android.net.util;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.util.SparseArray;
import java.io.FileDescriptor;
@@ -81,4 +82,19 @@ public class NetworkStackUtils {
}
return false;
}
+
+ /**
+ * Look up the value of a property for a particular namespace from {@link DeviceConfig}.
+ * @param namespace The namespace containing the property to look up.
+ * @param name The name of the property to look up.
+ * @param defaultValue The value to return if the property does not exist or has no non-null
+ * value.
+ * @return the corresponding value, or defaultValue if none exists.
+ */
+ @Nullable
+ public static String getDeviceConfigProperty(@NonNull String namespace, @NonNull String name,
+ @Nullable String defaultValue) {
+ // TODO: Link to DeviceConfig API once it is ready.
+ return defaultValue;
+ }
}
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index 19e9108d2fc8..63f057caa26e 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -35,7 +35,9 @@ import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkStackConnector;
+import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.PrivateDnsConfigParcel;
import android.net.dhcp.DhcpServer;
import android.net.dhcp.DhcpServingParams;
@@ -307,9 +309,9 @@ public class NetworkStackService extends Service {
}
@Override
- public void notifyNetworkConnected() {
+ public void notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) {
checkNetworkStackCallingPermission();
- mNm.notifyNetworkConnected();
+ mNm.notifyNetworkConnected(lp, nc);
}
@Override
@@ -319,15 +321,15 @@ public class NetworkStackService extends Service {
}
@Override
- public void notifyLinkPropertiesChanged() {
+ public void notifyLinkPropertiesChanged(LinkProperties lp) {
checkNetworkStackCallingPermission();
- mNm.notifyLinkPropertiesChanged();
+ mNm.notifyLinkPropertiesChanged(lp);
}
@Override
- public void notifyNetworkCapabilitiesChanged() {
+ public void notifyNetworkCapabilitiesChanged(NetworkCapabilities nc) {
checkNetworkStackCallingPermission();
- mNm.notifyNetworkCapabilitiesChanged();
+ mNm.notifyNetworkCapabilitiesChanged(nc);
}
}
}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index f3476ed1566f..c000fc6b721d 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -78,6 +78,7 @@ import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.RingBufferIndices;
@@ -221,19 +222,31 @@ public class NetworkMonitor extends StateMachine {
* Message to self indicating captive portal detection is completed.
* obj = CaptivePortalProbeResult for detection result;
*/
- public static final int CMD_PROBE_COMPLETE = 16;
+ private static final int CMD_PROBE_COMPLETE = 16;
/**
* ConnectivityService notifies NetworkMonitor of DNS query responses event.
* arg1 = returncode in OnDnsEvent which indicates the response code for the DNS query.
*/
- public static final int EVENT_DNS_NOTIFICATION = 17;
+ private static final int EVENT_DNS_NOTIFICATION = 17;
/**
* ConnectivityService notifies NetworkMonitor that the user accepts partial connectivity and
* NetworkMonitor should ignore the https probe.
*/
- public static final int EVENT_ACCEPT_PARTIAL_CONNECTIVITY = 18;
+ private static final int EVENT_ACCEPT_PARTIAL_CONNECTIVITY = 18;
+
+ /**
+ * ConnectivityService notifies NetworkMonitor of changed LinkProperties.
+ * obj = new LinkProperties.
+ */
+ private static final int EVENT_LINK_PROPERTIES_CHANGED = 19;
+
+ /**
+ * ConnectivityService notifies NetworkMonitor of changed NetworkCapabilities.
+ * obj = new NetworkCapabilities.
+ */
+ private static final int EVENT_NETWORK_CAPABILITIES_CHANGED = 20;
// Start mReevaluateDelayMs at this value and double.
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
@@ -379,10 +392,8 @@ public class NetworkMonitor extends StateMachine {
mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold();
mDataStallEvaluationType = getDataStallEvalutionType();
- // mLinkProperties and mNetworkCapbilities must never be null or we will NPE.
- // Provide empty objects in case we are started and the network disconnects before
- // we can ever fetch them.
- // TODO: Delete ASAP.
+ // Provide empty LinkProperties and NetworkCapabilities to make sure they are never null,
+ // even before notifyNetworkConnected.
mLinkProperties = new LinkProperties();
mNetworkCapabilities = new NetworkCapabilities(null);
}
@@ -434,8 +445,16 @@ public class NetworkMonitor extends StateMachine {
/**
* Send a notification to NetworkMonitor indicating that the network is now connected.
*/
- public void notifyNetworkConnected() {
- sendMessage(CMD_NETWORK_CONNECTED);
+ public void notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) {
+ sendMessage(CMD_NETWORK_CONNECTED, new Pair<>(
+ new LinkProperties(lp), new NetworkCapabilities(nc)));
+ }
+
+ private void updateConnectedNetworkAttributes(Message connectedMsg) {
+ final Pair<LinkProperties, NetworkCapabilities> attrs =
+ (Pair<LinkProperties, NetworkCapabilities>) connectedMsg.obj;
+ mLinkProperties = attrs.first;
+ mNetworkCapabilities = attrs.second;
}
/**
@@ -448,37 +467,15 @@ public class NetworkMonitor extends StateMachine {
/**
* Send a notification to NetworkMonitor indicating that link properties have changed.
*/
- public void notifyLinkPropertiesChanged() {
- getHandler().post(() -> {
- updateLinkProperties();
- });
- }
-
- private void updateLinkProperties() {
- final LinkProperties lp = mCm.getLinkProperties(mNetwork);
- // If null, we should soon get a message that the network was disconnected, and will stop.
- if (lp != null) {
- // TODO: send LinkProperties parceled in notifyLinkPropertiesChanged() and start().
- mLinkProperties = lp;
- }
+ public void notifyLinkPropertiesChanged(final LinkProperties lp) {
+ sendMessage(EVENT_LINK_PROPERTIES_CHANGED, new LinkProperties(lp));
}
/**
* Send a notification to NetworkMonitor indicating that network capabilities have changed.
*/
- public void notifyNetworkCapabilitiesChanged() {
- getHandler().post(() -> {
- updateNetworkCapabilities();
- });
- }
-
- private void updateNetworkCapabilities() {
- final NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork);
- // If null, we should soon get a message that the network was disconnected, and will stop.
- if (nc != null) {
- // TODO: send NetworkCapabilities parceled in notifyNetworkCapsChanged() and start().
- mNetworkCapabilities = nc;
- }
+ public void notifyNetworkCapabilitiesChanged(final NetworkCapabilities nc) {
+ sendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, new NetworkCapabilities(nc));
}
/**
@@ -547,16 +544,10 @@ public class NetworkMonitor extends StateMachine {
// does not entail any real state (hence no enter() or exit() routines).
private class DefaultState extends State {
@Override
- public void enter() {
- // TODO: have those passed parceled in start() and remove this
- updateLinkProperties();
- updateNetworkCapabilities();
- }
-
- @Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_NETWORK_CONNECTED:
+ updateConnectedNetworkAttributes(message);
logNetworkEvent(NetworkEvent.NETWORK_CONNECTED);
transitionTo(mEvaluatingState);
return HANDLED;
@@ -660,6 +651,12 @@ public class NetworkMonitor extends StateMachine {
case EVENT_ACCEPT_PARTIAL_CONNECTIVITY:
mAcceptPartialConnectivity = true;
break;
+ case EVENT_LINK_PROPERTIES_CHANGED:
+ mLinkProperties = (LinkProperties) message.obj;
+ break;
+ case EVENT_NETWORK_CAPABILITIES_CHANGED:
+ mNetworkCapabilities = (NetworkCapabilities) message.obj;
+ break;
default:
break;
}
@@ -684,6 +681,7 @@ public class NetworkMonitor extends StateMachine {
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_NETWORK_CONNECTED:
+ updateConnectedNetworkAttributes(message);
transitionTo(mValidatedState);
break;
case CMD_EVALUATE_PRIVATE_DNS:
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
index 4d4ceed9cb52..b4eeefd5ecae 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
@@ -72,6 +72,10 @@ public class IpMemoryStoreDatabase {
public static final String COLNAME_ASSIGNEDV4ADDRESS = "assignedV4Address";
public static final String COLTYPE_ASSIGNEDV4ADDRESS = "INTEGER";
+ public static final String COLNAME_ASSIGNEDV4ADDRESSEXPIRY = "assignedV4AddressExpiry";
+ // The lease expiry timestamp in uint of milliseconds
+ public static final String COLTYPE_ASSIGNEDV4ADDRESSEXPIRY = "BIGINT";
+
// Please note that the group hint is only a *hint*, hence its name. The client can offer
// this information to nudge the grouping in the decision it thinks is right, but it can't
// decide for the memory store what is the same L3 network.
@@ -86,13 +90,14 @@ public class IpMemoryStoreDatabase {
public static final String COLTYPE_MTU = "INTEGER DEFAULT -1";
public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS "
- + TABLENAME + " ("
- + COLNAME_L2KEY + " " + COLTYPE_L2KEY + " PRIMARY KEY NOT NULL, "
- + COLNAME_EXPIRYDATE + " " + COLTYPE_EXPIRYDATE + ", "
- + COLNAME_ASSIGNEDV4ADDRESS + " " + COLTYPE_ASSIGNEDV4ADDRESS + ", "
- + COLNAME_GROUPHINT + " " + COLTYPE_GROUPHINT + ", "
- + COLNAME_DNSADDRESSES + " " + COLTYPE_DNSADDRESSES + ", "
- + COLNAME_MTU + " " + COLTYPE_MTU + ")";
+ + TABLENAME + " ("
+ + COLNAME_L2KEY + " " + COLTYPE_L2KEY + " PRIMARY KEY NOT NULL, "
+ + COLNAME_EXPIRYDATE + " " + COLTYPE_EXPIRYDATE + ", "
+ + COLNAME_ASSIGNEDV4ADDRESS + " " + COLTYPE_ASSIGNEDV4ADDRESS + ", "
+ + COLNAME_ASSIGNEDV4ADDRESSEXPIRY + " " + COLTYPE_ASSIGNEDV4ADDRESSEXPIRY + ", "
+ + COLNAME_GROUPHINT + " " + COLTYPE_GROUPHINT + ", "
+ + COLNAME_DNSADDRESSES + " " + COLTYPE_DNSADDRESSES + ", "
+ + COLNAME_MTU + " " + COLTYPE_MTU + ")";
public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME;
}
@@ -134,7 +139,7 @@ public class IpMemoryStoreDatabase {
/** The SQLite DB helper */
public static class DbHelper extends SQLiteOpenHelper {
// Update this whenever changing the schema.
- private static final int SCHEMA_VERSION = 2;
+ private static final int SCHEMA_VERSION = 3;
private static final String DATABASE_FILENAME = "IpMemoryStore.db";
public DbHelper(@NonNull final Context context) {
@@ -153,10 +158,27 @@ public class IpMemoryStoreDatabase {
@Override
public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion,
final int newVersion) {
- // No upgrade supported yet.
- db.execSQL(NetworkAttributesContract.DROP_TABLE);
- db.execSQL(PrivateDataContract.DROP_TABLE);
- onCreate(db);
+ try {
+ if (oldVersion < 2) {
+ // upgrade from version 1 to version 2
+ // since we starts from version 2, do nothing here
+ }
+
+ if (oldVersion < 3) {
+ // upgrade from version 2 to version 3
+ final String sqlUpgradeAddressExpiry = "alter table"
+ + " " + NetworkAttributesContract.TABLENAME + " ADD"
+ + " " + NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESSEXPIRY
+ + " " + NetworkAttributesContract.COLTYPE_ASSIGNEDV4ADDRESSEXPIRY;
+ db.execSQL(sqlUpgradeAddressExpiry);
+ }
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Could not upgrade to the new version", e);
+ // create database with new version
+ db.execSQL(NetworkAttributesContract.DROP_TABLE);
+ db.execSQL(PrivateDataContract.DROP_TABLE);
+ onCreate(db);
+ }
}
/** Called when the database is downgraded */
@@ -204,6 +226,10 @@ public class IpMemoryStoreDatabase {
values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS,
inet4AddressToIntHTH(attributes.assignedV4Address));
}
+ if (null != attributes.assignedV4AddressExpiry) {
+ values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESSEXPIRY,
+ attributes.assignedV4AddressExpiry);
+ }
if (null != attributes.groupHint) {
values.put(NetworkAttributesContract.COLNAME_GROUPHINT, attributes.groupHint);
}
@@ -251,6 +277,8 @@ public class IpMemoryStoreDatabase {
final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
final int assignedV4AddressInt = getInt(cursor,
NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS, 0);
+ final long assignedV4AddressExpiry = getLong(cursor,
+ NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESSEXPIRY, 0);
final String groupHint = getString(cursor, NetworkAttributesContract.COLNAME_GROUPHINT);
final byte[] dnsAddressesBlob =
getBlob(cursor, NetworkAttributesContract.COLNAME_DNSADDRESSES);
@@ -258,6 +286,9 @@ public class IpMemoryStoreDatabase {
if (0 != assignedV4AddressInt) {
builder.setAssignedV4Address(intToInet4AddressHTH(assignedV4AddressInt));
}
+ if (0 != assignedV4AddressExpiry) {
+ builder.setAssignedV4AddressExpiry(assignedV4AddressExpiry);
+ }
builder.setGroupHint(groupHint);
if (null != dnsAddressesBlob) {
builder.setDnsAddresses(decodeAddressList(dnsAddressesBlob));
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index d732c4e81d83..6665aae65d90 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -26,6 +26,8 @@ import static android.provider.Settings.Global.DATA_STALL_EVALUATION_TYPE_DNS;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
@@ -60,6 +62,7 @@ import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.Settings;
import android.telephony.CellSignalStrength;
@@ -73,6 +76,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@@ -107,6 +111,7 @@ public class NetworkMonitorTest {
private @Spy Network mNetwork = new Network(TEST_NETID);
private @Mock DataStallStatsUtils mDataStallStatsUtils;
private @Mock WifiInfo mWifiInfo;
+ private @Captor ArgumentCaptor<String> mNetworkTestedRedirectUrlCaptor;
private static final int TEST_NETID = 4242;
@@ -122,7 +127,7 @@ public class NetworkMonitorTest {
private static final int HANDLER_TIMEOUT_MS = 1000;
- private static final LinkProperties TEST_LINKPROPERTIES = new LinkProperties();
+ private static final LinkProperties TEST_LINK_PROPERTIES = new LinkProperties();
private static final NetworkCapabilities METERED_CAPABILITIES = new NetworkCapabilities()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
@@ -182,10 +187,6 @@ public class NetworkMonitorTest {
InetAddresses.parseNumericAddress("192.168.0.0")
}).when(mNetwork).getAllByName(any());
- // Default values. Individual tests can override these.
- when(mCm.getLinkProperties(any())).thenReturn(TEST_LINKPROPERTIES);
- when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
-
setMinDataStallEvaluateInterval(500);
setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
setValidDataStallDnsTimeThreshold(500);
@@ -195,10 +196,9 @@ public class NetworkMonitorTest {
private class WrappedNetworkMonitor extends NetworkMonitor {
private long mProbeTime = 0;
- WrappedNetworkMonitor(Context context, Network network, IpConnectivityLog logger,
- Dependencies deps, DataStallStatsUtils statsUtils) {
- super(context, mCallbacks, network, logger,
- new SharedLog("test_nm"), deps, statsUtils);
+ WrappedNetworkMonitor() {
+ super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger, mDependencies,
+ mDataStallStatsUtils);
}
@Override
@@ -216,33 +216,30 @@ public class NetworkMonitorTest {
}
}
- private WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
- final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
- mContext, mNetwork, mLogger, mDependencies, mDataStallStatsUtils);
- when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
+ private WrappedNetworkMonitor makeMonitor() {
+ final WrappedNetworkMonitor nm = new WrappedNetworkMonitor();
nm.start();
waitForIdle(nm.getHandler());
return nm;
}
- private WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
- final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(
- mContext, mNetwork, mLogger, mDependencies, mDataStallStatsUtils);
- when(mCm.getNetworkCapabilities(any())).thenReturn(NOT_METERED_CAPABILITIES);
- nm.start();
- waitForIdle(nm.getHandler());
+ private WrappedNetworkMonitor makeMeteredNetworkMonitor() {
+ final WrappedNetworkMonitor nm = makeMonitor();
+ setNetworkCapabilities(nm, METERED_CAPABILITIES);
return nm;
}
- private NetworkMonitor makeMonitor() {
- final NetworkMonitor nm = new NetworkMonitor(
- mContext, mCallbacks, mNetwork, mLogger, mValidationLogger,
- mDependencies, mDataStallStatsUtils);
- nm.start();
- waitForIdle(nm.getHandler());
+ private WrappedNetworkMonitor makeNotMeteredNetworkMonitor() {
+ final WrappedNetworkMonitor nm = makeMonitor();
+ setNetworkCapabilities(nm, NOT_METERED_CAPABILITIES);
return nm;
}
+ private void setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc) {
+ nm.notifyNetworkCapabilitiesChanged(nc);
+ waitForIdle(nm.getHandler());
+ }
+
private void waitForIdle(Handler handler) {
final ConditionVariable cv = new ConditionVariable(false);
handler.post(cv::open);
@@ -256,7 +253,7 @@ public class NetworkMonitorTest {
setSslException(mHttpsConnection);
setPortal302(mHttpConnection);
- assertPortal(makeMonitor().isCaptivePortal());
+ runPortalNetworkTest();
}
@Test
@@ -264,17 +261,7 @@ public class NetworkMonitorTest {
setStatus(mHttpsConnection, 204);
setStatus(mHttpConnection, 500);
- assertNotPortal(makeMonitor().isCaptivePortal());
- }
-
- @Test
- public void testIsCaptivePortal_HttpsProbeFailedHttpSuccessNotUsed() throws IOException {
- setSslException(mHttpsConnection);
- // Even if HTTP returns a 204, do not use the result unless HTTPS succeeded
- setStatus(mHttpConnection, 204);
- setStatus(mFallbackConnection, 500);
-
- assertFailed(makeMonitor().isCaptivePortal());
+ runNotPortalNetworkTest();
}
@Test
@@ -283,17 +270,17 @@ public class NetworkMonitorTest {
setStatus(mHttpConnection, 500);
setPortal302(mFallbackConnection);
- assertPortal(makeMonitor().isCaptivePortal());
+ runPortalNetworkTest();
}
@Test
public void testIsCaptivePortal_FallbackProbeIsNotPortal() throws IOException {
setSslException(mHttpsConnection);
setStatus(mHttpConnection, 500);
- setStatus(mFallbackConnection, 204);
+ setStatus(mFallbackConnection, 500);
// Fallback probe did not see portal, HTTPS failed -> inconclusive
- assertFailed(makeMonitor().isCaptivePortal());
+ runFailedNetworkTest();
}
@Test
@@ -310,15 +297,15 @@ public class NetworkMonitorTest {
// TEST_OTHER_FALLBACK_URL is third
when(mRandom.nextInt()).thenReturn(2);
- final NetworkMonitor monitor = makeMonitor();
-
// First check always uses the first fallback URL: inconclusive
- assertFailed(monitor.isCaptivePortal());
+ final NetworkMonitor monitor = runNetworkTest(NETWORK_TEST_RESULT_INVALID);
+ assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
verify(mFallbackConnection, times(1)).getResponseCode();
verify(mOtherFallbackConnection, never()).getResponseCode();
// Second check uses the URL chosen by Random
- assertPortal(monitor.isCaptivePortal());
+ final CaptivePortalProbeResult result = monitor.isCaptivePortal();
+ assertTrue(result.isPortal());
verify(mOtherFallbackConnection, times(1)).getResponseCode();
}
@@ -328,7 +315,7 @@ public class NetworkMonitorTest {
setStatus(mHttpConnection, 500);
setStatus(mFallbackConnection, 404);
- assertFailed(makeMonitor().isCaptivePortal());
+ runFailedNetworkTest();
verify(mFallbackConnection, times(1)).getResponseCode();
verify(mOtherFallbackConnection, never()).getResponseCode();
}
@@ -342,7 +329,7 @@ public class NetworkMonitorTest {
setStatus(mHttpConnection, 500);
setPortal302(mOtherFallbackConnection);
- assertPortal(makeMonitor().isCaptivePortal());
+ runPortalNetworkTest();
verify(mOtherFallbackConnection, times(1)).getResponseCode();
verify(mFallbackConnection, never()).getResponseCode();
}
@@ -360,12 +347,12 @@ public class NetworkMonitorTest {
}
@Test
- public void testIsCaptivePortal_FallbackSpecIsNotPortal() throws IOException {
+ public void testIsCaptivePortal_FallbackSpecIsPartial() throws IOException {
setupFallbackSpec();
set302(mOtherFallbackConnection, "https://www.google.com/test?q=3");
- // HTTPS failed, fallback spec did not see a portal -> inconclusive
- assertFailed(makeMonitor().isCaptivePortal());
+ // HTTPS failed, fallback spec went through -> partial connectivity
+ runPartialConnectivityNetworkTest();
verify(mOtherFallbackConnection, times(1)).getResponseCode();
verify(mFallbackConnection, never()).getResponseCode();
}
@@ -375,7 +362,7 @@ public class NetworkMonitorTest {
setupFallbackSpec();
set302(mOtherFallbackConnection, "http://login.portal.example.com");
- assertPortal(makeMonitor().isCaptivePortal());
+ runPortalNetworkTest();
}
@Test
@@ -384,20 +371,20 @@ public class NetworkMonitorTest {
setSslException(mHttpsConnection);
setPortal302(mHttpConnection);
- assertNotPortal(makeMonitor().isCaptivePortal());
+ runNotPortalNetworkTest();
}
@Test
public void testIsDataStall_EvaluationDisabled() {
setDataStallEvaluationType(0);
- WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
assertFalse(wrappedMonitor.isDataStall());
}
@Test
public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() {
- WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
assertTrue(wrappedMonitor.isDataStall());
@@ -405,7 +392,7 @@ public class NetworkMonitorTest {
@Test
public void testIsDataStall_EvaluationDnsOnMeteredNetwork() {
- WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
assertFalse(wrappedMonitor.isDataStall());
@@ -416,7 +403,7 @@ public class NetworkMonitorTest {
@Test
public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() {
- WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
makeDnsTimeoutEvent(wrappedMonitor, 3);
assertFalse(wrappedMonitor.isDataStall());
@@ -430,7 +417,7 @@ public class NetworkMonitorTest {
// Set the value to larger than the default dns log size.
setConsecutiveDnsTimeoutThreshold(51);
- wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor = makeMeteredNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
makeDnsTimeoutEvent(wrappedMonitor, 50);
assertFalse(wrappedMonitor.isDataStall());
@@ -442,7 +429,7 @@ public class NetworkMonitorTest {
@Test
public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() {
// Test dns events happened in valid dns time threshold.
- WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
assertFalse(wrappedMonitor.isDataStall());
@@ -451,7 +438,7 @@ public class NetworkMonitorTest {
// Test dns events happened before valid dns time threshold.
setValidDataStallDnsTimeThreshold(0);
- wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+ wrappedMonitor = makeMeteredNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
assertFalse(wrappedMonitor.isDataStall());
@@ -464,24 +451,13 @@ public class NetworkMonitorTest {
setSslException(mHttpsConnection);
setStatus(mHttpConnection, 500);
setStatus(mFallbackConnection, 404);
- when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES);
- final NetworkMonitor nm = makeMonitor();
- nm.notifyNetworkConnected();
-
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
- .notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null);
+ runFailedNetworkTest();
}
@Test
public void testNoInternetCapabilityValidated() throws Exception {
- when(mCm.getNetworkCapabilities(any())).thenReturn(NO_INTERNET_CAPABILITIES);
-
- final NetworkMonitor nm = makeMonitor();
- nm.notifyNetworkConnected();
-
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
- .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
+ runNetworkTest(NO_INTERNET_CAPABILITIES, NETWORK_TEST_RESULT_VALID);
verify(mNetwork, never()).openConnection(any());
}
@@ -491,7 +467,7 @@ public class NetworkMonitorTest {
setPortal302(mHttpConnection);
final NetworkMonitor nm = makeMonitor();
- nm.notifyNetworkConnected();
+ nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, METERED_CAPABILITIES);
verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
.showProvisioningNotification(any(), any());
@@ -522,7 +498,7 @@ public class NetworkMonitorTest {
@Test
public void testDataStall_StallSuspectedAndSendMetrics() throws IOException {
- WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
makeDnsTimeoutEvent(wrappedMonitor, 5);
assertTrue(wrappedMonitor.isDataStall());
@@ -531,7 +507,7 @@ public class NetworkMonitorTest {
@Test
public void testDataStall_NoStallSuspectedAndSendMetrics() throws IOException {
- WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
makeDnsTimeoutEvent(wrappedMonitor, 3);
assertFalse(wrappedMonitor.isDataStall());
@@ -540,7 +516,7 @@ public class NetworkMonitorTest {
@Test
public void testCollectDataStallMetrics() {
- WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+ WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC);
@@ -578,14 +554,11 @@ public class NetworkMonitorTest {
setSslException(mHttpsConnection);
setStatus(mHttpConnection, 204);
- final NetworkMonitor nm = makeMonitor();
- nm.notifyNetworkConnected();
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
- .notifyNetworkTested(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY, null);
+ final NetworkMonitor nm = runNetworkTest(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY);
nm.setAcceptPartialConnectivity();
verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
- .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
+ .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), any());
}
@Test
@@ -593,12 +566,12 @@ public class NetworkMonitorTest {
setStatus(mHttpsConnection, 500);
setStatus(mHttpConnection, 204);
setStatus(mFallbackConnection, 500);
- assertPartialConnectivity(makeMonitor().isCaptivePortal());
+ runPartialConnectivityNetworkTest();
setStatus(mHttpsConnection, 500);
setStatus(mHttpConnection, 500);
setStatus(mFallbackConnection, 204);
- assertPartialConnectivity(makeMonitor().isCaptivePortal());
+ runPartialConnectivityNetworkTest();
}
private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
@@ -660,26 +633,41 @@ public class NetworkMonitorTest {
eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode);
}
- private void assertPortal(CaptivePortalProbeResult result) {
- assertTrue(result.isPortal());
- assertFalse(result.isFailed());
- assertFalse(result.isSuccessful());
+ private void runPortalNetworkTest() {
+ runNetworkTest(NETWORK_TEST_RESULT_INVALID);
+ assertNotNull(mNetworkTestedRedirectUrlCaptor.getValue());
}
- private void assertNotPortal(CaptivePortalProbeResult result) {
- assertFalse(result.isPortal());
- assertFalse(result.isFailed());
- assertTrue(result.isSuccessful());
+ private void runNotPortalNetworkTest() {
+ runNetworkTest(NETWORK_TEST_RESULT_VALID);
+ assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
}
- private void assertFailed(CaptivePortalProbeResult result) {
- assertFalse(result.isPortal());
- assertTrue(result.isFailed());
- assertFalse(result.isSuccessful());
+ private void runFailedNetworkTest() {
+ runNetworkTest(NETWORK_TEST_RESULT_INVALID);
+ assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
}
- private void assertPartialConnectivity(CaptivePortalProbeResult result) {
- assertTrue(result.isPartialConnectivity());
+ private void runPartialConnectivityNetworkTest() {
+ runNetworkTest(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY);
+ assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
+ }
+
+ private NetworkMonitor runNetworkTest(int testResult) {
+ return runNetworkTest(METERED_CAPABILITIES, testResult);
+ }
+
+ private NetworkMonitor runNetworkTest(NetworkCapabilities nc, int testResult) {
+ final NetworkMonitor monitor = makeMonitor();
+ monitor.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc);
+ try {
+ verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+ .notifyNetworkTested(eq(testResult), mNetworkTestedRedirectUrlCaptor.capture());
+ } catch (RemoteException e) {
+ fail("Unexpected exception: " + e);
+ }
+
+ return monitor;
}
private void setSslException(HttpURLConnection connection) throws IOException {
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
index d0e58b817e9d..071ff2635152 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -228,6 +228,7 @@ public class IpMemoryStoreServiceTest {
public void testNetworkAttributes() throws UnknownHostException {
final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+ na.setAssignedV4AddressExpiry(System.currentTimeMillis() + 7_200_000);
na.setGroupHint("hint1");
na.setMtu(219);
final String l2Key = FAKE_KEYS[0];
@@ -257,6 +258,8 @@ public class IpMemoryStoreServiceTest {
+ status.resultCode, status.isSuccess());
assertEquals(l2Key, key);
assertEquals(attributes.assignedV4Address, attr.assignedV4Address);
+ assertEquals(attributes.assignedV4AddressExpiry,
+ attr.assignedV4AddressExpiry);
assertEquals(attributes.groupHint, attr.groupHint);
assertEquals(attributes.mtu, attr.mtu);
assertEquals(attributes2.dnsAddresses, attr.dnsAddresses);
@@ -278,7 +281,7 @@ public class IpMemoryStoreServiceTest {
// Verify that this test does not miss any new field added later.
// If any field is added to NetworkAttributes it must be tested here for storing
// and retrieving.
- assertEquals(4, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
+ assertEquals(5, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
.filter(f -> !Modifier.isStatic(f.getModifiers())).count());
}
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 62535b635e44..b0e2700a1f30 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -43,14 +43,12 @@
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.INSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.intent.action.INSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="file" />
<data android:scheme="package" />
<data android:scheme="content" />
</intent-filter>
diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp
new file mode 100644
index 000000000000..7f4442deecd6
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "SettingsLibAdaptiveIcon",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "SettingsLibTile"
+ ],
+
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml b/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml
new file mode 100644
index 000000000000..256b8f3ea477
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml b/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml
new file mode 100644
index 000000000000..76d106a067dd
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<resources>
+ <color name="homepage_generic_icon_background">#1A73E8</color>
+
+ <color name="bt_outline_color">#1f000000</color> <!-- icon outline color -->
+</resources>
diff --git a/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml b/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml
new file mode 100644
index 000000000000..7f5b58c48abb
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 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.
+ -->
+
+<resources>
+ <!-- Dashboard foreground image inset (from background edge to foreground edge) -->
+ <dimen name="dashboard_tile_foreground_image_inset">6dp</dimen>
+
+ <!-- Stroke size of adaptive outline -->
+ <dimen name="adaptive_outline_stroke">1dp</dimen>
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
new file mode 100644
index 000000000000..fc93650a6b85
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 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.settingslib.widget;
+
+import static androidx.annotation.VisibleForTesting.NONE;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settingslib.drawer.Tile;
+
+/**
+ * Adaptive icon that can set background color
+ */
+public class AdaptiveIcon extends LayerDrawable {
+
+ private static final String TAG = "AdaptiveHomepageIcon";
+
+ @VisibleForTesting(otherwise = NONE)
+ int mBackgroundColor = -1;
+ private AdaptiveConstantState mAdaptiveConstantState;
+
+ public AdaptiveIcon(Context context, Drawable foreground) {
+ super(new Drawable[]{
+ new AdaptiveIconShapeDrawable(context.getResources()),
+ foreground
+ });
+ final int insetPx = context.getResources()
+ .getDimensionPixelSize(R.dimen.dashboard_tile_foreground_image_inset);
+ setLayerInset(1 /* index */, insetPx, insetPx, insetPx, insetPx);
+ mAdaptiveConstantState = new AdaptiveConstantState(context, foreground);
+ }
+
+ /**
+ * According {@code tile} metaData to set background color
+ */
+ public void setBackgroundColor(Context context, Tile tile) {
+ final Bundle metaData = tile.getMetaData();
+ try {
+ if (metaData != null) {
+ // Load from bg.argb first
+ int bgColor = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB,
+ 0 /* default */);
+ // Not found, load from bg.hint
+ if (bgColor == 0) {
+ final int colorRes = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
+ 0 /* default */);
+ if (colorRes != 0) {
+ bgColor = context.getPackageManager()
+ .getResourcesForApplication(tile.getPackageName())
+ .getColor(colorRes, null /* theme */);
+ }
+ }
+ // If found anything, use it.
+ if (bgColor != 0) {
+ setBackgroundColor(bgColor);
+ return;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to set background color for " + tile.getPackageName());
+ }
+ setBackgroundColor(context.getColor(R.color.homepage_generic_icon_background));
+ }
+
+ /**
+ * Set background color by {@code color}
+ */
+ public void setBackgroundColor(int color) {
+ mBackgroundColor = color;
+ getDrawable(0).setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ Log.d(TAG, "Setting background color " + mBackgroundColor);
+ mAdaptiveConstantState.mColor = color;
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return mAdaptiveConstantState;
+ }
+
+ @VisibleForTesting
+ static class AdaptiveConstantState extends ConstantState {
+ Context mContext;
+ Drawable mDrawable;
+ int mColor;
+
+ AdaptiveConstantState(Context context, Drawable drawable) {
+ this.mContext = context;
+ this.mDrawable = drawable;
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ final AdaptiveIcon
+ icon = new AdaptiveIcon(mContext, mDrawable);
+ icon.setBackgroundColor(mColor);
+
+ return icon;
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 0;
+ }
+ }
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
new file mode 100644
index 000000000000..4d7610cf97b6
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 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.settingslib.widget;
+
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.PathShape;
+import android.util.AttributeSet;
+import android.util.PathParser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Draws a filled {@link ShapeDrawable} using the path from {@link AdaptiveIconDrawable}.
+ */
+public class AdaptiveIconShapeDrawable extends ShapeDrawable {
+ public AdaptiveIconShapeDrawable() {
+ super();
+ }
+
+ public AdaptiveIconShapeDrawable(Resources resources) {
+ super();
+ init(resources);
+ }
+
+ @Override
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+ init(r);
+ }
+
+ private void init(Resources resources) {
+ final float pathSize = AdaptiveIconDrawable.MASK_SIZE;
+ final Path path = new Path(PathParser.createPathFromPathData(
+ resources.getString(com.android.internal.R.string.config_icon_mask)));
+ setShape(new PathShape(path, pathSize, pathSize));
+ }
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
new file mode 100644
index 000000000000..1c65bc248961
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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.settingslib.widget;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.DrawableWrapper;
+import android.util.PathParser;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Adaptive outline drawable with white plain background color and black outline
+ */
+public class AdaptiveOutlineDrawable extends DrawableWrapper {
+ @VisibleForTesting
+ final Paint mOutlinePaint;
+ private Path mPath;
+ private final int mInsetPx;
+ private final Bitmap mBitmap;
+
+ public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) {
+ super(new AdaptiveIconShapeDrawable(resources));
+
+ getDrawable().setTint(Color.WHITE);
+ mPath = new Path(PathParser.createPathFromPathData(
+ resources.getString(com.android.internal.R.string.config_icon_mask)));
+ mOutlinePaint = new Paint();
+ mOutlinePaint.setColor(resources.getColor(R.color.bt_outline_color, null));
+ mOutlinePaint.setStyle(Paint.Style.STROKE);
+ mOutlinePaint.setStrokeWidth(resources.getDimension(R.dimen.adaptive_outline_stroke));
+ mOutlinePaint.setAntiAlias(true);
+
+ mInsetPx = resources
+ .getDimensionPixelSize(R.dimen.dashboard_tile_foreground_image_inset);
+ mBitmap = bitmap;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ final Rect bounds = getBounds();
+ final float pathSize = AdaptiveIconDrawable.MASK_SIZE;
+
+ final float scaleX = (bounds.right - bounds.left) / pathSize;
+ final float scaleY = (bounds.bottom - bounds.top) / pathSize;
+
+ final int count = canvas.save();
+ canvas.scale(scaleX, scaleY);
+ // Draw outline
+ canvas.drawPath(mPath, mOutlinePaint);
+ canvas.restoreToCount(count);
+
+ // Draw the foreground icon
+ canvas.drawBitmap(mBitmap, bounds.left + mInsetPx, bounds.top + mInsetPx, null);
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mBitmap.getHeight() + 2 * mInsetPx;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mBitmap.getWidth() + 2 * mInsetPx;
+ }
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 730e9e134e3d..b532621cd617 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -22,6 +22,7 @@ android_library {
"SettingsLibEntityHeaderWidgets",
"SettingsLibBarChartPreference",
"SettingsLibProgressBar",
+ "SettingsLibAdaptiveIcon",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/Tile/Android.bp b/packages/SettingsLib/Tile/Android.bp
new file mode 100644
index 000000000000..bf16ef317fd8
--- /dev/null
+++ b/packages/SettingsLib/Tile/Android.bp
@@ -0,0 +1,11 @@
+android_library {
+ name: "SettingsLibTile",
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ ],
+
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/Tile/AndroidManifest.xml b/packages/SettingsLib/Tile/AndroidManifest.xml
new file mode 100644
index 000000000000..b13532e2a5fd
--- /dev/null
+++ b/packages/SettingsLib/Tile/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.drawer">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java
index a3dda658bec7..7b062b1ac9a9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
+/*
+ * Copyright (C) 2019 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
+ * 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,
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.settingslib.drawer;
import static java.lang.String.CASE_INSENSITIVE_ORDER;
@@ -26,6 +25,9 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+/**
+ * The category for handle {@link Tile}
+ */
public class DashboardCategory implements Parcelable {
/**
@@ -67,18 +69,30 @@ public class DashboardCategory implements Parcelable {
return result;
}
+ /**
+ * Add tile
+ */
public synchronized void addTile(Tile tile) {
mTiles.add(tile);
}
+ /**
+ * Remove tile
+ */
public synchronized void removeTile(int n) {
mTiles.remove(n);
}
+ /**
+ * Get size of tile
+ */
public int getTilesCount() {
return mTiles.size();
}
+ /**
+ * Get tile
+ */
public Tile getTile(int n) {
return mTiles.get(n);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index d28b00a7ed39..5108efbdc216 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
+/*
+ * Copyright (C) 2019 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
+ * 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,
@@ -84,8 +84,8 @@ public class Tile implements Parcelable {
mActivityPackage = in.readString();
mActivityName = in.readString();
mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
- final int N = in.readInt();
- for (int i = 0; i < N; i++) {
+ final int number = in.readInt();
+ for (int i = 0; i < number; i++) {
userHandle.add(UserHandle.CREATOR.createFromParcel(in));
}
mCategory = in.readString();
@@ -101,9 +101,9 @@ public class Tile implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mActivityPackage);
dest.writeString(mActivityName);
- final int N = userHandle.size();
- dest.writeInt(N);
- for (int i = 0; i < N; i++) {
+ final int size = userHandle.size();
+ dest.writeInt(size);
+ for (int i = 0; i < size; i++) {
userHandle.get(i).writeToParcel(dest, flags);
}
dest.writeString(mCategory);
@@ -151,6 +151,9 @@ public class Tile implements Parcelable {
}
}
+ /**
+ * Check whether title has order.
+ */
public boolean hasOrder() {
return mMetaData.containsKey(META_DATA_KEY_ORDER)
&& mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
@@ -262,6 +265,9 @@ public class Tile implements Parcelable {
}
}
+ /**
+ * Check whether title has key.
+ */
public boolean hasKey() {
return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_KEYHINT);
}
@@ -361,9 +367,12 @@ public class Tile implements Parcelable {
}
};
+ /**
+ * Check whether title is only have primary profile
+ */
public boolean isPrimaryProfileOnly() {
- String profile = mMetaData != null ?
- mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+ String profile = mMetaData != null
+ ? mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
profile = (profile != null ? profile : PROFILE_ALL);
return TextUtils.equals(profile, PROFILE_PRIMARY);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index 91892abdfb44..31925ab64ec3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.settingslib.drawer;
@@ -39,6 +39,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+/**
+ * Utils is a helper class that contains profile key, meta data, settings action
+ * and static methods for get icon or text from uri.
+ */
public class TileUtils {
private static final boolean DEBUG_TIMING = false;
diff --git a/packages/SettingsLib/res/drawable/ic_media_device.xml b/packages/SettingsLib/res/drawable/ic_media_device.xml
new file mode 100644
index 000000000000..5a6aeb4a8e23
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_media_device.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#00000000"
+ android:fillAlpha=".1"
+ android:pathData="M0 0h24v24H0z" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0 0h24v24H0z" />
+ <path
+ android:fillColor="#000000"
+ android:pathData="M21 3H3c-1.1 0-2 0.9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2 -0.9 2-2V5c0-1.1 -0.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z" />
+</vector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index ed3c11cd3ca3..39c55fd1925c 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -603,4 +603,26 @@
<item>3</item><item>3</item>
</array>
+ <!-- Bluetooth icon foreground colors -->
+ <integer-array name="bt_icon_fg_colors">
+ <item>@color/bt_color_icon_1</item>
+ <item>@color/bt_color_icon_2</item>
+ <item>@color/bt_color_icon_3</item>
+ <item>@color/bt_color_icon_4</item>
+ <item>@color/bt_color_icon_5</item>
+ <item>@color/bt_color_icon_6</item>
+ <item>@color/bt_color_icon_7</item>
+ </integer-array>
+
+ <!-- Bluetooth icon background colors -->
+ <integer-array name="bt_icon_bg_colors">
+ <item>@color/bt_color_bg_1</item>
+ <item>@color/bt_color_bg_2</item>
+ <item>@color/bt_color_bg_3</item>
+ <item>@color/bt_color_bg_4</item>
+ <item>@color/bt_color_bg_5</item>
+ <item>@color/bt_color_bg_6</item>
+ <item>@color/bt_color_bg_7</item>
+ </integer-array>
+
</resources>
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
index 66bbb3a6c890..4b91bbb8d8dc 100644
--- a/packages/SettingsLib/res/values/colors.xml
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -19,4 +19,20 @@
<color name="usage_graph_dots">@*android:color/tertiary_device_default_settings</color>
<color name="list_divider_color">#64000000</color>
+
+ <color name="bt_color_icon_1">#48a50e0e</color> <!-- 72% Material Red 900 -->
+ <color name="bt_color_icon_2">#480d652d</color> <!-- 72% Material Green 900 -->
+ <color name="bt_color_icon_3">#48e37400</color> <!-- 72% Material Yellow 900 -->
+ <color name="bt_color_icon_4">#48b06000</color> <!-- 72% Material Orange 900 -->
+ <color name="bt_color_icon_5">#489c166b</color> <!-- 72% Material Pink 900 -->
+ <color name="bt_color_icon_6">#48681da8</color> <!-- 72% Material Purple 900 -->
+ <color name="bt_color_icon_7">#48007b83</color> <!-- 72% Material Cyan 900 -->
+
+ <color name="bt_color_bg_1">#fad2cf</color> <!-- Material Red 100 -->
+ <color name="bt_color_bg_2">#ceead6</color> <!-- Material Green 100 -->
+ <color name="bt_color_bg_3">#feefc3</color> <!-- Material Yellow 100 -->
+ <color name="bt_color_bg_4">#fedfc8</color> <!-- Material Orange 100 -->
+ <color name="bt_color_bg_5">#fdcfe8</color> <!-- Material Pink 100 -->
+ <color name="bt_color_bg_6">#e9d2fd</color> <!-- Material Purple 100 -->
+ <color name="bt_color_bg_7">#cbf0f8</color> <!-- Material Cyan 100 -->
</resources>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index a9c5061f6d87..2cb9d4b14aa7 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -91,6 +91,7 @@
<!-- How far to inset the rounded edges -->
<dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen>
-
+ <!-- Size of nearby icon -->
+ <dimen name="bt_nearby_icon_size">24dp</dimen>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index bb8c8a6768ed..867efb408258 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,18 +1,30 @@
package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.util.Log;
import android.util.Pair;
import androidx.annotation.DrawableRes;
import com.android.settingslib.R;
+import com.android.settingslib.widget.AdaptiveIcon;
+import com.android.settingslib.widget.AdaptiveOutlineDrawable;
+import java.io.IOException;
import java.util.List;
public class BluetoothUtils {
+ private static final String TAG = "BluetoothUtils";
+
public static final boolean V = false; // verbose logging
public static final boolean D = true; // regular logging
@@ -112,4 +124,68 @@ public class BluetoothUtils {
public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId) {
return context.getDrawable(resId);
}
+
+ /**
+ * Get colorful bluetooth icon with description
+ */
+ public static Pair<Drawable, String> getBtRainbowDrawableWithDescription(Context context,
+ CachedBluetoothDevice cachedDevice) {
+ final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
+ context, cachedDevice);
+ final BluetoothDevice bluetoothDevice = cachedDevice.getDevice();
+ final boolean untetheredHeadset = bluetoothDevice != null
+ ? Boolean.parseBoolean(bluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))
+ : false;
+ final int iconSize = context.getResources().getDimensionPixelSize(
+ R.dimen.bt_nearby_icon_size);
+ final Resources resources = context.getResources();
+
+ // Deal with untethered headset
+ if (untetheredHeadset) {
+ final String uriString = bluetoothDevice != null
+ ? bluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)
+ : null;
+ final Uri iconUri = uriString != null ? Uri.parse(uriString) : null;
+ if (iconUri != null) {
+ try {
+ final Bitmap bitmap = MediaStore.Images.Media.getBitmap(
+ context.getContentResolver(), iconUri);
+ if (bitmap != null) {
+ final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
+ iconSize, false);
+ bitmap.recycle();
+ final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(
+ resources, resizedBitmap);
+ return new Pair<>(drawable, pair.second);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to get drawable for: " + iconUri, e);
+ }
+ }
+ }
+
+ return new Pair<>(buildBtRainbowDrawable(context,
+ pair.first, cachedDevice.getAddress().hashCode()), pair.second);
+ }
+
+ /**
+ * Build Bluetooth device icon with rainbow
+ */
+ public static Drawable buildBtRainbowDrawable(Context context, Drawable drawable,
+ int hashCode) {
+ final Resources resources = context.getResources();
+
+ // Deal with normal headset
+ final int[] iconFgColors = resources.getIntArray(R.array.bt_icon_fg_colors);
+ final int[] iconBgColors = resources.getIntArray(R.array.bt_icon_bg_colors);
+
+ // get color index based on mac address
+ final int index = Math.abs(hashCode % iconBgColors.length);
+ drawable.setColorFilter(iconFgColors[index], PorterDuff.Mode.SRC_ATOP);
+ final Drawable adaptiveIcon = new AdaptiveIcon(context, drawable);
+ ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(iconBgColors[index]);
+
+ return adaptiveIcon;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 3092b9960c7e..2711e3175957 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -18,8 +18,11 @@ package com.android.settingslib.media;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.util.Log;
+import android.util.Pair;
+import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
/**
@@ -48,9 +51,10 @@ public class BluetoothMediaDevice extends MediaDevice {
}
@Override
- public int getIcon() {
- //TODO(b/117129183): This is not final icon for bluetooth device, just for demo.
- return com.android.internal.R.drawable.ic_bt_headphones_a2dp;
+ public Drawable getIcon() {
+ final Pair<Drawable, String> pair = BluetoothUtils
+ .getBtRainbowDrawableWithDescription(mContext, mCachedDevice);
+ return pair.first;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 95f3d3d0f769..732e8dba3e44 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -16,10 +16,14 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.widget.Toast;
import androidx.mediarouter.media.MediaRouter;
+import com.android.settingslib.R;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+
/**
* InfoMediaDevice extends MediaDevice to represents wifi device.
*/
@@ -46,9 +50,10 @@ public class InfoMediaDevice extends MediaDevice {
}
@Override
- public int getIcon() {
- //TODO(b/121083246): This is not final icon for cast device, just for demo.
- return com.android.internal.R.drawable.ic_settings_print;
+ public Drawable getIcon() {
+ //TODO(b/120669861): Return remote device icon uri once api is ready.
+ return BluetoothUtils.buildBtRainbowDrawable(mContext,
+ mContext.getDrawable(R.drawable.ic_media_device), getId().hashCode());
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 9b9e80310c18..53a852069478 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -16,6 +16,7 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import androidx.annotation.IntDef;
@@ -70,11 +71,11 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
public abstract String getSummary();
/**
- * Get resource id of MediaDevice.
+ * Get icon of MediaDevice.
*
- * @return resource id of MediaDevice.
+ * @return drawable of icon.
*/
- public abstract int getIcon();
+ public abstract Drawable getIcon();
/**
* Get unique ID that represent MediaDevice
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 8c3fcc077edc..af91c3464194 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -16,10 +16,12 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.util.Log;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -56,8 +58,9 @@ public class PhoneMediaDevice extends MediaDevice {
}
@Override
- public int getIcon() {
- return R.drawable.ic_smartphone;
+ public Drawable getIcon() {
+ return BluetoothUtils.buildBtRainbowDrawable(mContext,
+ mContext.getDrawable(R.drawable.ic_smartphone), getId().hashCode());
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 3acbcd3f6b41..8a88a4c64d0a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -53,6 +53,7 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import androidx.annotation.NonNull;
@@ -65,8 +66,10 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@@ -197,6 +200,9 @@ public class AccessPoint implements Comparable<AccessPoint> {
private final Context mContext;
+ private WifiManager mWifiManager;
+ private WifiManager.ActionListener mConnectListener;
+
private String ssid;
private String bssid;
private int security;
@@ -1068,8 +1074,10 @@ public class AccessPoint implements Comparable<AccessPoint> {
/**
* Starts the OSU Provisioning flow.
*/
- public void startOsuProvisioning() {
- mContext.getSystemService(WifiManager.class).startSubscriptionProvisioning(
+ public void startOsuProvisioning(@Nullable WifiManager.ActionListener connectListener) {
+ mConnectListener = connectListener;
+
+ getWifiManager().startSubscriptionProvisioning(
mOsuProvider,
mContext.getMainExecutor(),
new AccessPointProvisioningCallback()
@@ -1539,12 +1547,20 @@ public class AccessPoint implements Comparable<AccessPoint> {
return string;
}
+ private WifiManager getWifiManager() {
+ if (mWifiManager == null) {
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ }
+ return mWifiManager;
+ }
+
/**
* Callbacks relaying changes to the AccessPoint representation.
*
* <p>All methods are invoked on the Main Thread.
*/
public interface AccessPointListener {
+
/**
* Indicates a change to the externally visible state of the AccessPoint trigger by an
* update of ScanResults, saved configuration state, connection state, or score
@@ -1561,7 +1577,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
* changed
*/
@MainThread void onAccessPointChanged(AccessPoint accessPoint);
-
/**
* Indicates the "wifi pie signal level" has changed, retrieved via calls to
* {@link AccessPoint#getLevel()}.
@@ -1643,11 +1658,46 @@ public class AccessPoint implements Comparable<AccessPoint> {
mOsuProvisioningComplete = true;
mOsuFailure = null;
mOsuStatus = null;
+
ThreadUtils.postOnMainThread(() -> {
if (mAccessPointListener != null) {
mAccessPointListener.onAccessPointChanged(AccessPoint.this);
}
});
+
+ // Connect to the freshly provisioned network.
+ WifiManager wifiManager = getWifiManager();
+
+ PasspointConfiguration passpointConfig = wifiManager
+ .getMatchingPasspointConfigsForOsuProviders(Collections.singleton(mOsuProvider))
+ .get(mOsuProvider);
+ if (passpointConfig == null) {
+ Log.e(TAG, "Missing PasspointConfiguration for newly provisioned network!");
+ if (mConnectListener != null) {
+ mConnectListener.onFailure(0);
+ }
+ return;
+ }
+
+ String fqdn = passpointConfig.getHomeSp().getFqdn();
+ for (Pair<WifiConfiguration, Map<Integer, List<ScanResult>>> pairing :
+ wifiManager.getAllMatchingWifiConfigs(wifiManager.getScanResults())) {
+ WifiConfiguration config = pairing.first;
+ if (TextUtils.equals(config.FQDN, fqdn)) {
+ List<ScanResult> homeScans =
+ pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK);
+ List<ScanResult> roamingScans =
+ pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK);
+
+ AccessPoint connectionAp =
+ new AccessPoint(mContext, config, homeScans, roamingScans);
+ wifiManager.connect(connectionAp.getConfig(), mConnectListener);
+ return;
+ }
+ }
+ if (mConnectListener != null) {
+ mConnectListener.onFailure(0);
+ }
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index fdc0fd33e6d7..5f2bc4e1d490 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -84,7 +84,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
private static final long DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS = 20 * DateUtils.MINUTE_IN_MILLIS;
/** Maximum age of scan results to hold onto while actively scanning. **/
- private static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000;
+ @VisibleForTesting static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000;
private static final String TAG = "WifiTracker";
private static final boolean DBG() {
@@ -142,6 +142,13 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
*/
private boolean mStaleScanResults = true;
+ /**
+ * Tracks whether the latest SCAN_RESULTS_AVAILABLE_ACTION contained new scans. If not, then
+ * we treat the last scan as an aborted scan and increase the eviction timeout window to avoid
+ * completely flushing the AP list before the next successful scan completes.
+ */
+ private boolean mLastScanSucceeded = true;
+
// Does not need to be locked as it only updated on the worker thread, with the exception of
// during onStart, which occurs before the receiver is registered on the work handler.
private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>();
@@ -478,17 +485,22 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
/**
- * Remove old scan results from the cache.
+ * Remove old scan results from the cache. If {@link #mLastScanSucceeded} is false, then
+ * increase the timeout window to avoid completely flushing the AP list before the next
+ * successful scan completes.
*
* <p>Should only ever be invoked from {@link #updateScanResultCache(List)} when
* {@link #mStaleScanResults} is false.
*/
private void evictOldScans() {
+ long evictionTimeoutMillis = mLastScanSucceeded ? MAX_SCAN_RESULT_AGE_MILLIS
+ : MAX_SCAN_RESULT_AGE_MILLIS * 2;
+
long nowMs = SystemClock.elapsedRealtime();
for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) {
ScanResult result = iter.next();
// result timestamp is in microseconds
- if (nowMs - result.timestamp / 1000 > MAX_SCAN_RESULT_AGE_MILLIS) {
+ if (nowMs - result.timestamp / 1000 > evictionTimeoutMillis) {
iter.remove();
}
}
@@ -840,6 +852,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
WifiManager.WIFI_STATE_UNKNOWN));
} else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
mStaleScanResults = false;
+ mLastScanSucceeded =
+ intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true);
fetchScansAndConfigsAndUpdateAccessPoints();
} else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 9c8e3f4fe543..8e4027164587 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -41,6 +41,7 @@ import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiSsid;
import android.net.wifi.hotspot2.OsuProvider;
@@ -53,6 +54,7 @@ import android.os.SystemClock;
import android.text.SpannableString;
import android.text.format.DateUtils;
import android.util.ArraySet;
+import android.util.Pair;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -72,6 +74,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -95,9 +98,12 @@ public class AccessPointTest {
private Context mContext;
private WifiInfo mWifiInfo;
+ @Mock private Context mMockContext;
+ @Mock private WifiManager mMockWifiManager;
@Mock private RssiCurve mockBadgeCurve;
@Mock private WifiNetworkScoreCache mockWifiNetworkScoreCache;
@Mock private AccessPoint.AccessPointListener mMockAccessPointListener;
+ @Mock private WifiManager.ActionListener mMockConnectListener;
private static final int NETWORK_ID = 123;
private static final int DEFAULT_RSSI = -55;
@@ -1360,6 +1366,9 @@ public class AccessPointTest {
.isEqualTo(mContext.getString(R.string.tap_to_sign_up));
}
+ /**
+ * Verifies that the summary of an OSU entry updates based on provisioning status.
+ */
@Test
public void testOsuAccessPointSummary_showsProvisioningUpdates() {
AccessPoint osuAccessPoint = new AccessPoint(mContext, createOsuProvider(),
@@ -1411,4 +1420,82 @@ public class AccessPointTest {
assertThat(osuAccessPoint.getSummary())
.isEqualTo(mContext.getString(R.string.osu_sign_up_complete));
}
+
+ /**
+ * Verifies that after provisioning through an OSU provider, we connect to the freshly
+ * provisioned network.
+ */
+ @Test
+ public void testOsuAccessPoint_connectsAfterProvisioning() {
+ // Set up mock for WifiManager.getAllMatchingWifiConfigs
+ WifiConfiguration config = new WifiConfiguration();
+ config.FQDN = "fqdn";
+ Map<Integer, List<ScanResult>> scanMapping = new HashMap<>();
+ scanMapping.put(WifiManager.PASSPOINT_HOME_NETWORK, mScanResults);
+ Pair<WifiConfiguration, Map<Integer, List<ScanResult>>> configMapPair =
+ new Pair<>(config, scanMapping);
+ List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> matchingWifiConfig =
+ new ArrayList<>();
+ matchingWifiConfig.add(configMapPair);
+ when(mMockWifiManager.getAllMatchingWifiConfigs(any())).thenReturn(matchingWifiConfig);
+
+ // Set up mock for WifiManager.getMatchingPasspointConfigsForOsuProviders
+ OsuProvider provider = createOsuProvider();
+ PasspointConfiguration passpointConfig = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("fqdn");
+ homeSp.setFriendlyName("Test Provider");
+ passpointConfig.setHomeSp(homeSp);
+ Map<OsuProvider, PasspointConfiguration> osuProviderConfigMap = new HashMap<>();
+ osuProviderConfigMap.put(provider, passpointConfig);
+ when(mMockWifiManager
+ .getMatchingPasspointConfigsForOsuProviders(Collections.singleton(provider)))
+ .thenReturn(osuProviderConfigMap);
+
+ when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+
+ AccessPoint osuAccessPoint = new AccessPoint(mMockContext, provider, mScanResults);
+ osuAccessPoint.setListener(mMockAccessPointListener);
+
+ AccessPoint.AccessPointProvisioningCallback provisioningCallback =
+ osuAccessPoint.new AccessPointProvisioningCallback();
+ provisioningCallback.onProvisioningComplete();
+
+ verify(mMockWifiManager).connect(any(), any());
+ }
+
+ /**
+ * Verifies that after provisioning through an OSU provider, we call the connect listener's
+ * onFailure() method if we cannot find the network we just provisioned.
+ */
+ @Test
+ public void testOsuAccessPoint_noMatchingConfigsAfterProvisioning_callsOnFailure() {
+ // Set up mock for WifiManager.getAllMatchingWifiConfigs
+ when(mMockWifiManager.getAllMatchingWifiConfigs(any())).thenReturn(new ArrayList<>());
+
+ // Set up mock for WifiManager.getMatchingPasspointConfigsForOsuProviders
+ OsuProvider provider = createOsuProvider();
+ PasspointConfiguration passpointConfig = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("fqdn");
+ homeSp.setFriendlyName("Test Provider");
+ passpointConfig.setHomeSp(homeSp);
+ Map<OsuProvider, PasspointConfiguration> osuProviderConfigMap = new HashMap<>();
+ osuProviderConfigMap.put(provider, passpointConfig);
+ when(mMockWifiManager
+ .getMatchingPasspointConfigsForOsuProviders(Collections.singleton(provider)))
+ .thenReturn(osuProviderConfigMap);
+
+ when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager);
+
+ AccessPoint osuAccessPoint = new AccessPoint(mMockContext, provider, mScanResults);
+ osuAccessPoint.setListener(mMockAccessPointListener);
+ osuAccessPoint.startOsuProvisioning(mMockConnectListener);
+
+ AccessPoint.AccessPointProvisioningCallback provisioningCallback =
+ osuAccessPoint.new AccessPointProvisioningCallback();
+ provisioningCallback.onProvisioningComplete();
+
+ verify(mMockConnectListener).onFailure(anyInt());
+ }
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index edf414ddf323..683ec8bb5a6c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -270,7 +270,7 @@ public class WifiTrackerTest {
SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */);
}
- private static ScanResult buildStaleScanResult() {
+ private static ScanResult buildScanResultWithTimestamp(long timestampMillis) {
return new ScanResult(
WifiSsid.createFromAsciiEncoded(SSID_3),
BSSID_3,
@@ -280,7 +280,7 @@ public class WifiTrackerTest {
"", // capabilities
RSSI_3,
0, // frequency
- 0 /* microsecond timestamp */);
+ timestampMillis * 1000 /* microsecond timestamp */);
}
private static WifiConfiguration buildPasspointConfiguration(String fqdn, String friendlyName) {
@@ -379,6 +379,12 @@ public class WifiTrackerTest {
tracker.mReceiver.onReceive(mContext, i);
}
+ private void sendFailedScanResults(WifiTracker tracker) throws InterruptedException {
+ Intent i = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ i.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
+ tracker.mReceiver.onReceive(mContext, i);
+ }
+
private void sendUpdatedScores() throws InterruptedException {
Bundle attr1 = new Bundle();
attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve1);
@@ -982,8 +988,8 @@ public class WifiTrackerTest {
@Test
public void onStart_updateScanResults_evictOldScanResult() {
- when(mockWifiManager.getScanResults()).thenReturn(
- Arrays.asList(buildScanResult1(), buildScanResult2(), buildStaleScanResult()));
+ when(mockWifiManager.getScanResults()).thenReturn(Arrays.asList(
+ buildScanResult1(), buildScanResult2(), buildScanResultWithTimestamp(0)));
WifiTracker tracker = createMockedWifiTracker();
tracker.forceUpdate();
@@ -995,6 +1001,33 @@ public class WifiTrackerTest {
}
/**
+ * Verifies that a failed scan reported on SCAN_RESULTS_AVAILABLE_ACTION should increase the
+ * ScanResult eviction timeout to twice the default.
+ */
+ @Test
+ public void failedScan_increasesEvictionTimeout() throws InterruptedException {
+ when(mockWifiManager.getScanResults()).thenReturn(Arrays.asList(
+ buildScanResult1(), buildScanResult2(), buildScanResultWithTimestamp(
+ SystemClock.elapsedRealtime() - WifiTracker.MAX_SCAN_RESULT_AGE_MILLIS)));
+ WifiTracker tracker = createMockedWifiTracker();
+
+ sendFailedScanResults(tracker);
+
+ // Failed scan increases timeout window to include the stale scan
+ assertThat(tracker.getAccessPoints()).hasSize(3);
+ assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1);
+ assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2);
+ assertThat(tracker.getAccessPoints().get(2).getBssid()).isEqualTo(BSSID_3);
+
+ sendScanResults(tracker);
+
+ // Successful scan resets the timeout window to remove the stale scan
+ assertThat(tracker.getAccessPoints()).hasSize(2);
+ assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1);
+ assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2);
+ }
+
+ /**
* Verifies that updatePasspointAccessPoints will only return AccessPoints whose
* isPasspoint() evaluates as true.
*/
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index b713e08eb67e..b228cf7c10c6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -15,15 +15,20 @@
*/
package com.android.settingslib.bluetooth;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.Pair;
+import com.android.settingslib.widget.AdaptiveIcon;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,6 +43,9 @@ public class BluetoothUtilsTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+
private Context mContext;
@Before
@@ -66,4 +74,16 @@ public class BluetoothUtilsTest {
verify(mContext).getDrawable(com.android.internal.R.drawable.ic_bt_laptop);
}
+
+ @Test
+ public void getBtRainbowDrawableWithDescription_normalHeadset_returnAdaptiveIcon() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn("false");
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn("1f:aa:bb");
+
+ assertThat(BluetoothUtils.getBtRainbowDrawableWithDescription(
+ RuntimeEnvironment.application,
+ mCachedBluetoothDevice).first).isInstanceOf(AdaptiveIcon.class);
+ }
} \ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
new file mode 100644
index 000000000000..ed6b9b0a135e
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 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.settingslib.widget;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Icon;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Bundle;
+
+import com.android.settingslib.R;
+import com.android.settingslib.drawer.CategoryKey;
+import com.android.settingslib.drawer.Tile;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AdaptiveIconTest {
+
+ private Context mContext;
+ private ActivityInfo mActivityInfo;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mActivityInfo = new ActivityInfo();
+ mActivityInfo.packageName = mContext.getPackageName();
+ mActivityInfo.name = "class";
+ mActivityInfo.metaData = new Bundle();
+ }
+
+ @Test
+ public void createIcon_shouldSetBackgroundAndInset() {
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+ assertThat(icon.getNumberOfLayers()).isEqualTo(2);
+ assertThat(icon.getDrawable(0)).isInstanceOf(AdaptiveIconShapeDrawable.class);
+ }
+
+ @Test
+ public void setBackgroundColor_shouldUpdateColorFilter() {
+ final AdaptiveIcon icon =
+ spy(new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)));
+ final ShapeDrawable background = mock(ShapeDrawable.class);
+ when(icon.getDrawable(0)).thenReturn(background);
+
+ icon.setBackgroundColor(Color.BLUE);
+
+ verify(background).setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP);
+ }
+
+ @Test
+ public void setBackgroundColor_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() {
+ final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000);
+ doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ .when(tile).getIcon(mContext);
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+ icon.setBackgroundColor(mContext, tile);
+ assertThat(icon.mBackgroundColor).isEqualTo(0xff0000);
+ }
+
+ @Test
+ public void setBackgroundColor_tileWithoutBackgroundColor_shouldSetDefaultBackgroundColor() {
+ final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ .when(tile).getIcon(mContext);
+ final AdaptiveIcon icon = new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+ icon.setBackgroundColor(mContext, tile);
+
+ assertThat(icon.mBackgroundColor)
+ .isEqualTo(mContext.getColor(R.color.homepage_generic_icon_background));
+ }
+
+ @Test
+ public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() {
+ final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
+ R.color.bt_outline_color);
+ doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ .when(tile).getIcon(mContext);
+
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+ icon.setBackgroundColor(mContext, tile);
+
+ assertThat(icon.mBackgroundColor)
+ .isEqualTo(mContext.getColor(R.color.bt_outline_color));
+ }
+
+ @Test
+ public void getConstantState_returnCorrectState() {
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+ icon.setBackgroundColor(Color.YELLOW);
+
+ final AdaptiveIcon.AdaptiveConstantState state =
+ (AdaptiveIcon.AdaptiveConstantState) icon.getConstantState();
+
+ assertThat(state.mColor).isEqualTo(Color.YELLOW);
+ assertThat(state.mContext).isEqualTo(mContext);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
new file mode 100644
index 000000000000..71d55bc3de2a
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.res.Resources;
+import android.graphics.Paint;
+
+import com.android.settingslib.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AdaptiveOutlineDrawableTest {
+
+ @Test
+ public void constructor_initPaint() {
+ final Resources resources = RuntimeEnvironment.application.getResources();
+ final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(resources, null);
+
+ assertThat(drawable.mOutlinePaint.getStyle()).isEqualTo(Paint.Style.STROKE);
+ assertThat(drawable.mOutlinePaint.getStrokeWidth()).isWithin(0.01f).of(
+ resources.getDimension(R.dimen.adaptive_outline_stroke));
+ }
+
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d39646b566d8..2a9456dd723c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -168,6 +168,9 @@
<uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
<uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
<uses-permission android:name="android.permission.WATCH_APPOPS" />
+ <!-- Permission needed to invoke DynamicSystem (AOT) -->
+ <uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
+
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
<uses-permission android:name="android.permission.SUSPEND_APPS" />
diff --git a/packages/SystemUI/res/drawable/ic_camera.xml b/packages/SystemUI/res/drawable/ic_camera.xml
new file mode 100644
index 000000000000..b330875864d3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_camera.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFF"
+ android:pathData="M20,5h-3.17L15,3H9L7.17,5H4C2.9,5 2,5.9 2,7v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V7C22,5.9 21.1,5 20,5zM20,19H4V7h16V19zM12,9c-2.21,0 -4,1.79 -4,4c0,2.21 1.79,4 4,4s4,-1.79 4,-4C16,10.79 14.21,9 12,9z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_camera.xml b/packages/SystemUI/res/drawable/stat_sys_camera.xml
index eb3e9632dd35..c914262571ea 100644
--- a/packages/SystemUI/res/drawable/stat_sys_camera.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_camera.xml
@@ -18,14 +18,5 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="3dp"
- android:insetRight="3dp">
- <vector
- android:width="17dp"
- android:height="17dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FFF"
- android:pathData="M20,5h-3.17L15,3H9L7.17,5H4C2.9,5 2,5.9 2,7v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V7C22,5.9 21.1,5 20,5zM20,19H4V7h16V19zM12,9c-2.21,0 -4,1.79 -4,4c0,2.21 1.79,4 4,4s4,-1.79 4,-4C16,10.79 14.21,9 12,9z"/>
- </vector>
-</inset>
+ android:insetRight="3dp"
+ android:drawable="@drawable/ic_camera" /> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index d49aff9abc01..3786812db827 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -39,7 +39,7 @@
android:layout_height="wrap_content"
android:paddingEnd="12dp"
android:paddingBottom="4dp"
- android:textColor="@color/ksh_keyword_color"
+ android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:maxLines="5"
android:singleLine="false"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index b82c250e2bd2..290d75b4de9c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -129,7 +129,6 @@
<!-- Keyboard shortcuts colors -->
<color name="ksh_application_group_color">#fff44336</color>
- <color name="ksh_keyword_color">#d9000000</color>
<color name="ksh_key_item_color">@color/material_grey_600</color>
<color name="ksh_key_item_background">@color/material_grey_100</color>
diff --git a/packages/SystemUI/res/values/config_car.xml b/packages/SystemUI/res/values/config_car.xml
deleted file mode 100644
index 2c549bc8ce2d..000000000000
--- a/packages/SystemUI/res/values/config_car.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2016, 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.
-*/
--->
-
-<resources>
- <!-- These next two work together, if you enable this first one you need to provide an intent
- uri that will be launched into the docked window. -->
- <bool name="config_enablePersistentDockedActivity">false</bool>
- <string name="config_persistentDockedActivityIntentUri" translatable="false"></string>
-
- <!-- configure which system ui bars should be displayed -->
- <bool name="config_enableLeftNavigationBar">false</bool>
- <bool name="config_enableRightNavigationBar">false</bool>
- <bool name="config_enableBottomNavigationBar">true</bool>
- <bool name="config_hideNavWhenKeyguardBouncerShown">true</bool>
-</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1a865eef8d24..6eb279affc4f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -23,8 +23,6 @@
<dimen name="navigation_bar_size">@*android:dimen/navigation_bar_height</dimen>
<!-- Minimum swipe distance to catch the swipe gestures to invoke assist or switch tasks. -->
<dimen name="navigation_bar_min_swipe_distance">48dp</dimen>
- <!-- The default distance from a side of the device to start an edge swipe from -->
- <dimen name="navigation_bar_default_edge_width">48dp</dimen>
<dimen name="navigation_bar_default_edge_height">500dp</dimen>
<!-- thickness (height) of the dead zone at the top of the navigation bar,
@@ -985,10 +983,6 @@
<!-- How much into a DisplayCutout's bounds we can go, on each side -->
<dimen name="display_cutout_margin_consumption">0px</dimen>
- <!-- How much we expand the touchable region of the status bar below the notch to catch touches
- that just start below the notch. -->
- <dimen name="display_cutout_touchable_region_size">12dp</dimen>
-
<!-- Padding below Ongoing App Ops dialog title -->
<dimen name="ongoing_appops_dialog_sep">16dp</dimen>
<!--Padding around text items in Ongoing App Ops dialog -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 46f4c8689196..d0c17b7f6738 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -112,4 +112,22 @@ public class QuickStepContract {
return context.getResources().getInteger(
com.android.internal.R.integer.config_navBarInteractionMode);
}
+
+ /**
+ * @return {@code true} if the navbar can be clicked through
+ */
+ public static boolean isNavBarClickThrough(Context context) {
+ return context.getResources().getBoolean(
+ com.android.internal.R.bool.config_navBarTapThrough);
+ }
+
+ /**
+ * @return the edge sensitivity width in px
+ */
+ public static int getEdgeSensitivityWidth(Context context) {
+ return context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.config_backGestureInset);
+ }
+
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 7f39e474f6c6..7e6ddcfea762 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -128,7 +128,8 @@ public final class KeyboardShortcuts {
private KeyCharacterMap mBackupKeyCharacterMap;
private KeyboardShortcuts(Context context) {
- this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault_Light);
+ this.mContext = new ContextThemeWrapper(
+ context, android.R.style.Theme_DeviceDefault_Settings);
this.mPackageManager = AppGlobals.getPackageManager();
loadResources(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
index 08a10dc925e3..ac58e681dbbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
@@ -36,7 +36,8 @@ import javax.inject.Singleton;
/**
*/
@Singleton
-public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher {
+public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher,
+ LightBarTransitionsController.DarkIntensityApplier {
private final LightBarTransitionsController mTransitionsController;
private final Rect mTintArea = new Rect();
@@ -54,8 +55,7 @@ public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher {
mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone);
mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone);
- mTransitionsController = new LightBarTransitionsController(context,
- this::setIconTintInternal);
+ mTransitionsController = new LightBarTransitionsController(context, this);
}
public LightBarTransitionsController getTransitionsController() {
@@ -104,13 +104,19 @@ public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher {
applyIconTint();
}
- private void setIconTintInternal(float darkIntensity) {
+ @Override
+ public void applyDarkIntensity(float darkIntensity) {
mDarkIntensity = darkIntensity;
mIconTint = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
mLightModeIconColorSingleTone, mDarkModeIconColorSingleTone);
applyIconTint();
}
+ @Override
+ public int getTintAnimationDuration() {
+ return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
+ }
+
private void applyIconTint() {
for (int i = 0; i < mReceivers.size(); i++) {
mReceivers.valueAt(i).onDarkChanged(mTintArea, mDarkIntensity, mIconTint);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 9844d8e5a67a..b7a154d67c10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -140,7 +140,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
mHeadsUpInset = mStatusBarHeight + resources.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding);
mDisplayCutoutTouchableRegionSize = resources.getDimensionPixelSize(
- R.dimen.display_cutout_touchable_region_size);
+ com.android.internal.R.dimen.display_cutout_touchable_region_size);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index b622688a8ac6..d7097309ce20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.statusbar.phone.NavBarTintController.DEFAULT_COLOR_ADAPT_TRANSITION_TIME;
-import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME;
-
import android.animation.ValueAnimator;
import android.content.Context;
import android.os.Bundle;
@@ -52,7 +49,6 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
private final DarkIntensityApplier mApplier;
private final KeyguardMonitor mKeyguardMonitor;
private final StatusBarStateController mStatusBarStateController;
- private NavBarTintController mColorAdaptionController;
private boolean mTransitionDeferring;
private long mTransitionDeferringStartTime;
@@ -118,7 +114,8 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
}
if (mTransitionPending && mTintChangePending) {
mTintChangePending = false;
- animateIconTint(mPendingDarkIntensity, 0 /* delay */, getTintAnimationDuration());
+ animateIconTint(mPendingDarkIntensity, 0 /* delay */,
+ mApplier.getTintAnimationDuration());
}
mTransitionPending = false;
}
@@ -159,15 +156,8 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()),
mTransitionDeferringDuration);
} else {
- animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, getTintAnimationDuration());
- }
- }
-
- public long getTintAnimationDuration() {
- if (NavBarTintController.isEnabled(mContext)) {
- return Math.max(DEFAULT_COLOR_ADAPT_TRANSITION_TIME, MIN_COLOR_ADAPT_TRANSITION_TIME);
+ animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, mApplier.getTintAnimationDuration());
}
- return DEFAULT_TINT_ANIMATION_DURATION;
}
public float getCurrentDarkIntensity() {
@@ -243,5 +233,6 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
*/
public interface DarkIntensityApplier {
void applyDarkIntensity(float darkIntensity);
+ int getTintAnimationDuration();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index d4cec429dc90..8ff6cc9b3d93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.phone.NavBarTintController.DEFAULT_COLOR_ADAPT_TRANSITION_TIME;
+import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME;
+
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
@@ -31,7 +34,8 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-public final class NavigationBarTransitions extends BarTransitions {
+public final class NavigationBarTransitions extends BarTransitions implements
+ LightBarTransitionsController.DarkIntensityApplier {
private final NavigationBarView mView;
private final IStatusBarService mBarService;
@@ -59,8 +63,7 @@ public final class NavigationBarTransitions extends BarTransitions {
mView = view;
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mLightTransitionsController = new LightBarTransitionsController(view.getContext(),
- this::applyDarkIntensity);
+ mLightTransitionsController = new LightBarTransitionsController(view.getContext(), this);
mAllowAutoDimWallpaperNotVisible = view.getContext().getResources()
.getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
@@ -170,4 +173,12 @@ public final class NavigationBarTransitions extends BarTransitions {
}
mView.onDarkIntensityChange(darkIntensity);
}
+
+ @Override
+ public int getTintAnimationDuration() {
+ if (NavBarTintController.isEnabled(mView.getContext())) {
+ return Math.max(DEFAULT_COLOR_ADAPT_TRANSITION_TIME, MIN_COLOR_ADAPT_TRANSITION_TIME);
+ }
+ return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index 7ea72c79501d..47a10547688b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -17,7 +17,9 @@
package com.android.systemui.statusbar.phone;
import android.annotation.IntDef;
+import android.content.ComponentCallbacks;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Point;
@@ -25,6 +27,8 @@ import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
+import com.android.systemui.shared.system.QuickStepContract;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -33,7 +37,7 @@ import java.lang.annotation.RetentionPolicy;
* prototypes to run in the system. The class will handle communication changes from the settings
* app and call back to listeners.
*/
-public class NavigationPrototypeController extends ContentObserver {
+public class NavigationPrototypeController extends ContentObserver implements ComponentCallbacks {
private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";
private static final String HIDE_HOME_BUTTON_SETTING = "quickstepcontroller_hidehome";
private static final String PROTOTYPE_ENABLED = "prototype_enabled";
@@ -85,9 +89,9 @@ public class NavigationPrototypeController extends ContentObserver {
registerObserver(HIDE_HOME_BUTTON_SETTING);
registerObserver(GESTURE_MATCH_SETTING);
registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING);
- registerObserver(EDGE_SENSITIVITY_WIDTH_SETTING);
registerObserver(SHOW_HOME_HANDLE_SETTING);
registerObserver(ENABLE_ASSISTANT_GESTURE);
+ mContext.registerComponentCallbacks(this);
}
/**
@@ -95,6 +99,7 @@ public class NavigationPrototypeController extends ContentObserver {
*/
public void unregister() {
mContext.getContentResolver().unregisterContentObserver(this);
+ mContext.unregisterComponentCallbacks(this);
}
@Override
@@ -115,9 +120,6 @@ public class NavigationPrototypeController extends ContentObserver {
} else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
mListener.onColorAdaptChanged(
NavBarTintController.isEnabled(mContext));
- } else if (path.endsWith(EDGE_SENSITIVITY_WIDTH_SETTING)) {
- mListener.onEdgeSensitivityChanged(getEdgeSensitivityWidth(),
- getEdgeSensitivityHeight());
} else if (path.endsWith(SHOW_HOME_HANDLE_SETTING)) {
mListener.onHomeHandleVisiblilityChanged(showHomeHandle());
} else if (path.endsWith(ENABLE_ASSISTANT_GESTURE)) {
@@ -130,8 +132,7 @@ public class NavigationPrototypeController extends ContentObserver {
* @return the width for edge swipe
*/
public int getEdgeSensitivityWidth() {
- // TODO: Move into resource
- return convertDpToPixel(getGlobalInt(EDGE_SENSITIVITY_WIDTH_SETTING, 48));
+ return QuickStepContract.getEdgeSensitivityWidth(mContext);
}
/**
@@ -203,6 +204,18 @@ public class NavigationPrototypeController extends ContentObserver {
return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (mListener != null) {
+ mListener.onEdgeSensitivityChanged(getEdgeSensitivityWidth(),
+ getEdgeSensitivityHeight());
+ }
+ }
+
+ @Override
+ public void onLowMemory() {
+ }
+
public interface OnPrototypeChangedListener {
void onGestureRemap(@GestureAction int[] actions);
void onBackButtonVisibilityChanged(boolean visible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 25cb7d0573da..8053ec7e5838 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -29,7 +29,6 @@ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
import static com.android.systemui.statusbar.phone.NavigationBarView.WINDOW_TARGET_BOTTOM;
-import static com.android.systemui.statusbar.phone.NavigationPrototypeController.EDGE_SENSITIVITY_WIDTH_SETTING;
import android.annotation.Nullable;
import android.content.Context;
@@ -264,10 +263,7 @@ public class QuickStepController implements GestureHelper {
mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
mAllowGestureDetection = true;
mNotificationsVisibleOnDown = !mNavigationBarView.isNotificationsFullyCollapsed();
- final int defaultRegionThreshold = mContext.getResources()
- .getDimensionPixelOffset(R.dimen.navigation_bar_default_edge_width);
- mGestureRegionThreshold = convertDpToPixel(getIntGlobalSetting(mContext,
- EDGE_SENSITIVITY_WIDTH_SETTING, defaultRegionThreshold));
+ mGestureRegionThreshold = QuickStepContract.getEdgeSensitivityWidth(mContext);
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -357,7 +353,7 @@ public class QuickStepController implements GestureHelper {
if (mCurrentAction != null) {
mCurrentAction.endGesture();
}
- } else if (QuickStepContract.isGesturalMode(mContext)
+ } else if (QuickStepContract.isNavBarClickThrough(mContext)
&& !mClickThroughPressed) {
// Enable click through functionality where no gesture has been detected and
// not passed the drag slop so inject a touch event at the same location
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
index 545a3cc52193..23d2e7babdcc 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml
@@ -26,4 +26,11 @@
<!-- Controls whether the nav bar can move from the bottom to the side in landscape.
Only applies if the device display is not square. -->
<bool name="config_navBarCanMove">false</bool>
-</resources> \ No newline at end of file
+
+ <!-- Controls whether the navigation bar lets through taps. -->
+ <bool name="config_navBarTapThrough">true</bool>
+
+ <!-- Controls the size of the back gesture inset. -->
+ <dimen name="config_backGestureInset">48dp</dimen>
+
+</resources>
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 904817e1763e..3c69bb73f893 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2552,6 +2552,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled);
pw.append(", nonInteractiveUiTimeout=" + userState.mNonInteractiveUiTimeout);
pw.append(", interactiveUiTimeout=" + userState.mInteractiveUiTimeout);
+ pw.append(", installedServiceCount=" + userState.mInstalledServices.size());
if (mUiAutomationManager.isUiAutomationRunningLocked()) {
pw.append(", ");
mUiAutomationManager.dumpUiAutomationService(fd, pw, args);
@@ -2559,7 +2560,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
pw.append("}");
pw.println();
- pw.append(" services:{");
+ pw.append(" Bound services:{");
final int serviceCount = userState.mBoundServices.size();
for (int j = 0; j < serviceCount; j++) {
if (j > 0) {
@@ -2570,6 +2571,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
AccessibilityServiceConnection service = userState.mBoundServices.get(j);
service.dump(fd, pw, args);
}
+ pw.println("}");
+ pw.append(" Enabled services:{");
+ Iterator<ComponentName> it = userState.mEnabledServices.iterator();
+ if (it.hasNext()) {
+ ComponentName componentName = it.next();
+ pw.append(componentName.toShortString());
+ while (it.hasNext()) {
+ componentName = it.next();
+ pw.append(", ");
+ pw.append(componentName.toShortString());
+ }
+ }
+ pw.println("}");
+ pw.append(" Binding services:{");
+ it = userState.mBindingServices.iterator();
+ if (it.hasNext()) {
+ ComponentName componentName = it.next();
+ pw.append(componentName.toShortString());
+ while (it.hasNext()) {
+ componentName = it.next();
+ pw.append(", ");
+ pw.append(componentName.toShortString());
+ }
+ }
pw.println("}]");
pw.println();
}
@@ -2585,6 +2610,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
pw.append(window.toString());
pw.append(']');
}
+ pw.println();
}
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1169eeb76801..ed0399f1e883 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5385,7 +5385,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mDnsResolver,
mNMS, factorySerialNumber);
// Make sure the network capabilities reflect what the agent info says.
- nai.networkCapabilities = mixInCapabilities(nai, nc);
+ nai.setNetworkCapabilities(mixInCapabilities(nai, nc));
final String extraInfo = networkInfo.getExtraInfo();
final String name = TextUtils.isEmpty(extraInfo)
? nai.networkCapabilities.getSSID() : extraInfo;
@@ -5478,12 +5478,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Start or stop DNS64 detection and 464xlat according to network state.
networkAgent.clatd.update();
notifyIfacesChangedForNetworkStats();
+ try {
+ networkAgent.networkMonitor().notifyLinkPropertiesChanged(newLp);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
if (networkAgent.everConnected) {
- try {
- networkAgent.networkMonitor().notifyLinkPropertiesChanged();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
}
}
@@ -5711,7 +5711,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkCapabilities prevNc;
synchronized (nai) {
prevNc = nai.networkCapabilities;
- nai.networkCapabilities = newNc;
+ nai.setNetworkCapabilities(newNc);
}
updateUids(nai, prevNc, newNc);
@@ -5726,11 +5726,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
rematchAllNetworksAndRequests(nai, oldScore);
- try {
- nai.networkMonitor().notifyNetworkCapabilitiesChanged();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
@@ -5989,11 +5984,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (capabilitiesChanged) {
- try {
- nai.networkMonitor().notifyNetworkCapabilitiesChanged();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
@@ -6402,7 +6392,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (networkAgent.networkMisc.acceptPartialConnectivity) {
networkAgent.networkMonitor().setAcceptPartialConnectivity();
}
- networkAgent.networkMonitor().notifyNetworkConnected();
+ networkAgent.networkMonitor().notifyNetworkConnected(
+ networkAgent.linkProperties, networkAgent.networkCapabilities);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/services/core/java/com/android/server/ExplicitHealthCheckController.java b/services/core/java/com/android/server/ExplicitHealthCheckController.java
index f50364d70bc7..164837ab98dd 100644
--- a/services/core/java/com/android/server/ExplicitHealthCheckController.java
+++ b/services/core/java/com/android/server/ExplicitHealthCheckController.java
@@ -39,7 +39,9 @@ import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -50,10 +52,22 @@ class ExplicitHealthCheckController {
private static final String TAG = "ExplicitHealthCheckController";
private final Object mLock = new Object();
private final Context mContext;
- @GuardedBy("mLock") @Nullable private StateCallback mStateCallback;
+
+ // Called everytime the service is connected, so the watchdog can sync it's state with
+ // the health check service. In practice, should never be null after it has been #setEnabled.
+ @GuardedBy("mLock") @Nullable private Runnable mOnConnected;
+ // Called everytime a package passes the health check, so the watchdog is notified of the
+ // passing check. In practice, should never be null after it has been #setEnabled.
+ @GuardedBy("mLock") @Nullable private Consumer<String> mPassedConsumer;
+ // Actual binder object to the explicit health check service.
@GuardedBy("mLock") @Nullable private IExplicitHealthCheckService mRemoteService;
- @GuardedBy("mLock") @Nullable private ServiceConnection mConnection;
+ // Cache for packages supporting explicit health checks. This cache should not change while
+ // the health check service is running.
@GuardedBy("mLock") @Nullable private List<String> mSupportedPackages;
+ // Connection to the explicit health check service, necessary to unbind
+ @GuardedBy("mLock") @Nullable private ServiceConnection mConnection;
+ // Bind state of the explicit health check service.
+ @GuardedBy("mLock") private boolean mEnabled;
ExplicitHealthCheckController(Context context) {
mContext = context;
@@ -61,28 +75,40 @@ class ExplicitHealthCheckController {
/**
* Requests an explicit health check for {@code packageName}.
- * After this request, the callback registered on {@link startService} can receive explicit
+ * After this request, the callback registered on {@link #setCallbacks} can receive explicit
* health check passed results.
*
* @throws IllegalStateException if the service is not started
*/
public void request(String packageName) throws RemoteException {
synchronized (mLock) {
+ if (!mEnabled) {
+ return;
+ }
+
enforceServiceReadyLocked();
+
+ Slog.i(TAG, "Requesting health check for package " + packageName);
mRemoteService.request(packageName);
}
}
/**
* Cancels all explicit health checks for {@code packageName}.
- * After this request, the callback registered on {@link startService} can no longer receive
+ * After this request, the callback registered on {@link #setCallbacks} can no longer receive
* explicit health check passed results.
*
* @throws IllegalStateException if the service is not started
*/
public void cancel(String packageName) throws RemoteException {
synchronized (mLock) {
+ if (!mEnabled) {
+ return;
+ }
+
enforceServiceReadyLocked();
+
+ Slog.i(TAG, "Cancelling health check for package " + packageName);
mRemoteService.cancel(packageName);
}
}
@@ -95,13 +121,21 @@ class ExplicitHealthCheckController {
*/
public void getSupportedPackages(Consumer<List<String>> consumer) throws RemoteException {
synchronized (mLock) {
+ if (!mEnabled) {
+ consumer.accept(Collections.emptyList());
+ return;
+ }
+
enforceServiceReadyLocked();
+
if (mSupportedPackages == null) {
+ Slog.d(TAG, "Getting health check supported packages");
mRemoteService.getSupportedPackages(new RemoteCallback(result -> {
mSupportedPackages = result.getStringArrayList(EXTRA_SUPPORTED_PACKAGES);
consumer.accept(mSupportedPackages);
}));
} else {
+ Slog.d(TAG, "Getting cached health check supported packages");
consumer.accept(mSupportedPackages);
}
}
@@ -115,95 +149,113 @@ class ExplicitHealthCheckController {
*/
public void getRequestedPackages(Consumer<List<String>> consumer) throws RemoteException {
synchronized (mLock) {
+ if (!mEnabled) {
+ consumer.accept(Collections.emptyList());
+ return;
+ }
+
enforceServiceReadyLocked();
+
+ Slog.d(TAG, "Getting health check requested packages");
mRemoteService.getRequestedPackages(new RemoteCallback(
result -> consumer.accept(
result.getStringArrayList(EXTRA_REQUESTED_PACKAGES))));
}
}
+ /** Enables or disables explicit health checks. */
+ public void setEnabled(boolean enabled) {
+ synchronized (mLock) {
+ if (enabled == mEnabled) {
+ return;
+ }
+
+ Slog.i(TAG, "Setting explicit health checks enabled " + enabled);
+ mEnabled = enabled;
+ if (enabled) {
+ bindService();
+ } else {
+ unbindService();
+ }
+ }
+ }
+
/**
- * Starts the explicit health check service.
- *
- * @param stateCallback will receive important state changes changes
- * @param passedConsumer will accept packages that pass explicit health checks
- *
- * @throws IllegalStateException if the service is already started
+ * Sets callbacks to listen to important events from the controller.
+ * Should be called at initialization.
*/
- public void startService(StateCallback stateCallback, Consumer<String> passedConsumer) {
+ public void setCallbacks(Runnable onConnected, Consumer<String> passedConsumer) {
+ Preconditions.checkNotNull(onConnected);
+ Preconditions.checkNotNull(passedConsumer);
+ mOnConnected = onConnected;
+ mPassedConsumer = passedConsumer;
+ }
+
+ /** Binds to the explicit health check service. */
+ private void bindService() {
synchronized (mLock) {
if (mRemoteService != null) {
- throw new IllegalStateException("Explicit health check service already started.");
+ return;
+ }
+ ComponentName component = getServiceComponentNameLocked();
+ if (component == null) {
+ Slog.wtf(TAG, "Explicit health check service not found");
+ return;
}
- mStateCallback = stateCallback;
+
+ Intent intent = new Intent();
+ intent.setComponent(component);
+ // TODO: Fix potential race conditions during mConnection state transitions.
+ // E.g after #onServiceDisconected, the mRemoteService object is invalid until
+ // we get an #onServiceConnected.
mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mLock) {
- mRemoteService = IExplicitHealthCheckService.Stub.asInterface(service);
- try {
- mRemoteService.setCallback(new RemoteCallback(result -> {
- String packageName =
- result.getString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE);
- if (!TextUtils.isEmpty(packageName)) {
- passedConsumer.accept(packageName);
- } else {
- Slog.w(TAG, "Empty package passed explicit health check?");
- }
- }));
- mStateCallback.onStart();
- Slog.i(TAG, "Explicit health check service is connected " + name);
- } catch (RemoteException e) {
- Slog.wtf(TAG, "Coud not setCallback on explicit health check service");
- }
- }
+ initState(service);
+ Slog.i(TAG, "Explicit health check service is connected " + name);
}
@Override
@MainThread
public void onServiceDisconnected(ComponentName name) {
- resetState();
+ // Service crashed or process was killed, #onServiceConnected will be called.
+ // Don't need to re-bind.
Slog.i(TAG, "Explicit health check service is disconnected " + name);
}
@Override
public void onBindingDied(ComponentName name) {
- resetState();
+ // Application hosting service probably got updated
+ // Need to re-bind.
+ synchronized (mLock) {
+ if (mEnabled) {
+ unbindService();
+ bindService();
+ }
+ }
Slog.i(TAG, "Explicit health check service binding is dead " + name);
}
@Override
public void onNullBinding(ComponentName name) {
- resetState();
- Slog.i(TAG, "Explicit health check service binding is null " + name);
+ // Should never happen. Service returned null from #onBind.
+ Slog.wtf(TAG, "Explicit health check service binding is null?? " + name);
}
};
- ComponentName component = getServiceComponentNameLocked();
- if (component != null) {
- Intent intent = new Intent();
- intent.setComponent(component);
- mContext.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE,
- UserHandle.of(UserHandle.USER_SYSTEM));
- }
+ Slog.i(TAG, "Binding to explicit health service");
+ mContext.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE,
+ UserHandle.of(UserHandle.USER_SYSTEM));
}
}
- // TODO: Differentiate between expected vs unexpected stop?
- /** Callback to receive important {@link ExplicitHealthCheckController} state changes. */
- abstract static class StateCallback {
- /** The controller is ready and we can request explicit health checks for packages */
- public void onStart() {}
-
- /** The controller is not ready and we cannot request explicit health checks for packages */
- public void onStop() {}
- }
-
- /** Stops the explicit health check service. */
- public void stopService() {
+ /** Unbinds the explicit health check service. */
+ private void unbindService() {
synchronized (mLock) {
if (mRemoteService != null) {
+ Slog.i(TAG, "Unbinding from explicit health service");
mContext.unbindService(mConnection);
+ mRemoteService = null;
}
}
}
@@ -247,19 +299,41 @@ class ExplicitHealthCheckController {
return name;
}
- private void resetState() {
+ private void initState(IBinder service) {
synchronized (mLock) {
- mStateCallback.onStop();
- mStateCallback = null;
mSupportedPackages = null;
- mRemoteService = null;
- mConnection = null;
+ mRemoteService = IExplicitHealthCheckService.Stub.asInterface(service);
+ try {
+ mRemoteService.setCallback(new RemoteCallback(result -> {
+ String packageName = result.getString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE);
+ if (!TextUtils.isEmpty(packageName)) {
+ synchronized (mLock) {
+ if (mPassedConsumer == null) {
+ Slog.w(TAG, "Health check passed for package " + packageName
+ + "but no consumer registered.");
+ } else {
+ mPassedConsumer.accept(packageName);
+ }
+ }
+ } else {
+ Slog.w(TAG, "Empty package passed explicit health check?");
+ }
+ }));
+ if (mOnConnected == null) {
+ Slog.w(TAG, "Health check service connected but no runnable registered.");
+ } else {
+ mOnConnected.run();
+ }
+ } catch (RemoteException e) {
+ Slog.wtf(TAG, "Could not setCallback on explicit health check service");
+ }
}
}
@GuardedBy("mLock")
private void enforceServiceReadyLocked() {
if (mRemoteService == null) {
+ // TODO: Try to bind to service
throw new IllegalStateException("Explicit health check service not ready");
}
}
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 660109cf6114..2ba4d975a6b0 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -26,11 +26,12 @@ import android.content.pm.VersionedPackage;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
-import android.util.Log;
import android.util.Slog;
import android.util.Xml;
@@ -54,10 +55,12 @@ import java.io.InputStream;
import java.lang.annotation.Retention;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
/**
* Monitors the health of packages on the system and notifies interested observers when packages
@@ -84,10 +87,10 @@ public class PackageWatchdog {
private final Object mLock = new Object();
// System server context
private final Context mContext;
- // Handler to run package cleanup runnables
- private final Handler mTimerHandler;
- // Handler for processing IO and observer actions
- private final Handler mWorkerHandler;
+ // Handler to run short running tasks
+ private final Handler mShortTaskHandler;
+ // Handler for processing IO and long running tasks
+ private final Handler mLongTaskHandler;
// Contains (observer-name -> observer-handle) that have ever been registered from
// previous boots. Observers with all packages expired are periodically pruned.
// It is saved to disk on system shutdown and repouplated on startup so it survives reboots.
@@ -97,6 +100,12 @@ public class PackageWatchdog {
private final AtomicFile mPolicyFile;
// Runnable to prune monitored packages that have expired
private final Runnable mPackageCleanup;
+ private final ExplicitHealthCheckController mHealthCheckController;
+ // Flag to control whether explicit health checks are supported or not
+ @GuardedBy("mLock")
+ private boolean mIsHealthCheckEnabled = true;
+ @GuardedBy("mLock")
+ private boolean mIsPackagesReady;
// Last SystemClock#uptimeMillis a package clean up was executed.
// 0 if mPackageCleanup not running.
private long mUptimeAtLastRescheduleMs;
@@ -104,32 +113,30 @@ public class PackageWatchdog {
// 0 if mPackageCleanup not running.
private long mDurationAtLastReschedule;
- // TODO(b/120598832): Remove redundant context param
private PackageWatchdog(Context context) {
- mContext = context;
- mPolicyFile = new AtomicFile(new File(new File(Environment.getDataDirectory(), "system"),
- "package-watchdog.xml"));
- mTimerHandler = new Handler(Looper.myLooper());
- mWorkerHandler = BackgroundThread.getHandler();
- mPackageCleanup = this::rescheduleCleanup;
- loadFromFile();
+ // Needs to be constructed inline
+ this(context, new AtomicFile(
+ new File(new File(Environment.getDataDirectory(), "system"),
+ "package-watchdog.xml")),
+ new Handler(Looper.myLooper()), BackgroundThread.getHandler(),
+ new ExplicitHealthCheckController(context));
}
/**
- * Creates a PackageWatchdog for testing that uses the same {@code looper} for all handlers
- * and creates package-watchdog.xml in an apps data directory.
+ * Creates a PackageWatchdog that allows injecting dependencies.
*/
@VisibleForTesting
- PackageWatchdog(Context context, Looper looper) {
+ PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler,
+ Handler longTaskHandler, ExplicitHealthCheckController controller) {
mContext = context;
- mPolicyFile = new AtomicFile(new File(context.getFilesDir(), "package-watchdog.xml"));
- mTimerHandler = new Handler(looper);
- mWorkerHandler = mTimerHandler;
+ mPolicyFile = policyFile;
+ mShortTaskHandler = shortTaskHandler;
+ mLongTaskHandler = longTaskHandler;
mPackageCleanup = this::rescheduleCleanup;
+ mHealthCheckController = controller;
loadFromFile();
}
-
/** Creates or gets singleton instance of PackageWatchdog. */
public static PackageWatchdog getInstance(Context context) {
synchronized (PackageWatchdog.class) {
@@ -141,6 +148,20 @@ public class PackageWatchdog {
}
/**
+ * Called during boot to notify when packages are ready on the device so we can start
+ * binding.
+ */
+ public void onPackagesReady() {
+ synchronized (mLock) {
+ mIsPackagesReady = true;
+ mHealthCheckController.setCallbacks(this::updateHealthChecks,
+ packageName -> onHealthCheckPassed(packageName));
+ // Controller is disabled at creation until here where we may enable it
+ mHealthCheckController.setEnabled(mIsHealthCheckEnabled);
+ }
+ }
+
+ /**
* Registers {@code observer} to listen for package failures
*
* <p>Observers are expected to call this on boot. It does not specify any packages but
@@ -163,40 +184,63 @@ public class PackageWatchdog {
* Starts observing the health of the {@code packages} for {@code observer} and notifies
* {@code observer} of any package failures within the monitoring duration.
*
- * <p>If monitoring a package with {@code withExplicitHealthCheck}, at the end of the monitoring
- * duration if {@link #onExplicitHealthCheckPassed} was never called,
+ * <p>If monitoring a package supporting explicit health check, at the end of the monitoring
+ * duration if {@link #onHealthCheckPassed} was never called,
* {@link PackageHealthObserver#execute} will be called as if the package failed.
*
* <p>If {@code observer} is already monitoring a package in {@code packageNames},
* the monitoring window of that package will be reset to {@code durationMs} and the health
- * check state will be reset to a default depending on {@code withExplictHealthCheck}.
+ * check state will be reset to a default depending on if the package is contained in
+ * {@link mPackagesWithExplicitHealthCheckEnabled}.
*
* @throws IllegalArgumentException if {@code packageNames} is empty
* or {@code durationMs} is less than 1
*/
- public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
- long durationMs, boolean withExplicitHealthCheck) {
- if (packageNames.isEmpty() || durationMs < 1) {
+ public void startObservingHealth(PackageHealthObserver observer, List<String> packages,
+ long durationMs) {
+ if (packages.isEmpty() || durationMs < 1) {
throw new IllegalArgumentException("Observation not started, no packages specified"
+ "or invalid duration");
}
+ if (!mIsPackagesReady) {
+ // TODO: Queue observation requests when packages are not ready
+ Slog.w(TAG, "Attempt to observe when packages not ready");
+ return;
+ }
+
+ try {
+ Slog.i(TAG, "Getting packages supporting explicit health check");
+ mHealthCheckController.getSupportedPackages(supportedPackages ->
+ startObservingInner(observer, packages, durationMs, supportedPackages));
+ } catch (RemoteException e) {
+ Slog.wtf(TAG, "Failed to fetch supported explicit health check packages");
+ }
+ }
+
+ private void startObservingInner(PackageHealthObserver observer,
+ List<String> packageNames, long durationMs,
+ List<String> healthCheckSupportedPackages) {
+ Slog.i(TAG, "Start observing packages " + packageNames
+ + ". Explicit health check supported packages " + healthCheckSupportedPackages);
List<MonitoredPackage> packages = new ArrayList<>();
for (int i = 0; i < packageNames.size(); i++) {
- // When observing packages withExplicitHealthCheck,
- // MonitoredPackage#mHasExplicitHealthCheckPassed will be false initially.
- packages.add(new MonitoredPackage(packageNames.get(i), durationMs,
- !withExplicitHealthCheck));
+ String packageName = packageNames.get(i);
+ boolean shouldEnableHealthCheck = healthCheckSupportedPackages.contains(packageName);
+ // If we should enable explicit health check for a package,
+ // MonitoredPackage#mHasHealthCheckPassed will be false
+ // until PackageWatchdog#onHealthCheckPassed
+ packages.add(new MonitoredPackage(packageName, durationMs, !shouldEnableHealthCheck));
}
synchronized (mLock) {
ObserverInternal oldObserver = mAllObservers.get(observer.getName());
if (oldObserver == null) {
- Slog.d(TAG, observer.getName() + " started monitoring health of packages "
- + packageNames);
+ Slog.d(TAG, observer.getName() + " started monitoring health "
+ + "of packages " + packageNames);
mAllObservers.put(observer.getName(),
new ObserverInternal(observer.getName(), packages));
} else {
- Slog.d(TAG, observer.getName() + " added the following packages to monitor "
- + packageNames);
+ Slog.d(TAG, observer.getName() + " added the following "
+ + "packages to monitor " + packageNames);
oldObserver.updatePackages(packages);
}
}
@@ -204,9 +248,97 @@ public class PackageWatchdog {
// Always reschedule because we may need to expire packages
// earlier than we are already scheduled for
rescheduleCleanup();
+ updateHealthChecks();
saveToFileAsync();
}
+ private void requestCheck(String packageName) {
+ try {
+ Slog.d(TAG, "Requesting explicit health check for " + packageName);
+ mHealthCheckController.request(packageName);
+ } catch (RemoteException e) {
+ Slog.wtf(TAG, "Failed to request explicit health check for " + packageName, e);
+ }
+ }
+
+ private void cancelCheck(String packageName) {
+ try {
+ Slog.d(TAG, "Cancelling explicit health check for " + packageName);
+ mHealthCheckController.cancel(packageName);
+ } catch (RemoteException e) {
+ Slog.wtf(TAG, "Failed to cancel explicit health check for " + packageName, e);
+ }
+ }
+
+ private void actOnDifference(Collection<String> collection1, Collection<String> collection2,
+ Consumer<String> action) {
+ Iterator<String> iterator = collection1.iterator();
+ while (iterator.hasNext()) {
+ String packageName = iterator.next();
+ if (!collection2.contains(packageName)) {
+ action.accept(packageName);
+ }
+ }
+ }
+
+ private void updateChecksInner(List<String> supportedPackages,
+ List<String> previousRequestedPackages) {
+ boolean shouldUpdateFile = false;
+
+ synchronized (mLock) {
+ Slog.i(TAG, "Updating explicit health checks. Supported packages: " + supportedPackages
+ + ". Requested packages: " + previousRequestedPackages);
+ Set<String> newRequestedPackages = new ArraySet<>();
+ Iterator<ObserverInternal> oit = mAllObservers.values().iterator();
+ while (oit.hasNext()) {
+ ObserverInternal observer = oit.next();
+ Iterator<MonitoredPackage> pit =
+ observer.mPackages.values().iterator();
+ while (pit.hasNext()) {
+ MonitoredPackage monitoredPackage = pit.next();
+ String packageName = monitoredPackage.mName;
+ if (!monitoredPackage.mHasPassedHealthCheck) {
+ if (supportedPackages.contains(packageName)) {
+ newRequestedPackages.add(packageName);
+ } else {
+ shouldUpdateFile = true;
+ monitoredPackage.mHasPassedHealthCheck = true;
+ }
+ }
+ }
+ }
+ // TODO: Support ending the binding if newRequestedPackages is empty.
+ // Will have to re-bind when we #startObservingHealth.
+
+ // Cancel packages no longer requested
+ actOnDifference(previousRequestedPackages, newRequestedPackages, p -> cancelCheck(p));
+ // Request packages not yet requested
+ actOnDifference(newRequestedPackages, previousRequestedPackages, p -> requestCheck(p));
+ }
+
+ if (shouldUpdateFile) {
+ saveToFileAsync();
+ }
+ }
+
+ private void updateHealthChecks() {
+ mShortTaskHandler.post(() -> {
+ try {
+ Slog.i(TAG, "Updating explicit health checks for all available packages");
+ mHealthCheckController.getSupportedPackages(supported -> {
+ try {
+ mHealthCheckController.getRequestedPackages(
+ requested -> updateChecksInner(supported, requested));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get requested health check packages", e);
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get supported health check package", e);
+ }
+ });
+ }
+
/**
* Unregisters {@code observer} from listening to package failure.
* Additionally, this stops observing any packages that may have previously been observed
@@ -250,7 +382,7 @@ public class PackageWatchdog {
* <p>This method could be called frequently if there is a severe problem on the device.
*/
public void onPackageFailure(List<VersionedPackage> packages) {
- mWorkerHandler.post(() -> {
+ mLongTaskHandler.post(() -> {
synchronized (mLock) {
if (mAllObservers.isEmpty()) {
return;
@@ -286,49 +418,32 @@ public class PackageWatchdog {
});
}
- /**
- * Updates the observers monitoring {@code packageName} that explicit health check has passed.
- *
- * <p> This update is strictly for registered observers at the time of the call
- * Observers that register after this signal will have no knowledge of prior signals and will
- * effectively behave as if the explicit health check hasn't passed for {@code packageName}.
- *
- * <p> {@code packageName} can still be considered failed if reported by
- * {@link #onPackageFailure} before the package expires.
- *
- * <p> Triggered by components outside the system server when they are fully functional after an
- * update.
- */
- public void onExplicitHealthCheckPassed(String packageName) {
- Slog.i(TAG, "Health check passed for package: " + packageName);
- boolean shouldUpdateFile = false;
- synchronized (mLock) {
- for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) {
- ObserverInternal observer = mAllObservers.valueAt(observerIdx);
- MonitoredPackage monitoredPackage = observer.mPackages.get(packageName);
- if (monitoredPackage != null && !monitoredPackage.mHasPassedHealthCheck) {
- monitoredPackage.mHasPassedHealthCheck = true;
- shouldUpdateFile = true;
- }
- }
- }
- if (shouldUpdateFile) {
- saveToFileAsync();
- }
- }
-
// TODO(b/120598832): Optimize write? Maybe only write a separate smaller file?
// This currently adds about 7ms extra to shutdown thread
/** Writes the package information to file during shutdown. */
public void writeNow() {
if (!mAllObservers.isEmpty()) {
- mWorkerHandler.removeCallbacks(this::saveToFile);
+ mLongTaskHandler.removeCallbacks(this::saveToFile);
pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastRescheduleMs);
saveToFile();
Slog.i(TAG, "Last write to update package durations");
}
}
+ // TODO(b/120598832): Set depending on DeviceConfig flag
+ /**
+ * Enables or disables explicit health checks.
+ * <p> If explicit health checks are enabled, the health check service is started.
+ * <p> If explicit health checks are disabled, pending explicit health check requests are
+ * passed and the health check service is stopped.
+ */
+ public void setExplicitHealthCheckEnabled(boolean enabled) {
+ synchronized (mLock) {
+ mIsHealthCheckEnabled = enabled;
+ mHealthCheckController.setEnabled(enabled);
+ }
+ }
+
/** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */
@Retention(SOURCE)
@IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_NONE,
@@ -371,6 +486,37 @@ public class PackageWatchdog {
String getName();
}
+ /**
+ * Updates the observers monitoring {@code packageName} that explicit health check has passed.
+ *
+ * <p> This update is strictly for registered observers at the time of the call
+ * Observers that register after this signal will have no knowledge of prior signals and will
+ * effectively behave as if the explicit health check hasn't passed for {@code packageName}.
+ *
+ * <p> {@code packageName} can still be considered failed if reported by
+ * {@link #onPackageFailure} before the package expires.
+ *
+ * <p> Triggered by components outside the system server when they are fully functional after an
+ * update.
+ */
+ private void onHealthCheckPassed(String packageName) {
+ Slog.i(TAG, "Health check passed for package: " + packageName);
+ boolean shouldUpdateFile = false;
+ synchronized (mLock) {
+ for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) {
+ ObserverInternal observer = mAllObservers.valueAt(observerIdx);
+ MonitoredPackage monitoredPackage = observer.mPackages.get(packageName);
+ if (monitoredPackage != null && !monitoredPackage.mHasPassedHealthCheck) {
+ monitoredPackage.mHasPassedHealthCheck = true;
+ shouldUpdateFile = true;
+ }
+ }
+ }
+ if (shouldUpdateFile) {
+ saveToFileAsync();
+ }
+ }
+
/** Reschedules handler to prune expired packages from observers. */
private void rescheduleCleanup() {
synchronized (mLock) {
@@ -393,8 +539,8 @@ public class PackageWatchdog {
|| nextDurationToScheduleMs < remainingDurationMs) {
// First schedule or an earlier reschedule
pruneObservers(elapsedDurationMs);
- mTimerHandler.removeCallbacks(mPackageCleanup);
- mTimerHandler.postDelayed(mPackageCleanup, nextDurationToScheduleMs);
+ mShortTaskHandler.removeCallbacks(mPackageCleanup);
+ mShortTaskHandler.postDelayed(mPackageCleanup, nextDurationToScheduleMs);
mDurationAtLastReschedule = nextDurationToScheduleMs;
mUptimeAtLastRescheduleMs = uptimeMs;
}
@@ -437,7 +583,7 @@ public class PackageWatchdog {
List<MonitoredPackage> failedPackages =
observer.updateMonitoringDurations(elapsedMs);
if (!failedPackages.isEmpty()) {
- onExplicitHealthCheckFailed(observer, failedPackages);
+ onHealthCheckFailed(observer, failedPackages);
}
if (observer.mPackages.isEmpty()) {
Slog.i(TAG, "Discarding observer " + observer.mName + ". All packages expired");
@@ -445,12 +591,13 @@ public class PackageWatchdog {
}
}
}
+ updateHealthChecks();
saveToFileAsync();
}
- private void onExplicitHealthCheckFailed(ObserverInternal observer,
+ private void onHealthCheckFailed(ObserverInternal observer,
List<MonitoredPackage> failedPackages) {
- mWorkerHandler.post(() -> {
+ mLongTaskHandler.post(() -> {
synchronized (mLock) {
PackageHealthObserver registeredObserver = observer.mRegisteredObserver;
if (registeredObserver != null) {
@@ -458,6 +605,7 @@ public class PackageWatchdog {
for (int i = 0; i < failedPackages.size(); i++) {
String packageName = failedPackages.get(i).mName;
long versionCode = 0;
+ Slog.i(TAG, "Explicit health check failed for package " + packageName);
try {
versionCode = pm.getPackageInfo(
packageName, 0 /* flags */).getLongVersionCode();
@@ -498,7 +646,7 @@ public class PackageWatchdog {
} catch (FileNotFoundException e) {
// Nothing to monitor
} catch (IOException | NumberFormatException | XmlPullParserException e) {
- Log.wtf(TAG, "Unable to read monitored packages, deleting file", e);
+ Slog.wtf(TAG, "Unable to read monitored packages, deleting file", e);
mPolicyFile.delete();
} finally {
IoUtils.closeQuietly(infile);
@@ -542,8 +690,8 @@ public class PackageWatchdog {
}
private void saveToFileAsync() {
- mWorkerHandler.removeCallbacks(this::saveToFile);
- mWorkerHandler.post(this::saveToFile);
+ mLongTaskHandler.removeCallbacks(this::saveToFile);
+ mLongTaskHandler.post(this::saveToFile);
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 1751856066fb..b759dd4e8122 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -20,8 +20,10 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK
import android.app.ActivityThread;
import android.content.ContentResolver;
+import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.Build;
import android.os.Handler;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.OnPropertyChangedListener;
@@ -38,6 +40,7 @@ import java.io.PrintWriter;
* Settings constants that can modify the activity manager's behavior.
*/
final class ActivityManagerConstants extends ContentObserver {
+ private static final String TAG = "ActivityManagerConstants";
// Key names stored in the settings value.
private static final String KEY_BACKGROUND_SETTLE_TIME = "background_settle_time";
@@ -272,6 +275,16 @@ final class ActivityManagerConstants extends ContentObserver {
// memory trimming.
public int CUR_TRIM_CACHED_PROCESSES;
+ private static final long MIN_AUTOMATIC_HEAP_DUMP_PSS_THRESHOLD_BYTES = 100 * 1024; // 100 KB
+
+ private final boolean mSystemServerAutomaticHeapDumpEnabled;
+
+ /** Package to report to when the memory usage exceeds the limit. */
+ private final String mSystemServerAutomaticHeapDumpPackageName;
+
+ /** Byte limit for dump heap monitoring. */
+ private long mSystemServerAutomaticHeapDumpPssThresholdBytes;
+
private static final Uri ACTIVITY_MANAGER_CONSTANTS_URI = Settings.Global.getUriFor(
Settings.Global.ACTIVITY_MANAGER_CONSTANTS);
@@ -286,6 +299,9 @@ final class ActivityManagerConstants extends ContentObserver {
Settings.Global.getUriFor(
Settings.Global.BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST);
+ private static final Uri ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI =
+ Settings.Global.getUriFor(Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS);
+
private final OnPropertyChangedListener mOnDeviceConfigChangedListener =
new OnPropertyChangedListener() {
@Override
@@ -296,9 +312,17 @@ final class ActivityManagerConstants extends ContentObserver {
}
};
- public ActivityManagerConstants(ActivityManagerService service, Handler handler) {
+ ActivityManagerConstants(Context context, ActivityManagerService service, Handler handler) {
super(handler);
mService = service;
+ mSystemServerAutomaticHeapDumpEnabled = Build.IS_DEBUGGABLE
+ && context.getResources().getBoolean(
+ com.android.internal.R.bool.config_debugEnableAutomaticSystemServerHeapDumps);
+ mSystemServerAutomaticHeapDumpPackageName = context.getPackageName();
+ mSystemServerAutomaticHeapDumpPssThresholdBytes = Math.max(
+ MIN_AUTOMATIC_HEAP_DUMP_PSS_THRESHOLD_BYTES,
+ context.getResources().getInteger(
+ com.android.internal.R.integer.config_debugSystemServerPssThresholdBytes));
}
public void start(ContentResolver resolver) {
@@ -308,10 +332,17 @@ final class ActivityManagerConstants extends ContentObserver {
mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_ENABLED_URI, false, this);
mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST_URI,
false, this);
+ if (mSystemServerAutomaticHeapDumpEnabled) {
+ mResolver.registerContentObserver(ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI,
+ false, this);
+ }
updateConstants();
updateActivityStartsLoggingEnabled();
updateBackgroundActivityStartsEnabled();
updateBackgroundActivityStartsPackageNamesWhitelist();
+ if (mSystemServerAutomaticHeapDumpEnabled) {
+ updateEnableAutomaticSystemServerHeapDumps();
+ }
DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
ActivityThread.currentApplication().getMainExecutor(),
mOnDeviceConfigChangedListener);
@@ -343,6 +374,8 @@ final class ActivityManagerConstants extends ContentObserver {
updateBackgroundActivityStartsEnabled();
} else if (BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST_URI.equals(uri)) {
updateBackgroundActivityStartsPackageNamesWhitelist();
+ } else if (ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI.equals(uri)) {
+ updateEnableAutomaticSystemServerHeapDumps();
}
}
@@ -450,6 +483,24 @@ final class ActivityManagerConstants extends ContentObserver {
mPackageNamesWhitelistedForBgActivityStarts = newSet;
}
+ private void updateEnableAutomaticSystemServerHeapDumps() {
+ if (!mSystemServerAutomaticHeapDumpEnabled) {
+ Slog.wtf(TAG,
+ "updateEnableAutomaticSystemServerHeapDumps called when leak detection "
+ + "disabled");
+ return;
+ }
+ // Monitoring is on by default, so if the setting hasn't been set by the user,
+ // monitoring should be on.
+ final boolean enabled = Settings.Global.getInt(mResolver,
+ Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS, 1) == 1;
+
+ // Setting the threshold to 0 stops the checking.
+ final long threshold = enabled ? mSystemServerAutomaticHeapDumpPssThresholdBytes : 0;
+ mService.setDumpHeapDebugLimit(null, 0, threshold,
+ mSystemServerAutomaticHeapDumpPackageName);
+ }
+
private void updateMaxCachedProcesses() {
String maxCachedProcessesFlag = DeviceConfig.getProperty(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MAX_CACHED_PROCESSES);
@@ -460,7 +511,7 @@ final class ActivityManagerConstants extends ContentObserver {
: mOverrideMaxCachedProcesses;
} catch (NumberFormatException e) {
// Bad flag value from Phenotype, revert to default.
- Slog.e("ActivityManagerConstants",
+ Slog.e(TAG,
"Unable to parse flag for max_cached_processes: " + maxCachedProcessesFlag, e);
CUR_MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d4bff007462a..3eb7c0374a01 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2135,6 +2135,8 @@ public class ActivityManagerService extends IActivityManager.Stub
mService.mServices.systemServicesReady();
} else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mService.startBroadcastObservers();
+ } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ mService.mPackageWatchdog.onPackagesReady();
}
}
@@ -2288,7 +2290,8 @@ public class ActivityManagerService extends IActivityManager.Stub
mBatteryStatsService = null;
mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null;
mHandlerThread = handlerThread;
- mConstants = hasHandlerThread ? new ActivityManagerConstants(this, mHandler) : null;
+ mConstants = hasHandlerThread
+ ? new ActivityManagerConstants(mContext, this, mHandler) : null;
final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
mProcessList.init(this, activeUids);
mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
@@ -2336,7 +2339,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mProcStartHandlerThread.start();
mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
- mConstants = new ActivityManagerConstants(this, mHandler);
+ mConstants = new ActivityManagerConstants(mContext, this, mHandler);
final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */);
mProcessList.init(this, activeUids);
mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index cc901821030a..8a462dabd14c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -16,7 +16,6 @@
package com.android.server.am;
-import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
@@ -306,9 +305,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
mSamplingInterval = 0;
mAutoStop = false;
mStreaming = false;
- mUserId = mInternal.mUserController.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), defUser, false, ALLOW_FULL_ONLY,
- "ActivityManagerShellCommand", null);
+ mUserId = defUser;
mDisplayId = INVALID_DISPLAY;
mWindowingMode = WINDOWING_MODE_UNDEFINED;
mActivityType = ACTIVITY_TYPE_UNDEFINED;
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index c385991e1c7b..fe762c06458a 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -39,12 +39,16 @@ import android.hardware.face.FaceManager;
import android.hardware.face.IFaceService;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
+import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SELinux;
import android.os.UserHandle;
import android.os.UserManager;
+import android.service.restricted_image.RestrictedImagesDumpProto;
+import android.service.restricted_image.RestrictedImageProto;
+import android.service.restricted_image.RestrictedImageSetProto;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -281,7 +285,9 @@ public class FaceService extends BiometricServiceBase {
final long ident = Binder.clearCallingIdentity();
try {
- if (args.length > 0 && "--proto".equals(args[0])) {
+ if (args.length == 1 && "--restricted_image".equals(args[0])) {
+ dumpRestrictedImage(fd);
+ } else if (args.length > 0 && "--proto".equals(args[0])) {
dumpProto(fd);
} else {
dumpInternal(pw);
@@ -1063,4 +1069,74 @@ public class FaceService extends BiometricServiceBase {
mPerformanceMap.clear();
mCryptoPerformanceMap.clear();
}
+
+ private void dumpRestrictedImage(FileDescriptor fd) {
+ // WARNING: CDD restricts image data from leaving TEE unencrypted on
+ // production devices:
+ // [C-1-10] MUST not allow unencrypted access to identifiable biometric
+ // data or any data derived from it (such as embeddings) to the
+ // Application Processor outside the context of the TEE.
+ // As such, this API should only be enabled for testing purposes on
+ // engineering and userdebug builds. All modules in the software stack
+ // MUST enforce final build products do NOT have this functionality.
+ // Additionally, the following check MUST NOT be removed.
+ if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
+ return;
+ }
+
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ final long setToken = proto.start(RestrictedImagesDumpProto.SETS);
+
+ // Name of the service
+ proto.write(RestrictedImageSetProto.CATEGORY, "face");
+
+ // Individual images
+ for (int i = 0; i < 5; i++) {
+ final long imageToken = proto.start(RestrictedImageSetProto.IMAGES);
+ proto.write(RestrictedImageProto.MIME_TYPE, "image/png");
+ proto.write(RestrictedImageProto.IMAGE_DATA, new byte[] {
+ // png image data
+ -119, 80, 78, 71, 13, 10, 26, 10,
+ 0, 0, 0, 13, 73, 72, 68, 82,
+ 0, 0, 0, 100, 0, 0, 0, 100,
+ 1, 3, 0, 0, 0, 74, 44, 7,
+ 23, 0, 0, 0, 4, 103, 65, 77,
+ 65, 0, 0, -79, -113, 11, -4, 97,
+ 5, 0, 0, 0, 1, 115, 82, 71,
+ 66, 0, -82, -50, 28, -23, 0, 0,
+ 0, 6, 80, 76, 84, 69, -1, -1,
+ -1, 0, 0, 0, 85, -62, -45, 126,
+ 0, 0, 0, -115, 73, 68, 65, 84,
+ 56, -53, -19, -46, -79, 17, -128, 32,
+ 12, 5, -48, 120, 22, -106, -116, -32,
+ 40, -84, 101, -121, -93, 57, 10, 35,
+ 88, 82, 112, 126, 3, -60, 104, 6,
+ -112, 70, 127, -59, -69, -53, 29, 33,
+ -127, -24, 79, -49, -52, -15, 41, 36,
+ 34, -105, 85, 124, -14, 88, 27, 6,
+ 28, 68, 1, 82, 62, 22, -95, -108,
+ 55, -95, 40, -9, -110, -12, 98, -107,
+ 76, -41, -105, -62, -50, 111, -60, 46,
+ -14, -4, 24, -89, 42, -103, 16, 63,
+ -72, -11, -15, 48, -62, 102, -44, 102,
+ -73, -56, 56, -21, -128, 92, -70, -124,
+ 117, -46, -67, -77, 82, 80, 121, -44,
+ -56, 116, 93, -45, -90, -5, -29, -24,
+ -83, -75, 52, -34, 55, -22, 102, -21,
+ -105, -124, -23, 71, 87, -7, -25, -59,
+ -100, -73, -92, -122, -7, -109, -49, -80,
+ -89, 0, 0, 0, 0, 73, 69, 78,
+ 68, -82, 66, 96, -126
+ });
+ // proto.write(RestrictedImageProto.METADATA, flattened_protobuf);
+ proto.end(imageToken);
+ }
+
+ // Face service metadata
+ // proto.write(RestrictedImageSetProto.METADATA, flattened_protobuf);
+
+ proto.end(setToken);
+ proto.flush();
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index e3fdbe84a1d3..cfa91314f490 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -30,6 +30,7 @@ import android.net.NetworkState;
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.Messenger;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
@@ -121,7 +122,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// This Network object is always valid.
public final Network network;
public LinkProperties linkProperties;
- // This should only be modified via ConnectivityService.updateCapabilities().
+ // This should only be modified by ConnectivityService, via setNetworkCapabilities().
+ // TODO: make this private with a getter.
public NetworkCapabilities networkCapabilities;
public final NetworkMisc networkMisc;
// Indicates if netd has been told to create this Network. From this point on the appropriate
@@ -279,6 +281,25 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
mNetworkMonitor = networkMonitor;
}
+ /**
+ * Set the NetworkCapabilities on this NetworkAgentInfo. Also attempts to notify NetworkMonitor
+ * of the new capabilities, if NetworkMonitor has been created.
+ *
+ * <p>If {@link NetworkMonitor#notifyNetworkCapabilitiesChanged(NetworkCapabilities)} fails,
+ * the exception is logged but not reported to callers.
+ */
+ public void setNetworkCapabilities(NetworkCapabilities nc) {
+ networkCapabilities = nc;
+ final INetworkMonitor nm = mNetworkMonitor;
+ if (nm != null) {
+ try {
+ nm.notifyNetworkCapabilitiesChanged(nc);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error notifying NetworkMonitor of updated NetworkCapabilities", e);
+ }
+ }
+ }
+
public ConnectivityService connService() {
return mConnService;
}
diff --git a/services/core/java/com/android/server/incident/IncidentCompanionService.java b/services/core/java/com/android/server/incident/IncidentCompanionService.java
index 5c69c1dd3930..9989c1a511ad 100644
--- a/services/core/java/com/android/server/incident/IncidentCompanionService.java
+++ b/services/core/java/com/android/server/incident/IncidentCompanionService.java
@@ -23,7 +23,10 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
import android.os.IIncidentAuthListener;
import android.os.IIncidentCompanion;
import android.os.IIncidentManager;
@@ -49,6 +52,12 @@ public class IncidentCompanionService extends SystemService {
static final String TAG = "IncidentCompanionService";
/**
+ * Dump argument for proxying restricted image dumps to the services
+ * listed in the config.
+ */
+ private static String[] RESTRICTED_IMAGE_DUMP_ARGS = new String[] { "--restricted_image" };
+
+ /**
* The two permissions, for sendBroadcastAsUserMultiplePermissions.
*/
private static final String[] DUMP_AND_USAGE_STATS_PERMISSIONS = new String[] {
@@ -260,7 +269,42 @@ public class IncidentCompanionService extends SystemService {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) {
return;
}
- mPendingReports.dump(fd, writer, args);
+
+ if (args.length == 1 && "--restricted_image".equals(args[0])) {
+ // Does NOT clearCallingIdentity
+ dumpRestrictedImages(fd);
+ } else {
+ // Regular dump
+ mPendingReports.dump(fd, writer, args);
+ }
+ }
+
+ /**
+ * Proxy for the restricted images section.
+ */
+ private void dumpRestrictedImages(FileDescriptor fd) {
+ // Only supported on eng or userdebug.
+ if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
+ return;
+ }
+
+ final Resources res = getContext().getResources();
+ final String[] services = res.getStringArray(
+ com.android.internal.R.array.config_restrictedImagesServices);
+ final int servicesCount = services.length;
+ for (int i = 0; i < servicesCount; i++) {
+ final String name = services[i];
+ Log.d(TAG, "Looking up service " + name);
+ final IBinder service = ServiceManager.getService(name);
+ if (service != null) {
+ Log.d(TAG, "Calling dump on service: " + name);
+ try {
+ service.dump(fd, RESTRICTED_IMAGE_DUMP_ARGS);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "dump --restricted_image of " + name + " threw", ex);
+ }
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 622c49e67967..5abc73eb255a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1809,6 +1809,10 @@ public class InputManagerService extends IInputManager.Stub
}
// Native callback.
+ private void onPointerDownOutsideFocus(IBinder touchedToken) {
+ }
+
+ // Native callback.
private int getVirtualKeyQuietTimeMillis() {
return mContext.getResources().getInteger(
com.android.internal.R.integer.config_virtualKeyQuietTimeMillis);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 75b62cb349af..af58b195a491 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1335,7 +1335,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
case TYPE_WARNING: {
title = res.getText(R.string.data_usage_warning_title);
body = res.getString(R.string.data_usage_warning_body,
- Formatter.formatFileSize(mContext, totalBytes));
+ Formatter.formatFileSize(mContext, totalBytes, Formatter.FLAG_IEC_UNITS));
builder.setSmallIcon(R.drawable.stat_notify_error);
@@ -1383,7 +1383,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
final long overBytes = totalBytes - policy.limitBytes;
body = res.getString(R.string.data_usage_limit_snoozed_body,
- Formatter.formatFileSize(mContext, overBytes));
+ Formatter.formatFileSize(mContext, overBytes, Formatter.FLAG_IEC_UNITS));
builder.setOngoing(true);
builder.setSmallIcon(R.drawable.stat_notify_error);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 4cc08d88bd50..4ed24ec7971b 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1291,13 +1291,11 @@ public final class NotificationRecord {
lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL,
stats.naturalImportance);
}
- // Log Assistant override if it was itself overridden by System. Since System can't be
- // overridden, it never needs logging.
- if (mImportanceExplanationCode == MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM
- && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
- lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST,
+ }
+ // Log Assistant override if present, whether or not importance calculation is complete.
+ if (mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
+ lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST,
mAssistantImportance);
- }
}
return lm;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index ce3c452044f8..e4cb283fe864 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -360,7 +360,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
final long age = System.currentTimeMillis() - session.createdMillis;
final long timeSinceUpdate =
- System.currentTimeMillis() - session.updatedMillis;
+ System.currentTimeMillis() - session.getUpdatedMillis();
final boolean valid;
if (session.isStaged()) {
if (timeSinceUpdate >= MAX_TIME_SINCE_UPDATE_MILLIS
@@ -498,10 +498,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
- if (callingUid == Process.SYSTEM_UID) {
+ if (Build.IS_DEBUGGABLE || isDowngradeAllowedForCaller(callingUid)) {
params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
} else {
params.installFlags &= ~PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ params.installFlags &= ~PackageManager.INSTALL_REQUEST_DOWNGRADE;
}
boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0;
@@ -621,6 +622,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
return sessionId;
}
+ private boolean isDowngradeAllowedForCaller(int callingUid) {
+ return callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID
+ || callingUid == Process.SHELL_UID;
+ }
+
@Override
public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {
synchronized (mSessions) {
@@ -813,7 +819,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
statusReceiver, versionedPackage.getPackageName(),
- !canSilentlyInstallPackage, userId);
+ canSilentlyInstallPackage, userId);
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
== PackageManager.PERMISSION_GRANTED) {
// Sweet, call straight through!
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5d539a4b1dda..e45a993b99f5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -202,7 +202,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/** Timestamp of the last time this session changed state */
@GuardedBy("mLock")
- long updatedMillis;
+ private long updatedMillis;
/** Uid of the creator of this session. */
private final int mOriginalInstallerUid;
@@ -1833,6 +1833,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ /**
+ * @return the timestamp of when this session last changed state
+ */
+ public long getUpdatedMillis() {
+ synchronized (mLock) {
+ return updatedMillis;
+ }
+ }
+
String getInstallerPackageName() {
synchronized (mLock) {
return mInstallerPackageName;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3cab9e553e8e..098225f9e820 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3502,7 +3502,7 @@ public class PackageManagerService extends IPackageManager.Stub
private @NonNull String getRequiredInstallerLPr() {
final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
+ intent.setDataAndType(Uri.parse("content://com.example/foo.apk"), PACKAGE_MIME_TYPE);
final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 9348806c0e59..d8f07feb1ddb 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -166,8 +166,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
* This may cause {@code packages} to be rolled back if they crash too freqeuntly.
*/
public void startObservingHealth(List<String> packages, long durationMs) {
- PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs,
- false /* withExplicitHealthCheck */);
+ PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
}
/** Verifies the rollback state after a reboot. */
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index c2a4339eceda..d52ba169768f 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2671,7 +2671,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wallpaper.connection.mReply = null;
}
try {
- wallpaper.connection.mService.detach();
+ // It can be null if user switching happens before service connection.
+ if (wallpaper.connection.mService != null) {
+ wallpaper.connection.mService.detach();
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Failed detaching wallpaper service ", e);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a93bdbaabdc7..91ec4a083ed9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2730,30 +2730,40 @@ final class ActivityRecord extends ConfigurationContainer {
final int appHeight = resolvedAppBounds.height();
final int parentAppWidth = parentAppBounds.width();
final int parentAppHeight = parentAppBounds.height();
+ if (parentAppWidth == appWidth && parentAppHeight == appHeight) {
+ // Matched the parent bounds.
+ return false;
+ }
+ if (parentAppWidth > appWidth && parentAppHeight > appHeight) {
+ // Both sides are smaller than the parent.
+ return true;
+ }
if (parentAppWidth < appWidth || parentAppHeight < appHeight) {
// One side is larger than the parent.
return true;
}
- if (info.hasFixedAspectRatio()) {
+ // The rest of the condition is that only one side is smaller than the parent, but it still
+ // needs to exclude the cases where the size is limited by the fixed aspect ratio.
+ if (info.maxAspectRatio > 0) {
final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
/ Math.min(appWidth, appHeight);
+ if (aspectRatio >= info.maxAspectRatio) {
+ // The current size has reached the max aspect ratio.
+ return false;
+ }
+ }
+ if (info.minAspectRatio > 0) {
+ // The activity should have at least the min aspect ratio, so this checks if the parent
+ // still has available space to provide larger aspect ratio.
final float parentAspectRatio = (0.5f + Math.max(parentAppWidth, parentAppHeight))
/ Math.min(parentAppWidth, parentAppHeight);
- // Check if the parent still has available space in long side.
- if (aspectRatio < parentAspectRatio
- && (aspectRatio < info.maxAspectRatio || info.minAspectRatio > 0)) {
- return true;
+ if (parentAspectRatio <= info.minAspectRatio) {
+ // The long side has reached the parent.
+ return false;
}
}
-
- final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
- // If the width or height is the same as parent, it is already the best fit of the override
- // bounds, therefore this condition is considered as not size compatibility mode. Here uses
- // right and bottom as width and height of parent because the bounds may contain decor
- // insets which has been accounted in override bounds. See {@link #computeBounds}.
- return parentAppBounds.right != resolvedBounds.width()
- && parentAppBounds.bottom != resolvedBounds.height();
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 3bbe28d429c5..90bb494232c7 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -51,6 +51,7 @@ public class BarController {
private static final int MSG_NAV_BAR_VISIBILITY_CHANGED = 1;
protected final String mTag;
+ protected final int mDisplayId;
private final int mTransientFlag;
private final int mUnhideFlag;
private final int mTranslucentFlag;
@@ -74,9 +75,10 @@ public class BarController {
private OnBarVisibilityChangedListener mVisibilityChangeListener;
- BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag,
+ BarController(String tag, int displayId, int transientFlag, int unhideFlag, int translucentFlag,
int statusBarManagerId, int translucentWmFlag, int transparentFlag) {
mTag = "BarController." + tag;
+ mDisplayId = displayId;
mTransientFlag = transientFlag;
mUnhideFlag = unhideFlag;
mTranslucentFlag = translucentFlag;
@@ -230,7 +232,7 @@ public class BarController {
public void run() {
StatusBarManagerInternal statusbar = getStatusBarInternal();
if (statusbar != null) {
- statusbar.setWindowState(mWin.getDisplayId(), mStatusBarManagerId, state);
+ statusbar.setWindowState(mDisplayId, mStatusBarManagerId, state);
}
}
});
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 1888e9474267..bbb857f4c24c 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -26,6 +26,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.InsetsState.TYPE_TOP_GESTURES;
+import static android.view.InsetsState.TYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
@@ -40,6 +42,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
@@ -152,6 +155,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.util.ScreenshotHelper;
+import com.android.internal.util.function.TriConsumer;
import com.android.internal.widget.PointerLocationView;
import com.android.server.LocalServices;
import com.android.server.UiThread;
@@ -215,6 +219,12 @@ public class DisplayPolicy {
private final Object mServiceAcquireLock = new Object();
private StatusBarManagerInternal mStatusBarManagerInternal;
+ @Px
+ private int mBottomGestureAdditionalInset;
+ @Px
+ private int mSideGestureInset;
+ private boolean mNavigationBarLetsThroughTaps;
+
private StatusBarManagerInternal getStatusBarManagerInternal() {
synchronized (mServiceAcquireLock) {
if (mStatusBarManagerInternal == null) {
@@ -261,15 +271,9 @@ public class DisplayPolicy {
/** Cached value of {@link ScreenShapeHelper#getWindowOutsetBottomPx} */
@Px private int mWindowOutsetBottom;
- private final StatusBarController mStatusBarController = new StatusBarController();
+ private final StatusBarController mStatusBarController;
- private final BarController mNavigationBarController = new BarController("NavigationBar",
- View.NAVIGATION_BAR_TRANSIENT,
- View.NAVIGATION_BAR_UNHIDE,
- View.NAVIGATION_BAR_TRANSLUCENT,
- StatusBarManager.WINDOW_NAVIGATION_BAR,
- FLAG_TRANSLUCENT_NAVIGATION,
- View.NAVIGATION_BAR_TRANSPARENT);
+ private final BarController mNavigationBarController;
private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
new BarController.OnBarVisibilityChangedListener() {
@@ -416,6 +420,17 @@ public class DisplayPolicy {
mDisplayContent = displayContent;
mLock = service.getWindowManagerLock();
+ final int displayId = displayContent.getDisplayId();
+ mStatusBarController = new StatusBarController(displayId);
+ mNavigationBarController = new BarController("NavigationBar",
+ displayId,
+ View.NAVIGATION_BAR_TRANSIENT,
+ View.NAVIGATION_BAR_UNHIDE,
+ View.NAVIGATION_BAR_TRANSLUCENT,
+ StatusBarManager.WINDOW_NAVIGATION_BAR,
+ FLAG_TRANSLUCENT_NAVIGATION,
+ View.NAVIGATION_BAR_TRANSPARENT);
+
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer);
@@ -527,7 +542,6 @@ public class DisplayPolicy {
if (mWindowSleepToken != null) {
return;
}
- final int displayId = displayContent.getDisplayId();
mWindowSleepToken = service.mAtmInternal.acquireSleepToken(
"WindowSleepTokenOnDisplay" + displayId, displayId);
};
@@ -857,11 +871,14 @@ public class DisplayPolicy {
if (mDisplayContent.isDefaultDisplay) {
mService.mPolicy.setKeyguardCandidateLw(win);
}
- mDisplayContent.setInsetProvider(TYPE_TOP_BAR, win,
+ final TriConsumer<DisplayFrames, WindowState, Rect> frameProvider =
(displayFrames, windowState, rect) -> {
rect.top = 0;
rect.bottom = getStatusBarHeight(displayFrames);
- });
+ };
+ mDisplayContent.setInsetProvider(TYPE_TOP_BAR, win, frameProvider);
+ mDisplayContent.setInsetProvider(TYPE_TOP_GESTURES, win, frameProvider);
+ mDisplayContent.setInsetProvider(TYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider);
break;
case TYPE_NAVIGATION_BAR:
mContext.enforceCallingOrSelfPermission(
@@ -878,6 +895,31 @@ public class DisplayPolicy {
mNavBarVisibilityListener, true);
mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR,
win, null /* frameProvider */);
+ mDisplayContent.setInsetProvider(InsetsState.TYPE_BOTTOM_GESTURES, win,
+ (displayFrames, windowState, inOutFrame) -> {
+ inOutFrame.top -= mBottomGestureAdditionalInset;
+ });
+ mDisplayContent.setInsetProvider(InsetsState.TYPE_LEFT_GESTURES, win,
+ (displayFrames, windowState, inOutFrame) -> {
+ inOutFrame.left = 0;
+ inOutFrame.top = 0;
+ inOutFrame.bottom = displayFrames.mDisplayHeight;
+ inOutFrame.right = displayFrames.mUnrestricted.left + mSideGestureInset;
+ });
+ mDisplayContent.setInsetProvider(InsetsState.TYPE_RIGHT_GESTURES, win,
+ (displayFrames, windowState, inOutFrame) -> {
+ inOutFrame.left = displayFrames.mUnrestricted.right - mSideGestureInset;
+ inOutFrame.top = 0;
+ inOutFrame.bottom = displayFrames.mDisplayHeight;
+ inOutFrame.right = displayFrames.mDisplayWidth;
+ });
+ mDisplayContent.setInsetProvider(InsetsState.TYPE_BOTTOM_TAPPABLE_ELEMENT, win,
+ (displayFrames, windowState, inOutFrame) -> {
+ if ((windowState.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0
+ || mNavigationBarLetsThroughTaps) {
+ inOutFrame.setEmpty();
+ }
+ });
if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
break;
case TYPE_NAVIGATION_BAR_PANEL:
@@ -2544,6 +2586,7 @@ public class DisplayPolicy {
*/
public void onOverlayChangedLw() {
onConfigurationChanged();
+ mSystemGestures.onConfigurationChanged();
}
/**
@@ -2606,11 +2649,20 @@ public class DisplayPolicy {
}
mNavBarOpacityMode = res.getInteger(R.integer.config_navBarOpacityMode);
+ mSideGestureInset = res.getDimensionPixelSize(R.dimen.config_backGestureInset);
+ mNavigationBarLetsThroughTaps = res.getBoolean(R.bool.config_navBarTapThrough);
// EXPERIMENT TODO(b/113952590): Remove once experiment in bug is completed
mExperiments.onConfigurationChanged(uiContext);
// EXPERIMENT END
+ // EXPERIMENT: TODO(b/113952590): Replace with real code after experiment.
+ // This should calculate how much above the frame we accept gestures. Currently,
+ // we extend the frame to capture the gestures, so this is 0.
+ mBottomGestureAdditionalInset = mExperiments.getNavigationBarFrameHeight()
+ - mExperiments.getNavigationBarFrameHeight();
+ // EXPERIMENT END
+
updateConfigurationAndScreenSizeDependentBehaviors();
mWindowOutsetBottom = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
}
diff --git a/services/core/java/com/android/server/wm/StatusBarController.java b/services/core/java/com/android/server/wm/StatusBarController.java
index 6db606d2a30b..f4260d32a77d 100644
--- a/services/core/java/com/android/server/wm/StatusBarController.java
+++ b/services/core/java/com/android/server/wm/StatusBarController.java
@@ -36,22 +36,22 @@ public class StatusBarController extends BarController {
private Runnable mAppTransitionPending = () -> {
StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null && mWin != null) {
- statusBar.appTransitionPending(mWin.getDisplayId());
+ if (statusBar != null) {
+ statusBar.appTransitionPending(mDisplayId);
}
};
private Runnable mAppTransitionCancelled = () -> {
StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null && mWin != null) {
- statusBar.appTransitionCancelled(mWin.getDisplayId());
+ if (statusBar != null) {
+ statusBar.appTransitionCancelled(mDisplayId);
}
};
private Runnable mAppTransitionFinished = () -> {
StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null && mWin != null) {
- statusBar.appTransitionFinished(mWin.getDisplayId());
+ if (statusBar != null) {
+ statusBar.appTransitionFinished(mDisplayId);
}
};
@@ -65,8 +65,8 @@ public class StatusBarController extends BarController {
long statusBarAnimationStartTime, long statusBarAnimationDuration) {
mHandler.post(() -> {
StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null && mWin != null) {
- statusBar.appTransitionStarting(mWin.getDisplayId(),
+ if (statusBar != null) {
+ statusBar.appTransitionStarting(mDisplayId,
statusBarAnimationStartTime, statusBarAnimationDuration);
}
});
@@ -84,8 +84,9 @@ public class StatusBarController extends BarController {
}
};
- StatusBarController() {
+ StatusBarController(int displayId) {
super("StatusBar",
+ displayId,
View.STATUS_BAR_TRANSIENT,
View.STATUS_BAR_UNHIDE,
View.STATUS_BAR_TRANSLUCENT,
diff --git a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
index bd4e542033c5..35afaedb0b43 100644
--- a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
@@ -17,9 +17,13 @@
package com.android.server.wm;
import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayCutout;
import android.view.GestureDetector;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -46,8 +50,9 @@ class SystemGesturesPointerEventListener implements PointerEventListener {
private final Context mContext;
private final Handler mHandler;
- private final int mSwipeStartThreshold;
- private final int mSwipeDistanceThreshold;
+ private int mDisplayCutoutTouchableRegionSize;
+ private int mSwipeStartThreshold;
+ private int mSwipeDistanceThreshold;
private final Callbacks mCallbacks;
private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS];
private final float[] mDownX = new float[MAX_TRACKED_POINTERS];
@@ -65,14 +70,33 @@ class SystemGesturesPointerEventListener implements PointerEventListener {
private long mLastFlingTime;
SystemGesturesPointerEventListener(Context context, Handler handler, Callbacks callbacks) {
- mContext = context;
+ mContext = checkNull("context", context);
mHandler = handler;
mCallbacks = checkNull("callbacks", callbacks);
- mSwipeStartThreshold = checkNull("context", context).getResources()
+
+ onConfigurationChanged();
+ }
+
+ void onConfigurationChanged() {
+ mSwipeStartThreshold = mContext.getResources()
.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+
+ final Display display = DisplayManagerGlobal.getInstance()
+ .getRealDisplay(Display.DEFAULT_DISPLAY);
+ final DisplayCutout displayCutout = display.getCutout();
+ if (displayCutout != null) {
+ final Rect bounds = displayCutout.getBoundingRectTop();
+ if (!bounds.isEmpty()) {
+ // Expand swipe start threshold such that we can catch touches that just start below
+ // the notch area
+ mDisplayCutoutTouchableRegionSize = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.display_cutout_touchable_region_size);
+ mSwipeStartThreshold += mDisplayCutoutTouchableRegionSize;
+ }
+ }
mSwipeDistanceThreshold = mSwipeStartThreshold;
if (DEBUG) Slog.d(TAG, "mSwipeStartThreshold=" + mSwipeStartThreshold
- + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
+ + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
}
private static <T> T checkNull(String name, T arg) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 3d84bd40fb5a..3d6c8684e6d8 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -96,6 +96,7 @@ static struct {
jmethodID interceptKeyBeforeDispatching;
jmethodID dispatchUnhandledKey;
jmethodID checkInjectEventsPermission;
+ jmethodID onPointerDownOutsideFocus;
jmethodID getVirtualKeyQuietTimeMillis;
jmethodID getExcludedDeviceNames;
jmethodID getInputPortAssociations;
@@ -259,6 +260,7 @@ public:
virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
virtual bool checkInjectEventsPermissionNonReentrant(
int32_t injectorPid, int32_t injectorUid);
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken);
/* --- PointerControllerPolicyInterface implementation --- */
@@ -1205,6 +1207,15 @@ bool NativeInputManager::checkInjectEventsPermissionNonReentrant(
return result;
}
+void NativeInputManager::onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) {
+ ATRACE_CALL();
+ JNIEnv* env = jniEnv();
+
+ jobject touchedTokenObj = javaObjectForIBinder(env, touchedToken);
+ env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDownOutsideFocus, touchedTokenObj);
+ checkAndClearExceptionFromCallback(env, "onPointerDownOutsideFocus");
+}
+
void NativeInputManager::loadPointerIcon(SpriteIcon* icon, int32_t displayId) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
@@ -1809,6 +1820,9 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gServiceClassInfo.checkInjectEventsPermission, clazz,
"checkInjectEventsPermission", "(II)Z");
+ GET_METHOD_ID(gServiceClassInfo.onPointerDownOutsideFocus, clazz,
+ "onPointerDownOutsideFocus", "(Landroid/os/IBinder;)V");
+
GET_METHOD_ID(gServiceClassInfo.getVirtualKeyQuietTimeMillis, clazz,
"getVirtualKeyQuietTimeMillis", "()I");
diff --git a/services/net/java/android/net/INetworkMonitor.aidl b/services/net/java/android/net/INetworkMonitor.aidl
index 1b0e1d788ff3..3ed4640525c6 100644
--- a/services/net/java/android/net/INetworkMonitor.aidl
+++ b/services/net/java/android/net/INetworkMonitor.aidl
@@ -15,6 +15,8 @@
*/
package android.net;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
import android.net.PrivateDnsConfigParcel;
/** @hide */
@@ -46,8 +48,8 @@ oneway interface INetworkMonitor {
void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config);
void notifyDnsResponse(int returnCode);
void notifySystemReady();
- void notifyNetworkConnected();
+ void notifyNetworkConnected(in LinkProperties lp, in NetworkCapabilities nc);
void notifyNetworkDisconnected();
- void notifyLinkPropertiesChanged();
- void notifyNetworkCapabilitiesChanged();
+ void notifyLinkPropertiesChanged(in LinkProperties lp);
+ void notifyNetworkCapabilitiesChanged(in NetworkCapabilities nc);
} \ No newline at end of file
diff --git a/services/net/java/android/net/ipmemorystore/NetworkAttributes.java b/services/net/java/android/net/ipmemorystore/NetworkAttributes.java
index 6a9eae00e3ff..e76976991797 100644
--- a/services/net/java/android/net/ipmemorystore/NetworkAttributes.java
+++ b/services/net/java/android/net/ipmemorystore/NetworkAttributes.java
@@ -60,6 +60,13 @@ public class NetworkAttributes {
public final Inet4Address assignedV4Address;
private static final float WEIGHT_ASSIGNEDV4ADDR = 300.0f;
+ // The lease expiry timestamp of v4 address allocated from DHCP server, in milliseconds.
+ @Nullable
+ public final Long assignedV4AddressExpiry;
+ // lease expiry doesn't imply any correlation between "the same lease expiry value" and "the
+ // same L3 network".
+ private static final float WEIGHT_ASSIGNEDV4ADDREXPIRY = 0.0f;
+
// Optionally supplied by the client if it has an opinion on L3 network. For example, this
// could be a hash of the SSID + security type on WiFi.
@Nullable
@@ -81,6 +88,7 @@ public class NetworkAttributes {
/** @hide */
@VisibleForTesting
public static final float TOTAL_WEIGHT = WEIGHT_ASSIGNEDV4ADDR
+ + WEIGHT_ASSIGNEDV4ADDREXPIRY
+ WEIGHT_GROUPHINT
+ WEIGHT_DNSADDRESSES
+ WEIGHT_MTU;
@@ -89,11 +97,16 @@ public class NetworkAttributes {
@VisibleForTesting
public NetworkAttributes(
@Nullable final Inet4Address assignedV4Address,
+ @Nullable final Long assignedV4AddressExpiry,
@Nullable final String groupHint,
@Nullable final List<InetAddress> dnsAddresses,
@Nullable final Integer mtu) {
if (mtu != null && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
+ if (assignedV4AddressExpiry != null && assignedV4AddressExpiry <= 0) {
+ throw new IllegalArgumentException("lease expiry can't be negative or zero");
+ }
this.assignedV4Address = assignedV4Address;
+ this.assignedV4AddressExpiry = assignedV4AddressExpiry;
this.groupHint = groupHint;
this.dnsAddresses = null == dnsAddresses ? null :
Collections.unmodifiableList(new ArrayList<>(dnsAddresses));
@@ -105,6 +118,8 @@ public class NetworkAttributes {
// The call to the other constructor must be the first statement of this constructor,
// so everything has to be inline
this((Inet4Address) getByAddressOrNull(parcelable.assignedV4Address),
+ parcelable.assignedV4AddressExpiry > 0
+ ? parcelable.assignedV4AddressExpiry : null,
parcelable.groupHint,
blobArrayToInetAddressList(parcelable.dnsAddresses),
parcelable.mtu >= 0 ? parcelable.mtu : null);
@@ -150,6 +165,8 @@ public class NetworkAttributes {
final NetworkAttributesParcelable parcelable = new NetworkAttributesParcelable();
parcelable.assignedV4Address =
(null == assignedV4Address) ? null : assignedV4Address.getAddress();
+ parcelable.assignedV4AddressExpiry =
+ (null == assignedV4AddressExpiry) ? 0 : assignedV4AddressExpiry;
parcelable.groupHint = groupHint;
parcelable.dnsAddresses = inetAddressListToBlobArray(dnsAddresses);
parcelable.mtu = (null == mtu) ? -1 : mtu;
@@ -168,6 +185,8 @@ public class NetworkAttributes {
public float getNetworkGroupSamenessConfidence(@NonNull final NetworkAttributes o) {
final float samenessScore =
samenessContribution(WEIGHT_ASSIGNEDV4ADDR, assignedV4Address, o.assignedV4Address)
+ + samenessContribution(WEIGHT_ASSIGNEDV4ADDREXPIRY, assignedV4AddressExpiry,
+ o.assignedV4AddressExpiry)
+ samenessContribution(WEIGHT_GROUPHINT, groupHint, o.groupHint)
+ samenessContribution(WEIGHT_DNSADDRESSES, dnsAddresses, o.dnsAddresses)
+ samenessContribution(WEIGHT_MTU, mtu, o.mtu);
@@ -189,6 +208,8 @@ public class NetworkAttributes {
@Nullable
private Inet4Address mAssignedAddress;
@Nullable
+ private Long mAssignedAddressExpiry;
+ @Nullable
private String mGroupHint;
@Nullable
private List<InetAddress> mDnsAddresses;
@@ -206,6 +227,20 @@ public class NetworkAttributes {
}
/**
+ * Set the lease expiry timestamp of assigned v4 address.
+ * @param assignedV4AddressExpiry The lease expiry timestamp of assigned v4 address.
+ * @return This builder.
+ */
+ public Builder setAssignedV4AddressExpiry(
+ @Nullable final Long assignedV4AddressExpiry) {
+ if (null != assignedV4AddressExpiry && assignedV4AddressExpiry <= 0) {
+ throw new IllegalArgumentException("lease expiry can't be negative or zero");
+ }
+ mAssignedAddressExpiry = assignedV4AddressExpiry;
+ return this;
+ }
+
+ /**
* Set the group hint.
* @param groupHint The group hint.
* @return This builder.
@@ -248,14 +283,15 @@ public class NetworkAttributes {
* @return The built NetworkAttributes object.
*/
public NetworkAttributes build() {
- return new NetworkAttributes(mAssignedAddress, mGroupHint, mDnsAddresses, mMtu);
+ return new NetworkAttributes(mAssignedAddress, mAssignedAddressExpiry,
+ mGroupHint, mDnsAddresses, mMtu);
}
}
/** @hide */
public boolean isEmpty() {
- return (null == assignedV4Address) && (null == groupHint)
- && (null == dnsAddresses) && (null == mtu);
+ return (null == assignedV4Address) && (null == assignedV4AddressExpiry)
+ && (null == groupHint) && (null == dnsAddresses) && (null == mtu);
}
@Override
@@ -263,6 +299,7 @@ public class NetworkAttributes {
if (!(o instanceof NetworkAttributes)) return false;
final NetworkAttributes other = (NetworkAttributes) o;
return Objects.equals(assignedV4Address, other.assignedV4Address)
+ && Objects.equals(assignedV4AddressExpiry, other.assignedV4AddressExpiry)
&& Objects.equals(groupHint, other.groupHint)
&& Objects.equals(dnsAddresses, other.dnsAddresses)
&& Objects.equals(mtu, other.mtu);
@@ -270,7 +307,8 @@ public class NetworkAttributes {
@Override
public int hashCode() {
- return Objects.hash(assignedV4Address, groupHint, dnsAddresses, mtu);
+ return Objects.hash(assignedV4Address, assignedV4AddressExpiry,
+ groupHint, dnsAddresses, mtu);
}
/** Pretty print */
@@ -286,6 +324,13 @@ public class NetworkAttributes {
nullFields.add("assignedV4Addr");
}
+ if (null != assignedV4AddressExpiry) {
+ resultJoiner.add("assignedV4AddressExpiry :");
+ resultJoiner.add(assignedV4AddressExpiry.toString());
+ } else {
+ nullFields.add("assignedV4AddressExpiry");
+ }
+
if (null != groupHint) {
resultJoiner.add("groupHint :");
resultJoiner.add(groupHint);
diff --git a/services/net/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/services/net/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
index 0894d7260915..997eb2b5128b 100644
--- a/services/net/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
+++ b/services/net/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
@@ -30,6 +30,7 @@ import android.net.ipmemorystore.Blob;
*/
parcelable NetworkAttributesParcelable {
byte[] assignedV4Address;
+ long assignedV4AddressExpiry;
String groupHint;
Blob[] dnsAddresses;
int mtu;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index ee09c7e704c0..7c2235050caf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -477,8 +477,9 @@ public class NotificationRecordTest extends UiServiceTestCase {
assertEquals(MetricsEvent.IMPORTANCE_EXPLANATION_APP,
logMaker.getTaggedData(
MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL_EXPLANATION));
- // This field is only populated if the assistant was itself overridden by the system.
- assertNull(logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST));
+ // This field is populated whenever mImportanceExplanationCode is.
+ assertEquals(IMPORTANCE_LOW,
+ logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index d580557638cf..32e96a592207 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -438,8 +438,11 @@ public class ActivityRecordTests extends ActivityTestsBase {
doReturn(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
.when(mActivity.mAppWindowToken).getOrientationIgnoreVisibility();
mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
- mActivity.info.maxAspectRatio = 1;
+ mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
ensureActivityConfiguration();
+ // The parent configuration doesn't change since the first resolved configuration, so the
+ // activity shouldn't be in the size compatibility mode.
+ assertFalse(mActivity.inSizeCompatMode());
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
// Ensure the app bounds keep the declared aspect ratio.
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index d0c261279c9d..28af7cee8f38 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -19,11 +19,16 @@ package com.android.server;
import static com.android.server.PackageWatchdog.TRIGGER_FAILURE_COUNT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
import android.content.pm.VersionedPackage;
+import android.os.Handler;
+import android.os.RemoteException;
import android.os.test.TestLooper;
+import android.util.AtomicFile;
import androidx.test.InstrumentationRegistry;
@@ -36,11 +41,14 @@ import org.junit.Test;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
-// TODO(zezeozue): Write test without using PackageWatchdog#getPackages. Just rely on
+// TODO: Write test without using PackageWatchdog#getPackages. Just rely on
// behavior of observers receiving crash notifications or not to determine if it's registered
+// TODO: Use Truth in tests.
/**
* Test PackageWatchdog.
*/
@@ -77,12 +85,11 @@ public class PackageWatchdogTest {
TestObserver observer3 = new TestObserver(OBSERVER_NAME_3);
// Start observing for observer1 which will be unregistered
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION, false);
+ watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
// Start observing for observer2 which will expire
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION,
- false);
+ watchdog.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
// Start observing for observer3 which will have expiry duration reduced
- watchdog.startObservingHealth(observer3, Arrays.asList(APP_A), LONG_DURATION, false);
+ watchdog.startObservingHealth(observer3, Arrays.asList(APP_A), LONG_DURATION);
// Verify packages observed at start
// 1
@@ -145,9 +152,8 @@ public class PackageWatchdogTest {
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog1.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION, false);
- watchdog1.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION,
- false);
+ watchdog1.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog1.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
// Verify 2 observers are registered and saved internally
// 1
@@ -193,8 +199,8 @@ public class PackageWatchdogTest {
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION, false);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION, false);
+ watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A below the threshold
for (int i = 0; i < TRIGGER_FAILURE_COUNT - 1; i++) {
@@ -220,8 +226,8 @@ public class PackageWatchdogTest {
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION, false);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_B), SHORT_DURATION, false);
+ watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startObservingHealth(observer1, Arrays.asList(APP_B), SHORT_DURATION);
// Then fail APP_C (not observed) above the threshold
for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
@@ -255,7 +261,7 @@ public class PackageWatchdogTest {
}
};
- watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION, false);
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A (different version) above the threshold
for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
@@ -288,13 +294,13 @@ public class PackageWatchdogTest {
// Start observing for all impact observers
watchdog.startObservingHealth(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
- SHORT_DURATION, false);
+ SHORT_DURATION);
watchdog.startObservingHealth(observerHigh, Arrays.asList(APP_A, APP_B, APP_C),
- SHORT_DURATION, false);
+ SHORT_DURATION);
watchdog.startObservingHealth(observerMid, Arrays.asList(APP_A, APP_B),
- SHORT_DURATION, false);
+ SHORT_DURATION);
watchdog.startObservingHealth(observerLow, Arrays.asList(APP_A),
- SHORT_DURATION, false);
+ SHORT_DURATION);
// Then fail all apps above the threshold
for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
@@ -346,8 +352,8 @@ public class PackageWatchdogTest {
PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
// Start observing for observerFirst and observerSecond with failure handling
- watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION, false);
- watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION, false);
+ watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
+ watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
// Then fail APP_A above the threshold
for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
@@ -424,8 +430,8 @@ public class PackageWatchdogTest {
PackageHealthObserverImpact.USER_IMPACT_HIGH);
// Start observing for observer1 and observer2 with failure handling
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION, false);
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION, false);
+ watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
// Then fail APP_A above the threshold
for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) {
@@ -442,11 +448,12 @@ public class PackageWatchdogTest {
}
/**
- * Test explicit health check status determines package failure or success on expiry
+ * Test package passing explicit health checks does not fail and vice versa.
*/
@Test
- public void testPackageFailureExplicitHealthCheck() throws Exception {
- PackageWatchdog watchdog = createWatchdog();
+ public void testExplicitHealthChecks() throws Exception {
+ TestController controller = new TestController();
+ PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1,
PackageHealthObserverImpact.USER_IMPACT_HIGH);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2,
@@ -457,21 +464,36 @@ public class PackageWatchdogTest {
// Start observing with explicit health checks for APP_A and APP_B respectively
// with observer1 and observer2
- watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION, true);
- watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION, true);
- // Explicit health check passed for APP_A (observer1 is aware)
- watchdog.onExplicitHealthCheckPassed(APP_A);
- // Start observing APP_A with explicit health checks for observer3.
+ controller.setSupportedPackages(Arrays.asList(APP_A, APP_B));
+ watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION);
+
+ // Run handler so requests are dispatched to the controller
+ mTestLooper.dispatchAll();
+
+ // Verify we requested health checks for APP_A and APP_B
+ List<String> requestedPackages = controller.getRequestedPackages();
+ assertEquals(2, requestedPackages.size());
+ assertEquals(APP_A, requestedPackages.get(0));
+ assertEquals(APP_B, requestedPackages.get(1));
+
+ // Then health check passed for APP_A (observer1 is aware)
+ controller.setPackagePassed(APP_A);
+
+ // Then start observing APP_A with explicit health checks for observer3.
// Observer3 didn't exist when we got the explicit health check above, so
// it starts out with a non-passing explicit health check and has to wait for a pass
// otherwise it would be notified of APP_A failure on expiry
- watchdog.startObservingHealth(observer3, Arrays.asList(APP_A), SHORT_DURATION, true);
+ watchdog.startObservingHealth(observer3, Arrays.asList(APP_A), SHORT_DURATION);
// Then expire observers
Thread.sleep(SHORT_DURATION);
// Run handler so package failures are dispatched to observers
mTestLooper.dispatchAll();
+ // Verify we cancelled all requests on expiry
+ assertEquals(0, controller.getRequestedPackages().size());
+
// Verify observer1 is not notified
assertEquals(0, observer1.mFailedPackages.size());
@@ -484,9 +506,96 @@ public class PackageWatchdogTest {
assertEquals(APP_A, observer3.mFailedPackages.get(0));
}
+ /**
+ * Test explicit health check state can be disabled and enabled correctly.
+ */
+ @Test
+ public void testExplicitHealthCheckStateChanges() throws Exception {
+ TestController controller = new TestController();
+ PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
+ TestObserver observer = new TestObserver(OBSERVER_NAME_1,
+ PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+
+ // Start observing with explicit health checks for APP_A and APP_B
+ controller.setSupportedPackages(Arrays.asList(APP_A, APP_B, APP_C));
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_B), LONG_DURATION);
+
+ // Run handler so requests are dispatched to the controller
+ mTestLooper.dispatchAll();
+
+ // Verify we requested health checks for APP_A and APP_B
+ List<String> requestedPackages = controller.getRequestedPackages();
+ assertEquals(2, requestedPackages.size());
+ assertEquals(APP_A, requestedPackages.get(0));
+ assertEquals(APP_B, requestedPackages.get(1));
+
+ // Disable explicit health checks (marks APP_A and APP_B as passed)
+ watchdog.setExplicitHealthCheckEnabled(false);
+
+ // Run handler so requests/cancellations are dispatched to the controller
+ mTestLooper.dispatchAll();
+
+ // Verify all checks are cancelled
+ assertEquals(0, controller.getRequestedPackages().size());
+
+ // Then expire APP_A
+ Thread.sleep(SHORT_DURATION);
+ mTestLooper.dispatchAll();
+
+ // Verify APP_A is not failed (APP_B) is not expired yet
+ assertEquals(0, observer.mFailedPackages.size());
+
+ // Re-enable explicit health checks
+ watchdog.setExplicitHealthCheckEnabled(true);
+
+ // Run handler so requests/cancellations are dispatched to the controller
+ mTestLooper.dispatchAll();
+
+ // Verify no requests are made cos APP_A is expired and APP_B was marked as passed
+ assertEquals(0, controller.getRequestedPackages().size());
+
+ // Then set new supported packages
+ controller.setSupportedPackages(Arrays.asList(APP_C));
+ // Start observing APP_A and APP_C; only APP_C has support for explicit health checks
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_C), SHORT_DURATION);
+
+ // Run handler so requests/cancellations are dispatched to the controller
+ mTestLooper.dispatchAll();
+
+ // Verify requests are only made for APP_C
+ requestedPackages = controller.getRequestedPackages();
+ assertEquals(1, requestedPackages.size());
+ assertEquals(APP_C, requestedPackages.get(0));
+
+ // Then expire APP_A and APP_C
+ Thread.sleep(SHORT_DURATION);
+ mTestLooper.dispatchAll();
+
+ // Verify only APP_C is failed because explicit health checks was not supported for APP_A
+ assertEquals(1, observer.mFailedPackages.size());
+ assertEquals(APP_C, observer.mFailedPackages.get(0));
+ }
+
private PackageWatchdog createWatchdog() {
- return new PackageWatchdog(InstrumentationRegistry.getContext(),
- mTestLooper.getLooper());
+ return createWatchdog(new TestController(), true /* withPackagesReady */);
+ }
+
+ private PackageWatchdog createWatchdog(TestController controller, boolean withPackagesReady) {
+ Context context = InstrumentationRegistry.getContext();
+ AtomicFile policyFile =
+ new AtomicFile(new File(context.getFilesDir(), "package-watchdog.xml"));
+ Handler handler = new Handler(mTestLooper.getLooper());
+ PackageWatchdog watchdog =
+ new PackageWatchdog(context, policyFile, handler, handler, controller);
+ // Verify controller is not automatically started
+ assertFalse(controller.mIsEnabled);
+ if (withPackagesReady) {
+ watchdog.onPackagesReady();
+ // Verify controller by default is started when packages are ready
+ assertTrue(controller.mIsEnabled);
+ }
+ return watchdog;
}
private static class TestObserver implements PackageHealthObserver {
@@ -517,4 +626,69 @@ public class PackageWatchdogTest {
return mName;
}
}
+
+ private static class TestController extends ExplicitHealthCheckController {
+ TestController() {
+ super(null /* controller */);
+ }
+
+ private boolean mIsEnabled;
+ private List<String> mSupportedPackages = new ArrayList<>();
+ private List<String> mRequestedPackages = new ArrayList<>();
+ private Runnable mStateChangedRunnable;
+ private Consumer<String> mPassedConsumer;
+
+ @Override
+ public void request(String packageName) throws RemoteException {
+ if (!mRequestedPackages.contains(packageName)) {
+ mRequestedPackages.add(packageName);
+ }
+ }
+
+ @Override
+ public void cancel(String packageName) throws RemoteException {
+ mRequestedPackages.remove(packageName);
+ }
+
+ @Override
+ public void getSupportedPackages(Consumer<List<String>> consumer) throws RemoteException {
+ consumer.accept(mIsEnabled ? mSupportedPackages : Collections.emptyList());
+ }
+
+ @Override
+ public void getRequestedPackages(Consumer<List<String>> consumer) throws RemoteException {
+ // Pass copy to prevent ConcurrentModificationException during test
+ consumer.accept(
+ mIsEnabled ? new ArrayList<>(mRequestedPackages) : Collections.emptyList());
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ mIsEnabled = enabled;
+ mStateChangedRunnable.run();
+ }
+
+ @Override
+ public void setCallbacks(Runnable stateChangedRunnable, Consumer<String> passedConsumer) {
+ mStateChangedRunnable = stateChangedRunnable;
+ mPassedConsumer = passedConsumer;
+ }
+
+ public void setSupportedPackages(List<String> packages) {
+ mSupportedPackages.clear();
+ mSupportedPackages.addAll(packages);
+ }
+
+ public void setPackagePassed(String packageName) {
+ mPassedConsumer.accept(packageName);
+ }
+
+ public List<String> getRequestedPackages() {
+ if (mIsEnabled) {
+ return mRequestedPackages;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+ }
}
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
index 76cccc95742d..1a3ea6096c82 100644
--- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
+++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
@@ -44,6 +44,8 @@ public class ParcelableTests {
assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+ // lease will expire in two hours
+ builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 7_200_000);
// groupHint stays null this time around
builder.setDnsAddresses(Collections.emptyList());
builder.setMtu(18);
@@ -51,6 +53,7 @@ public class ParcelableTests {
assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("6.7.8.9"));
+ builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000);
builder.setGroupHint("groupHint");
builder.setDnsAddresses(Arrays.asList(
InetAddress.getByName("ACA1:652B:0911:DE8F:1200:115E:913B:AA2A"),
@@ -66,7 +69,7 @@ public class ParcelableTests {
// Verify that this test does not miss any new field added later.
// If any field is added to NetworkAttributes it must be tested here for parceling
// roundtrip.
- assertEquals(4, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
+ assertEquals(5, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
.filter(f -> !Modifier.isStatic(f.getModifiers())).count());
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 0ead2288a33e..d82b4e4850bc 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -499,7 +499,7 @@ public class ConnectivityServiceTest {
};
try {
- doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected();
+ doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected(any(), any());
doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt());
} catch (RemoteException e) {
fail(e.getMessage());
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
index dc2018543050..fb84611cb662 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
@@ -57,6 +57,7 @@ public class NetworkAttributesTest {
final NetworkAttributes na =
new NetworkAttributes(
(Inet4Address) Inet4Address.getByAddress(new byte[] {1, 2, 3, 4}),
+ System.currentTimeMillis() + 7_200_000,
"some hint",
Arrays.asList(Inet4Address.getByAddress(new byte[] {5, 6, 7, 8}),
Inet4Address.getByAddress(new byte[] {9, 0, 1, 2})),
diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java
index fff9635992d4..e2668bc4281f 100644
--- a/tests/testables/src/android/testing/TestableContext.java
+++ b/tests/testables/src/android/testing/TestableContext.java
@@ -296,13 +296,13 @@ public class TestableContext extends ContextWrapper implements TestRule {
@Override
public void registerComponentCallbacks(ComponentCallbacks callback) {
if (mComponent != null) mComponent.getLeakInfo(callback).addAllocation(new Throwable());
- super.registerComponentCallbacks(callback);
+ getBaseContext().registerComponentCallbacks(callback);
}
@Override
public void unregisterComponentCallbacks(ComponentCallbacks callback) {
if (mComponent != null) mComponent.getLeakInfo(callback).clearAllocations();
- super.unregisterComponentCallbacks(callback);
+ getBaseContext().unregisterComponentCallbacks(callback);
}
public TestablePermissions getTestablePermissions() {
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 53361414e9b8..857792192902 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -217,6 +217,29 @@ TEST_F(ResourceParserTest, ParseStyledStringWithWhitespace) {
EXPECT_THAT(str->value->spans[1].last_char, Eq(13u));
}
+TEST_F(ResourceParserTest, ParseStringTranslatableAttribute) {
+ // If there is no translate attribute the default is 'true'
+ EXPECT_TRUE(TestParse(R"(<string name="foo1">Translate</string>)"));
+ String* str = test::GetValue<String>(&table_, "string/foo1");
+ ASSERT_THAT(str, NotNull());
+ ASSERT_TRUE(str->IsTranslatable());
+
+ // Explicit 'true' translate attribute
+ EXPECT_TRUE(TestParse(R"(<string name="foo2" translatable="true">Translate</string>)"));
+ str = test::GetValue<String>(&table_, "string/foo2");
+ ASSERT_THAT(str, NotNull());
+ ASSERT_TRUE(str->IsTranslatable());
+
+ // Explicit 'false' translate attribute
+ EXPECT_TRUE(TestParse(R"(<string name="foo3" translatable="false">Do not translate</string>)"));
+ str = test::GetValue<String>(&table_, "string/foo3");
+ ASSERT_THAT(str, NotNull());
+ ASSERT_FALSE(str->IsTranslatable());
+
+ // Invalid value for the translate attribute, should be boolean ('true' or 'false')
+ EXPECT_FALSE(TestParse(R"(<string name="foo4" translatable="yes">Translate</string>)"));
+}
+
TEST_F(ResourceParserTest, IgnoreXliffTagsOtherThanG) {
std::string input = R"(
<string name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 2ec1ab31a58c..9b81369fa9f0 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -143,6 +143,8 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& path_data, io::IFile* file, IArchiveWriter* writer,
const std::string& output_path) {
TRACE_CALL();
+ // Filenames starting with "donottranslate" are not localizable
+ bool translatable_file = path_data.name.find("donottranslate") != 0;
ResourceTable table;
{
auto fin = file->OpenInputStream();
@@ -157,9 +159,7 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
ResourceParserOptions parser_options;
parser_options.error_on_positional_arguments = !options.legacy_mode;
-
- // If the filename includes donottranslate, then the default translatable is false.
- parser_options.translatable = path_data.name.find("donottranslate") == std::string::npos;
+ parser_options.translatable = translatable_file;
// If visibility was forced, we need to use it when creating a new resource and also error if
// we try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags.
@@ -172,7 +172,7 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
}
}
- if (options.pseudolocalize) {
+ if (options.pseudolocalize && translatable_file) {
// Generate pseudo-localized strings (en-XA and ar-XB).
// These are created as weak symbols, and are only generated from default
// configuration
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index c0c05cda35e7..5f637bd8d582 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -27,6 +27,8 @@
namespace aapt {
+using CompilerTest = CommandTestFixture;
+
std::string BuildPath(std::vector<std::string> args) {
std::string out;
if (args.empty()) {
@@ -51,7 +53,7 @@ int TestCompile(const std::string& path, const std::string& outDir, bool legacy,
return CompileCommand(&diag).Execute(args, &std::cerr);
}
-TEST(CompilerTest, MultiplePeriods) {
+TEST_F(CompilerTest, MultiplePeriods) {
StdErrDiagnostics diag;
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
@@ -108,7 +110,7 @@ TEST(CompilerTest, MultiplePeriods) {
ASSERT_EQ(::android::base::utf8::unlink(path5_out.c_str()), 0);
}
-TEST(CompilerTest, DirInput) {
+TEST_F(CompilerTest, DirInput) {
StdErrDiagnostics diag;
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
@@ -138,7 +140,7 @@ TEST(CompilerTest, DirInput) {
ASSERT_EQ(::android::base::utf8::unlink(kOutputFlata.c_str()), 0);
}
-TEST(CompilerTest, ZipInput) {
+TEST_F(CompilerTest, ZipInput) {
StdErrDiagnostics diag;
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
const std::string kResZip =
@@ -169,4 +171,86 @@ TEST(CompilerTest, ZipInput) {
ASSERT_EQ(::android::base::utf8::unlink(kOutputFlata.c_str()), 0);
}
-} // namespace aapt \ No newline at end of file
+/*
+ * This tests the "protection" from pseudo-translation of
+ * non-translatable files (starting with 'donotranslate')
+ * and strings (with the translatable="false" attribute)
+ *
+ * We check 4 string files, 2 translatable, and 2 not (based on file name)
+ * Each file contains 2 strings, one translatable, one not (attribute based)
+ * Each of these files are compiled and linked into one .apk, then we load the
+ * strings from the apk and check if there are pseudo-translated strings.
+ */
+
+// Using 000 and 111 because they are not changed by pseudo-translation,
+// making our life easier.
+constexpr static const char sTranslatableXmlContent[] =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<resources>"
+ " <string name=\"normal\">000</string>"
+ " <string name=\"non_translatable\" translatable=\"false\">111</string>"
+ "</resources>";
+
+static void AssertTranslations(CommandTestFixture *ctf, std::string file_name,
+ std::vector<std::string> expected) {
+
+ StdErrDiagnostics diag;
+
+ const std::string source_file = ctf->GetTestPath("/res/values/" + file_name + ".xml");
+ const std::string compiled_files_dir = ctf->GetTestPath("/compiled_" + file_name);
+ const std::string out_apk = ctf->GetTestPath("/" + file_name + ".apk");
+
+ CHECK(ctf->WriteFile(source_file, sTranslatableXmlContent));
+ CHECK(file::mkdirs(compiled_files_dir.data()));
+
+ ASSERT_EQ(CompileCommand(&diag).Execute({
+ source_file,
+ "-o", compiled_files_dir,
+ "-v",
+ "--pseudo-localize"
+ }, &std::cerr), 0);
+
+ ASSERT_TRUE(ctf->Link({
+ "--manifest", ctf->GetDefaultManifest(),
+ "-o", out_apk
+ }, compiled_files_dir, &diag));
+
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+
+ ResourceTable* table = apk->GetResourceTable();
+ ASSERT_NE(table, nullptr);
+ table->string_pool.Sort();
+
+ const std::vector<std::unique_ptr<StringPool::Entry>>& pool_strings =
+ table->string_pool.strings();
+
+ // The actual / expected vectors have the same size
+ const size_t pool_size = pool_strings.size();
+ ASSERT_EQ(pool_size, expected.size());
+
+ for (size_t i = 0; i < pool_size; i++) {
+ std::string actual = pool_strings[i]->value;
+ ASSERT_EQ(actual, expected[i]);
+ }
+}
+
+TEST_F(CompilerTest, DoNotTranslateTest) {
+ // The first string (000) is translatable, the second is not
+ // ar-XB uses "\u200F\u202E...\u202C\u200F"
+ std::vector<std::string> expected_translatable = {
+ "000", "111", // default locale
+ "[000 one]", // en-XA
+ "\xE2\x80\x8F\xE2\x80\xAE" "000" "\xE2\x80\xAC\xE2\x80\x8F", // ar-XB
+ };
+ AssertTranslations(this, "foo", expected_translatable);
+ AssertTranslations(this, "foo_donottranslate", expected_translatable);
+
+ // No translatable strings because these are non-translatable files
+ std::vector<std::string> expected_not_translatable = {
+ "000", "111", // default locale
+ };
+ AssertTranslations(this, "donottranslate", expected_not_translatable);
+ AssertTranslations(this, "donottranslate_foo", expected_not_translatable);
+}
+
+} // namespace aapt