diff options
147 files changed, 3458 insertions, 1152 deletions
diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl new file mode 100644 index 000000000000..3d50d14e1b83 --- /dev/null +++ b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl @@ -0,0 +1,21 @@ +/** + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media; + +/** {@hide} */ +interface IMediaCommunicationService { +} + diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 60dea079a36b..5773e4de3f4e 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -38,6 +38,7 @@ java_library { static_libs: [ "exoplayer2-extractor", "mediatranscoding_aidl_interface-java", + "modules-utils-build", ], jarjar_rules: "jarjar_rules.txt", @@ -52,6 +53,7 @@ java_library { visibility: [ "//frameworks/av/apex:__subpackages__", "//frameworks/base", // For framework-all + "//frameworks/base/apex/media/service", ], } @@ -80,6 +82,7 @@ filegroup { "java/android/media/Session2CommandGroup.java", "java/android/media/Session2Link.java", "java/android/media/Session2Token.java", + "java/android/media/MediaCommunicationManager.java", ], path: "java", } diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt index 2543a9cad576..8b9990f9e833 100644 --- a/apex/media/framework/api/current.txt +++ b/apex/media/framework/api/current.txt @@ -28,6 +28,9 @@ package android.media { ctor public ApplicationMediaCapabilities.FormatNotFoundException(@NonNull String); } + public class MediaCommunicationManager { + } + public class MediaController2 implements java.lang.AutoCloseable { method public void cancelSessionCommand(@NonNull Object); method public void close(); diff --git a/apex/media/framework/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt index d89d9d3343d1..eb71fddc05cb 100644 --- a/apex/media/framework/jarjar_rules.txt +++ b/apex/media/framework/jarjar_rules.txt @@ -1 +1,2 @@ +rule com.android.modules.utils.** android.media.internal.utils.@1 rule com.google.android.exoplayer2.** android.media.internal.exo.@1 diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java new file mode 100644 index 000000000000..b8065ef8c597 --- /dev/null +++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java @@ -0,0 +1,49 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media; + +import android.annotation.NonNull; +import android.annotation.SystemService; +import android.content.Context; + +import com.android.modules.utils.build.SdkLevel; + +/** + * Provides support for interacting with {@link android.media.MediaSession2 MediaSession2s} + * that applications have published to express their ongoing media playback state. + */ +// TODO: Add notifySession2Created() and sendMessage(). +@SystemService(Context.MEDIA_COMMUNICATION_SERVICE) +public class MediaCommunicationManager { + private static final String TAG = "MediaCommunicationManager"; + + private final Context mContext; + private final IMediaCommunicationService mService; + + /** + * @hide + */ + public MediaCommunicationManager(@NonNull Context context) { + if (!SdkLevel.isAtLeastS()) { + throw new UnsupportedOperationException("Android version must be S or greater."); + } + mContext = context; + mService = IMediaCommunicationService.Stub.asInterface( + MediaFrameworkInitializer.getMediaServiceManager() + .getMediaCommunicationServiceRegisterer() + .get()); + } +} diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java index 813ad7b43932..93328355026e 100644 --- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java +++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java @@ -19,10 +19,11 @@ package android.media; import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.SystemApi.Client; -import android.media.MediaTranscodeManager; import android.app.SystemServiceRegistry; import android.content.Context; +import com.android.modules.utils.build.SdkLevel; + /** * Class for performing registration for all media services on com.android.media apex. * @@ -74,5 +75,12 @@ public class MediaFrameworkInitializer { MediaTranscodeManager.class, context -> new MediaTranscodeManager(context) ); + if (SdkLevel.isAtLeastS()) { + SystemServiceRegistry.registerContextAwareService( + Context.MEDIA_COMMUNICATION_SERVICE, + MediaCommunicationManager.class, + context -> new MediaCommunicationManager(context) + ); + } } } diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp new file mode 100644 index 000000000000..5b24cfa4219b --- /dev/null +++ b/apex/media/service/Android.bp @@ -0,0 +1,41 @@ +// Copyright 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +filegroup { + name: "service-media-s-sources", + srcs: [ + "java/**/*.java", + ], + path: "java", + visibility: ["//frameworks/base/services"], // TODO(b/177640454): Should be private. +} + +java_sdk_library { + name: "service-media-s", + permitted_packages: [ + "com.android.server.media", + ], + defaults: ["framework-system-server-module-defaults"], + srcs: [ + ":service-media-s-sources", + ], + libs: [ + "updatable-media", + ], + sdk_version: "system_server_current", + min_sdk_version: "29", // TODO: We may need to bump this at some point. + apex_available: [ + "com.android.media", + ], +} + diff --git a/apex/media/service/api/current.txt b/apex/media/service/api/current.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/apex/media/service/api/current.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/apex/media/service/api/removed.txt b/apex/media/service/api/removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/apex/media/service/api/removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/apex/media/service/api/system-server-current.txt b/apex/media/service/api/system-server-current.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/apex/media/service/api/system-server-current.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/apex/media/service/api/system-server-removed.txt b/apex/media/service/api/system-server-removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/apex/media/service/api/system-server-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java new file mode 100644 index 000000000000..0468fdf30ba8 --- /dev/null +++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.media; + +import android.content.Context; +import android.media.IMediaCommunicationService; + +import com.android.server.SystemService; + +/** + * A system service that managers {@link android.media.MediaSession2} creations + * and their ongoing media playback state. + * @hide + */ +public class MediaCommunicationService extends SystemService { + + public MediaCommunicationService(Context context) { + super(context); + } + + @Override + public void onStart() { + publishBinderService(Context.MEDIA_COMMUNICATION_SERVICE, new Stub()); + } + + private class Stub extends IMediaCommunicationService.Stub { + } +} diff --git a/core/api/current.txt b/core/api/current.txt index 0b81c902686b..c454666d52d9 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -10373,6 +10373,7 @@ package android.content { field public static final String LAUNCHER_APPS_SERVICE = "launcherapps"; field public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater"; field public static final String LOCATION_SERVICE = "location"; + field public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication"; field public static final String MEDIA_METRICS_SERVICE = "media_metrics"; field public static final String MEDIA_PROJECTION_SERVICE = "media_projection"; field public static final String MEDIA_ROUTER_SERVICE = "media_router"; @@ -15770,6 +15771,7 @@ package android.graphics { method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter); method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float); method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float, @NonNull android.graphics.RenderEffect); + method @NonNull public static android.graphics.RenderEffect createShaderEffect(@NonNull android.graphics.Shader); } public final class RenderNode { @@ -16798,6 +16800,18 @@ package android.graphics.text { package android.hardware { + public abstract class Battery { + ctor public Battery(); + method @FloatRange(from=-1.0F, to=1.0f) public abstract float getCapacity(); + method public abstract int getStatus(); + method public abstract boolean hasBattery(); + field public static final int STATUS_CHARGING = 2; // 0x2 + field public static final int STATUS_DISCHARGING = 3; // 0x3 + field public static final int STATUS_FULL = 5; // 0x5 + field public static final int STATUS_NOT_CHARGING = 4; // 0x4 + field public static final int STATUS_UNKNOWN = 1; // 0x1 + } + @Deprecated public class Camera { method @Deprecated public final void addCallbackBuffer(byte[]); method @Deprecated public final void autoFocus(android.hardware.Camera.AutoFocusCallback); @@ -18498,6 +18512,7 @@ package android.hardware.input { public final class InputManager { method public android.view.InputDevice getInputDevice(int); method public int[] getInputDeviceIds(); + method public float getMaximumObscuringOpacityForTouch(); method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler); method public void unregisterInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener); method @Nullable public android.view.VerifiedInputEvent verifyInputEvent(@NonNull android.view.InputEvent); @@ -38576,6 +38591,7 @@ package android.speech { public class SpeechRecognizer { method public void cancel(); + method @NonNull public static android.speech.SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull android.content.Context); method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context); method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context, android.content.ComponentName); method public void destroy(); @@ -39713,7 +39729,6 @@ package android.telecom { field public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE"; field public static final String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS"; field public static final String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS"; - field public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE"; field public static final String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE"; field public static final String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP"; field public static final String EXTRA_LOCATION = "android.telecom.extra.LOCATION"; @@ -39722,6 +39737,7 @@ package android.telecom { field public static final String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS"; field public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE"; field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE"; + field public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI"; field public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY"; field public static final String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT"; field public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE"; @@ -46362,6 +46378,7 @@ package android.view { public final class InputDevice implements android.os.Parcelable { method public int describeContents(); + method @NonNull public android.hardware.Battery getBattery(); method public int getControllerNumber(); method public String getDescriptor(); method public static android.view.InputDevice getDevice(int); @@ -46416,6 +46433,7 @@ package android.view { field public static final int SOURCE_MOUSE = 8194; // 0x2002 field public static final int SOURCE_MOUSE_RELATIVE = 131076; // 0x20004 field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000 + field public static final int SOURCE_SENSOR = 67108864; // 0x4000000 field public static final int SOURCE_STYLUS = 16386; // 0x4002 field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008 field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002 diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 0915d0763604..4d2040b825c7 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -103,6 +103,7 @@ package android.media { } public class MediaServiceManager { + method @NonNull public android.media.MediaServiceManager.ServiceRegisterer getMediaCommunicationServiceRegisterer(); method @NonNull public android.media.MediaServiceManager.ServiceRegisterer getMediaSessionServiceRegisterer(); method @NonNull public android.media.MediaServiceManager.ServiceRegisterer getMediaTranscodingServiceRegisterer(); } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2a2b799816c3..e8909c89b300 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5606,12 +5606,12 @@ package android.media.tv.tuner.filter { public class Filter implements java.lang.AutoCloseable { method public void close(); method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration); - method public int configureMonitorEvent(int); method public int flush(); method public int getId(); method public long getId64Bit(); method public int read(@NonNull byte[], long, long); method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter); + method public int setMonitorEventMask(int); method public int start(); method public int stop(); field public static final int MONITOR_EVENT_IP_CID_CHANGE = 2; // 0x2 @@ -9829,6 +9829,7 @@ package android.service.notification { method public void onNotificationDirectReplied(@NonNull String); method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification); method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel); + method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel, @NonNull android.service.notification.NotificationListenerService.RankingMap); method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean); method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String); method public void onNotificationVisibilityChanged(@NonNull String, boolean); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index c237d7d57238..5717e6676e98 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -931,9 +931,8 @@ package android.hardware.input { public final class InputManager { method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context); - method public float getMaximumObscuringOpacityForTouch(@NonNull android.content.Context); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, float); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(float); field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 7eda50e5c9cb..ea7e5ea7c802 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -3198,6 +3198,61 @@ public final class BluetoothAdapter { } /** + * Register a callback to receive events whenever the bluetooth stack goes down and back up, + * e.g. in the event the bluetooth is turned off/on via settings. + * + * If the bluetooth stack is currently up, there will not be an initial callback call. + * You can use the return value as an indication of this being the case. + * + * Callbacks will be delivered on a binder thread. + * + * @return whether bluetooth is already up currently + * + * @hide + */ + public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) { + return getBluetoothService(callback.mRemote) != null; + } + + /** + * Unregister a callback registered via {@link #registerServiceLifecycleCallback} + * + * @hide + */ + public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) { + removeServiceStateCallback(callback.mRemote); + } + + /** + * A callback for {@link #registerServiceLifecycleCallback} + * + * @hide + */ + public abstract static class ServiceLifecycleCallback { + + /** Called when the bluetooth stack is up */ + public abstract void onBluetoothServiceUp(); + + /** Called when the bluetooth stack is down */ + public abstract void onBluetoothServiceDown(); + + IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() { + @Override + public void onBluetoothServiceUp(IBluetooth bluetoothService) { + ServiceLifecycleCallback.this.onBluetoothServiceUp(); + } + + @Override + public void onBluetoothServiceDown() { + ServiceLifecycleCallback.this.onBluetoothServiceDown(); + } + + @Override + public void onBrEdrDown() {} + }; + } + + /** * Starts a scan for Bluetooth LE devices. * * <p>Results of the scan are reported using the diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java index 17bf11b3d4ef..960a08755cb8 100644 --- a/core/java/android/companion/Association.java +++ b/core/java/android/companion/Association.java @@ -38,7 +38,7 @@ public final class Association implements Parcelable { private final @NonNull String mDeviceMacAddress; private final @NonNull String mPackageName; private final @Nullable String mDeviceProfile; - private final boolean mKeepProfilePrivilegesWhenDeviceAway; + private final boolean mNotifyOnDeviceNearby; /** @hide */ public int getUserId() { @@ -47,7 +47,7 @@ public final class Association implements Parcelable { - // Code below generated by codegen v1.0.21. + // Code below generated by codegen v1.0.22. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -71,7 +71,7 @@ public final class Association implements Parcelable { @NonNull String deviceMacAddress, @NonNull String packageName, @Nullable String deviceProfile, - boolean keepProfilePrivilegesWhenDeviceAway) { + boolean notifyOnDeviceNearby) { this.mUserId = userId; com.android.internal.util.AnnotationValidations.validate( UserIdInt.class, null, mUserId); @@ -82,7 +82,7 @@ public final class Association implements Parcelable { com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mPackageName); this.mDeviceProfile = deviceProfile; - this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway; + this.mNotifyOnDeviceNearby = notifyOnDeviceNearby; // onConstructed(); // You can define this method to get a callback } @@ -103,8 +103,8 @@ public final class Association implements Parcelable { } @DataClass.Generated.Member - public boolean isKeepProfilePrivilegesWhenDeviceAway() { - return mKeepProfilePrivilegesWhenDeviceAway; + public boolean isNotifyOnDeviceNearby() { + return mNotifyOnDeviceNearby; } @Override @@ -118,7 +118,7 @@ public final class Association implements Parcelable { "deviceMacAddress = " + mDeviceMacAddress + ", " + "packageName = " + mPackageName + ", " + "deviceProfile = " + mDeviceProfile + ", " + - "keepProfilePrivilegesWhenDeviceAway = " + mKeepProfilePrivilegesWhenDeviceAway + + "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby + " }"; } @@ -139,7 +139,7 @@ public final class Association implements Parcelable { && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress) && Objects.equals(mPackageName, that.mPackageName) && Objects.equals(mDeviceProfile, that.mDeviceProfile) - && mKeepProfilePrivilegesWhenDeviceAway == that.mKeepProfilePrivilegesWhenDeviceAway; + && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby; } @Override @@ -153,7 +153,7 @@ public final class Association implements Parcelable { _hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress); _hash = 31 * _hash + Objects.hashCode(mPackageName); _hash = 31 * _hash + Objects.hashCode(mDeviceProfile); - _hash = 31 * _hash + Boolean.hashCode(mKeepProfilePrivilegesWhenDeviceAway); + _hash = 31 * _hash + Boolean.hashCode(mNotifyOnDeviceNearby); return _hash; } @@ -164,7 +164,7 @@ public final class Association implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mKeepProfilePrivilegesWhenDeviceAway) flg |= 0x10; + if (mNotifyOnDeviceNearby) flg |= 0x10; if (mDeviceProfile != null) flg |= 0x8; dest.writeByte(flg); dest.writeInt(mUserId); @@ -185,7 +185,7 @@ public final class Association implements Parcelable { // static FieldType unparcelFieldName(Parcel in) { ... } byte flg = in.readByte(); - boolean keepProfilePrivilegesWhenDeviceAway = (flg & 0x10) != 0; + boolean notifyOnDeviceNearby = (flg & 0x10) != 0; int userId = in.readInt(); String deviceMacAddress = in.readString(); String packageName = in.readString(); @@ -201,7 +201,7 @@ public final class Association implements Parcelable { com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mPackageName); this.mDeviceProfile = deviceProfile; - this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway; + this.mNotifyOnDeviceNearby = notifyOnDeviceNearby; // onConstructed(); // You can define this method to get a callback } @@ -221,10 +221,10 @@ public final class Association implements Parcelable { }; @DataClass.Generated( - time = 1606940835778L, - codegenVersion = "1.0.21", + time = 1610482674799L, + codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/companion/Association.java", - inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mKeepProfilePrivilegesWhenDeviceAway\npublic int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)") + inputSignatures = "private final @android.annotation.UserIdInt int mUserId\nprivate final @android.annotation.NonNull java.lang.String mDeviceMacAddress\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate final boolean mNotifyOnDeviceNearby\npublic int getUserId()\nclass Association extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index d7dc86a5c59f..46d8900e59a1 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -63,7 +63,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; -import android.system.Int32Ref; +import android.system.Int64Ref; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -4050,7 +4050,7 @@ public abstract class ContentResolver implements ContentInterface { // Convert to Point, since that's what the API is defined as final Bundle opts = new Bundle(); opts.putParcelable(EXTRA_SIZE, new Point(size.getWidth(), size.getHeight())); - final Int32Ref orientation = new Int32Ref(0); + final Int64Ref orientation = new Int64Ref(0); Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> { final AssetFileDescriptor afd = content.openTypedAssetFile(uri, "image/*", opts, diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index c5206d77ea50..bed42f4f8b6d 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -64,7 +64,6 @@ import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.StatFs; -import android.os.StrictMode; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; @@ -3575,6 +3574,7 @@ public abstract class Context { LIGHTS_SERVICE, //@hide: PEOPLE_SERVICE, //@hide: DEVICE_STATE_SERVICE, + //@hide: SPEECH_RECOGNITION_SERVICE, UWB_SERVICE, MEDIA_METRICS_SERVICE, }) @@ -4371,6 +4371,16 @@ public abstract class Context { public static final String BIOMETRIC_SERVICE = "biometric"; /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.media.MediaCommunicationManager} + * for managing {@link android.media.MediaSession2}. + * + * @see #getSystemService(String) + * @see android.media.MediaCommunicationManager + */ + public static final String MEDIA_COMMUNICATION_SERVICE = "media_communication"; + + /** * Use with {@link #getSystemService} to retrieve a * {@link android.media.MediaRouter} for controlling and managing * routing of media. @@ -5410,6 +5420,14 @@ public abstract class Context { public static final String MEDIA_METRICS_SERVICE = "media_metrics"; /** + * Use with {@link #getSystemService(String)} to access system speech recognition service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String SPEECH_RECOGNITION_SERVICE = "speech_recognition"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * @@ -6469,7 +6487,7 @@ public abstract class Context { * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI * contexts throws {@link android.os.strictmode.Violation} if - * {@link StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled. + * {@link android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled. * <p> * Examples of UI contexts are * an {@link android.app.Activity Activity}, a context created from @@ -6479,7 +6497,7 @@ public abstract class Context { * * @see #getDisplay() * @see #getSystemService(String) - * @see StrictMode.VmPolicy.Builder#detectIncorrectContextUse() + * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse() */ public static boolean isUiContext(@NonNull Context context) { return context.isUiContext(); diff --git a/core/java/android/hardware/Battery.java b/core/java/android/hardware/Battery.java new file mode 100644 index 000000000000..24c8d76a9813 --- /dev/null +++ b/core/java/android/hardware/Battery.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +import android.annotation.FloatRange; +import android.annotation.IntDef; +import android.os.BatteryManager; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The Battery class is a representation of a single battery on a device. + */ +public abstract class Battery { + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "STATUS_" }, value = { + STATUS_UNKNOWN, + STATUS_CHARGING, + STATUS_DISCHARGING, + STATUS_NOT_CHARGING, + STATUS_FULL + }) + public @interface BatteryStatus { + } + + /** Battery status is unknown. */ + public static final int STATUS_UNKNOWN = BatteryManager.BATTERY_STATUS_UNKNOWN; + /** Battery is charging. */ + public static final int STATUS_CHARGING = BatteryManager.BATTERY_STATUS_CHARGING; + /** Battery is discharging. */ + public static final int STATUS_DISCHARGING = BatteryManager.BATTERY_STATUS_DISCHARGING; + /** Battery is connected to power but not charging. */ + public static final int STATUS_NOT_CHARGING = BatteryManager.BATTERY_STATUS_NOT_CHARGING; + /** Battery is full. */ + public static final int STATUS_FULL = BatteryManager.BATTERY_STATUS_FULL; + + /** + * Check whether the hardware has a battery. + * + * @return True if the hardware has a battery, else false. + */ + public abstract boolean hasBattery(); + + /** + * Get the battery status. + * + * @return the battery status. + */ + public abstract @BatteryStatus int getStatus(); + + /** + * Get remaining battery capacity as float percentage [0.0f, 1.0f] of total capacity + * Returns -1 when battery capacity can't be read. + * + * @return the battery capacity. + */ + public abstract @FloatRange(from = -1.0f, to = 1.0f) float getCapacity(); +} diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index 4145a7273ed2..08b1e245dc83 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -29,7 +29,6 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; import android.os.RemoteException; -import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.util.Slog; @@ -47,6 +46,13 @@ public class BiometricManager { private static final String TAG = "BiometricManager"; /** + * An ID that should match any biometric sensor on the device. + * + * @hide + */ + public static final int SENSOR_ID_ANY = -1; + + /** * No error detected. */ public static final int BIOMETRIC_SUCCESS = @@ -139,7 +145,7 @@ public class BiometricManager { * * <p>This corresponds to {@link KeyProperties#AUTH_BIOMETRIC_STRONG} during key generation. * - * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) + * @see android.security.keystore.KeyGenParameterSpec.Builder */ int BIOMETRIC_STRONG = 0x000F; @@ -182,7 +188,7 @@ public class BiometricManager { * <p>This corresponds to {@link KeyProperties#AUTH_DEVICE_CREDENTIAL} during key * generation. * - * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) + * @see android.security.keystore.KeyGenParameterSpec.Builder */ int DEVICE_CREDENTIAL = 1 << 15; } diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 76cf9b9d28b9..4f6a7c75cca6 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -36,7 +36,6 @@ import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.security.identity.IdentityCredential; -import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.text.TextUtils; import android.util.Log; @@ -325,7 +324,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * request authentication with the proper set of authenticators (e.g. match the * authenticators specified during key generation). * - * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) + * @see android.security.keystore.KeyGenParameterSpec.Builder * @see KeyProperties#AUTH_BIOMETRIC_STRONG * @see KeyProperties#AUTH_DEVICE_CREDENTIAL * @@ -365,6 +364,21 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** + * If set, authenticate using the biometric sensor with the given ID. + * + * @param sensorId The ID of a biometric sensor, or -1 to allow any sensor (default). + * @return This builder. + * + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + @NonNull + public Builder setSensorId(int sensorId) { + mPromptInfo.setSensorId(sensorId); + return this; + } + + /** * Creates a {@link BiometricPrompt}. * * @return An instance of {@link BiometricPrompt}. @@ -589,7 +603,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and * time-based. This is specified during key creation via the timeout parameter of the - * {@link KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)} API. + * {@code setUserAuthenticationParameters(int, int)} method of {@link + * android.security.keystore.KeyGenParameterSpec.Builder}. * * <p>CryptoObjects are used to unlock auth-per-use keys via * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor, @@ -778,6 +793,27 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId) { + authenticateUserForOperation(cancel, executor, callback, userId, 0 /* operationId */); + } + + /** + * Authenticates for the given user and keystore operation. + * + * @param cancel An object that can be used to cancel authentication + * @param executor An executor to handle callback events + * @param callback An object to receive authentication events + * @param userId The user to authenticate + * @param operationId The keystore operation associated with authentication + * + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void authenticateUserForOperation( + @NonNull CancellationSignal cancel, + @NonNull @CallbackExecutor Executor executor, + @NonNull AuthenticationCallback callback, + int userId, + long operationId) { if (cancel == null) { throw new IllegalArgumentException("Must supply a cancellation signal"); } @@ -787,7 +823,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (callback == null) { throw new IllegalArgumentException("Must supply a callback"); } - authenticateInternal(null /* crypto */, cancel, executor, callback, userId); + authenticateInternal(operationId, cancel, executor, callback, userId); } /** @@ -912,11 +948,31 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } } - private void authenticateInternal(@Nullable CryptoObject crypto, + private void authenticateInternal( + @Nullable CryptoObject crypto, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId) { + + mCryptoObject = crypto; + final long operationId = crypto != null ? crypto.getOpId() : 0L; + authenticateInternal(operationId, cancel, executor, callback, userId); + } + + private void authenticateInternal( + long operationId, + @NonNull CancellationSignal cancel, + @NonNull @CallbackExecutor Executor executor, + @NonNull AuthenticationCallback callback, + int userId) { + + // Ensure we don't return the wrong crypto object as an auth result. + if (mCryptoObject != null && mCryptoObject.getOpId() != operationId) { + Log.w(TAG, "CryptoObject operation ID does not match argument; setting field to null"); + mCryptoObject = null; + } + try { if (cancel.isCanceled()) { Log.w(TAG, "Authentication already canceled"); @@ -925,13 +981,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan cancel.setOnCancelListener(new OnAuthenticationCancelListener()); } - mCryptoObject = crypto; mExecutor = executor; mAuthenticationCallback = callback; - final long operationId = crypto != null ? crypto.getOpId() : 0; final PromptInfo promptInfo; - if (crypto != null) { + if (operationId != 0L) { // Allowed authenticators should default to BIOMETRIC_STRONG for crypto auth. // Note that we use a new PromptInfo here so as to not overwrite the application's // preference, since it is possible that the same prompt configuration be used @@ -952,10 +1006,9 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } catch (RemoteException e) { Log.e(TAG, "Remote exception while authenticating", e); - mExecutor.execute(() -> { - callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE, - mContext.getString(R.string.biometric_error_hw_unavailable)); - }); + mExecutor.execute(() -> callback.onAuthenticationError( + BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE, + mContext.getString(R.string.biometric_error_hw_unavailable))); } } } diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index c2eff7de832b..0e99f31d3b52 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -40,6 +40,7 @@ public class PromptInfo implements Parcelable { private @BiometricManager.Authenticators.Types int mAuthenticators; private boolean mDisallowBiometricsIfPolicyExists; private boolean mReceiveSystemEvents; + private int mSensorId = -1; public PromptInfo() { @@ -59,6 +60,7 @@ public class PromptInfo implements Parcelable { mAuthenticators = in.readInt(); mDisallowBiometricsIfPolicyExists = in.readBoolean(); mReceiveSystemEvents = in.readBoolean(); + mSensorId = in.readInt(); } public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() { @@ -93,6 +95,7 @@ public class PromptInfo implements Parcelable { dest.writeInt(mAuthenticators); dest.writeBoolean(mDisallowBiometricsIfPolicyExists); dest.writeBoolean(mReceiveSystemEvents); + dest.writeInt(mSensorId); } public boolean containsPrivateApiConfigurations() { @@ -166,6 +169,10 @@ public class PromptInfo implements Parcelable { mReceiveSystemEvents = receiveSystemEvents; } + public void setSensorId(int sensorId) { + mSensorId = sensorId; + } + // Getters public CharSequence getTitle() { @@ -226,4 +233,8 @@ public class PromptInfo implements Parcelable { public boolean isReceiveSystemEvents() { return mReceiveSystemEvents; } + + public int getSensorId() { + return mSensorId; + } } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index d93286531465..66b9600941ae 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -24,7 +24,6 @@ import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; @@ -56,8 +55,6 @@ import android.security.identity.IdentityCredential; import android.util.Slog; import android.view.Surface; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.security.Signature; import java.util.ArrayList; import java.util.List; @@ -98,13 +95,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ public static final int SENSOR_ID_ANY = -1; - /** - * @hide - */ - @IntDef({SENSOR_ID_ANY}) - @Retention(RetentionPolicy.SOURCE) - public @interface SensorId {} - private IFingerprintService mService; private Context mContext; private IBinder mToken = new Binder(); @@ -508,8 +498,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, - @NonNull AuthenticationCallback callback, Handler handler, @SensorId int sensorId, - int userId) { + @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId) { + if (callback == null) { throw new IllegalArgumentException("Must supply an authentication callback"); } @@ -653,15 +643,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(MANAGE_FINGERPRINT) public void generateChallenge(int userId, GenerateChallengeCallback callback) { - final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties = - getSensorPropertiesInternal(); - if (fingerprintSensorProperties.isEmpty()) { + final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor(); + if (sensorProps == null) { Slog.e(TAG, "No sensors"); return; } - - final int sensorId = fingerprintSensorProperties.get(0).sensorId; - generateChallenge(sensorId, userId, callback); + generateChallenge(sensorProps.sensorId, userId, callback); } /** @@ -681,18 +668,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(MANAGE_FINGERPRINT) public void revokeChallenge(int userId, long challenge) { - if (mService != null) try { - final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties = - getSensorPropertiesInternal(); - if (fingerprintSensorProperties.isEmpty()) { - Slog.e(TAG, "No sensors"); - return; + if (mService != null) { + try { + final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor(); + if (sensorProps == null) { + Slog.e(TAG, "No sensors"); + return; + } + mService.revokeChallenge(mToken, sensorProps.sensorId, userId, + mContext.getOpPackageName(), challenge); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } - final int sensorId = fingerprintSensorProperties.get(0).sensorId; - mService.revokeChallenge(mToken, sensorId, userId, mContext.getOpPackageName(), - challenge); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); } } @@ -1161,6 +1148,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } + @Nullable + private FingerprintSensorPropertiesInternal getFirstFingerprintSensor() { + final List<FingerprintSensorPropertiesInternal> allSensors = getSensorPropertiesInternal(); + return allSensors.isEmpty() ? null : allSensors.get(0); + } + private void cancelEnrollment() { if (mService != null) try { mService.cancelEnrollment(mToken); diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index b39df4d2b6bf..c69c47f80d01 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -93,6 +93,10 @@ interface IInputManager { int[] getVibratorIds(int deviceId); boolean isVibrating(int deviceId); + // Input device battery query. + int getBatteryStatus(int deviceId); + int getBatteryCapacity(int deviceId); + void setPointerIconType(int typeId); void setCustomPointerIcon(in PointerIcon icon); diff --git a/core/java/android/hardware/input/InputDeviceBattery.java b/core/java/android/hardware/input/InputDeviceBattery.java new file mode 100644 index 000000000000..0fe124e2c0ce --- /dev/null +++ b/core/java/android/hardware/input/InputDeviceBattery.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.input; + +import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; +import static android.os.IInputConstants.INVALID_BATTERY_CAPACITY; + +import android.hardware.Battery; + +/** + * Battery implementation for input devices. + * + * @hide + */ +public final class InputDeviceBattery extends Battery { + private static final float NULL_BATTERY_CAPACITY = -1.0f; + + private final InputManager mInputManager; + private final int mDeviceId; + private final boolean mHasBattery; + + InputDeviceBattery(InputManager inputManager, int deviceId, boolean hasBattery) { + mInputManager = inputManager; + mDeviceId = deviceId; + mHasBattery = hasBattery; + } + + @Override + public boolean hasBattery() { + return mHasBattery; + } + + @Override + public int getStatus() { + if (!mHasBattery) { + return BATTERY_STATUS_UNKNOWN; + } + return mInputManager.getBatteryStatus(mDeviceId); + } + + @Override + public float getCapacity() { + if (mHasBattery) { + int capacity = mInputManager.getBatteryCapacity(mDeviceId); + if (capacity != INVALID_BATTERY_CAPACITY) { + return (float) capacity / 100.0f; + } + } + return NULL_BATTERY_CAPACITY; + } +} diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 6ab11068aac7..185c59d8ccfc 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -25,6 +25,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; import android.annotation.TestApi; +import android.app.ActivityThread; import android.compat.annotation.ChangeId; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -909,18 +910,17 @@ public final class InputManager { } /** - * Returns the maximum allowed obscuring opacity by UID to propagate touches. + * Returns the maximum allowed obscuring opacity per UID to propagate touches. * - * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams - * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows - * above the touch-consuming window. - * - * @see #setMaximumObscuringOpacityForTouch(Context, float) + * <p>For certain window types (eg. {@link LayoutParams#TYPE_APPLICATION_OVERLAY}), the decision + * of honoring {@link LayoutParams#FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring + * opacity of the windows above the touch-consuming window, per UID. Check documentation of + * {@link LayoutParams#FLAG_NOT_TOUCHABLE} for more details. * - * @hide + * @see LayoutParams#FLAG_NOT_TOUCHABLE */ - @TestApi - public float getMaximumObscuringOpacityForTouch(@NonNull Context context) { + public float getMaximumObscuringOpacityForTouch() { + Context context = ActivityThread.currentApplication(); return Settings.Global.getFloat(context.getContentResolver(), Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH); @@ -946,17 +946,18 @@ public final class InputManager { * * This value should be between 0 (inclusive) and 1 (inclusive). * - * @see #getMaximumObscuringOpacityForTouch(Context) + * @see #getMaximumObscuringOpacityForTouch() * * @hide */ @TestApi @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) - public void setMaximumObscuringOpacityForTouch(@NonNull Context context, float opacity) { + public void setMaximumObscuringOpacityForTouch(float opacity) { if (opacity < 0 || opacity > 1) { throw new IllegalArgumentException( "Maximum obscuring opacity for touch should be >= 0 and <= 1"); } + Context context = ActivityThread.currentApplication(); Settings.Global.putFloat(context.getContentResolver(), Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity); } @@ -1244,6 +1245,32 @@ public final class InputManager { } /** + * Get the battery status of the input device + * @param deviceId The input device ID + * @hide + */ + public int getBatteryStatus(int deviceId) { + try { + return mIm.getBatteryStatus(deviceId); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Get the remaining battery capacity of the input device + * @param deviceId The input device ID + * @hide + */ + public int getBatteryCapacity(int deviceId) { + try { + return mIm.getBatteryCapacity(deviceId); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** * Add a runtime association between the input port and the display port. This overrides any * static associations. * @param inputPort The port of the input device. @@ -1470,6 +1497,15 @@ public final class InputManager { } /** + * Gets a battery object associated with an input device, assuming it has one. + * @return The battery, never null. + * @hide + */ + public InputDeviceBattery getInputDeviceBattery(int deviceId, boolean hasBattery) { + return new InputDeviceBattery(this, deviceId, hasBattery); + } + + /** * Listens for changes in input devices. */ public interface InputDeviceListener { diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 33e33ee5fa02..9bfd75ef2170 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -249,11 +249,13 @@ public class CallLog { // Nasty casework for the shadow calllog begins... // First see if we're just inserting for one user. If so, insert into the shadow // based on whether that user is unlocked. - if (user != null) { - Uri baseUri = userManager.isUserUnlocked(user) ? CALL_COMPOSER_PICTURE_URI + UserHandle realUser = UserHandle.CURRENT.equals(user) + ? android.os.Process.myUserHandle() : user; + if (realUser != null) { + Uri baseUri = userManager.isUserUnlocked(realUser) ? CALL_COMPOSER_PICTURE_URI : SHADOW_CALL_COMPOSER_PICTURE_URI; Uri pictureInsertionUri = ContentProvider.maybeAddUserId(baseUri, - user.getIdentifier()); + realUser.getIdentifier()); Log.i(LOG_TAG, "Inserting call composer for single user at " + pictureInsertionUri); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 79f055e882aa..fbeb5dea8b47 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8055,6 +8055,15 @@ public final class Settings { "one_handed_tutorial_show_count"; /** + * Indicates whether transform is enabled. + * <p> + * Type: int (0 for false, 1 for true) + * + * @hide + */ + public static final String TRANSFORM_ENABLED = "transform_enabled"; + + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per * UiModeManager. @@ -14721,9 +14730,8 @@ public final class Settings { * touch, allow the UID to propagate the touch. * </ul> * - * @see android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch(Context) - * @see android.hardware.input.InputManager#setMaximumObscuringOpacityForTouch(Context, - * float) + * @see android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch() + * @see android.hardware.input.InputManager#setMaximumObscuringOpacityForTouch(float) * * @hide */ diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index e0f3018e3d0e..44daeff76997 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -46,7 +46,7 @@ oneway interface INotificationListener void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType); // assistants only - void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel); + void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel, in NotificationRankingUpdate update); void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId); void onNotificationsSeen(in List<String> keys); void onPanelRevealed(int items); diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index cf2152cc3ad4..1d49a7206023 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -126,7 +126,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p> * * @param sbn the new notification - * @return an adjustment or null to take no action, within 100ms. + * @return an adjustment or null to take no action, within 200ms. */ abstract public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn); @@ -135,7 +135,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS * * @param sbn the new notification * @param channel the channel the notification was posted to - * @return an adjustment or null to take no action, within 100ms. + * @return an adjustment or null to take no action, within 200ms. */ public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn, @NonNull NotificationChannel channel) { @@ -143,6 +143,20 @@ public abstract class NotificationAssistantService extends NotificationListenerS } /** + * A notification was posted by an app. Called before post. + * + * @param sbn the new notification + * @param channel the channel the notification was posted to + * @param rankingMap The current ranking map that can be used to retrieve ranking information + * for active notifications. + * @return an adjustment or null to take no action, within 200ms. + */ + public @Nullable Adjustment onNotificationEnqueued(@NonNull StatusBarNotification sbn, + @NonNull NotificationChannel channel, @NonNull RankingMap rankingMap) { + return onNotificationEnqueued(sbn, channel); + } + + /** * Implement this method to learn when notifications are removed, how they were interacted with * before removal, and why they were removed. * <p> @@ -316,7 +330,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper { @Override public void onNotificationEnqueuedWithChannel(IStatusBarNotificationHolder sbnHolder, - NotificationChannel channel) { + NotificationChannel channel, NotificationRankingUpdate update) { StatusBarNotification sbn; try { sbn = sbnHolder.get(); @@ -330,9 +344,11 @@ public abstract class NotificationAssistantService extends NotificationListenerS return; } + applyUpdateLocked(update); SomeArgs args = SomeArgs.obtain(); args.arg1 = sbn; args.arg2 = channel; + args.arg3 = getCurrentRanking(); mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED, args).sendToTarget(); } @@ -472,8 +488,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS SomeArgs args = (SomeArgs) msg.obj; StatusBarNotification sbn = (StatusBarNotification) args.arg1; NotificationChannel channel = (NotificationChannel) args.arg2; + RankingMap ranking = (RankingMap) args.arg3; args.recycle(); - Adjustment adjustment = onNotificationEnqueued(sbn, channel); + Adjustment adjustment = onNotificationEnqueued(sbn, channel, ranking); setAdjustmentIssuer(adjustment); if (adjustment != null) { if (!isBound()) { diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index c41e599d5cae..64cddc35d2bb 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1431,7 +1431,8 @@ public abstract class NotificationListenerService extends Service { @Override public void onNotificationEnqueuedWithChannel( - IStatusBarNotificationHolder notificationHolder, NotificationChannel channel) + IStatusBarNotificationHolder notificationHolder, NotificationChannel channel, + NotificationRankingUpdate update) throws RemoteException { // no-op in the listener } diff --git a/core/java/android/speech/IRecognitionServiceManager.aidl b/core/java/android/speech/IRecognitionServiceManager.aidl new file mode 100644 index 000000000000..7158ba2f9f63 --- /dev/null +++ b/core/java/android/speech/IRecognitionServiceManager.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.speech; + +import android.speech.IRecognitionServiceManagerCallback; + +/** + * Binder service allowing speech recognition proxied by the system. + * + * {@hide} + */ +interface IRecognitionServiceManager { + void createSession(in IRecognitionServiceManagerCallback callback); +} diff --git a/core/java/android/speech/IRecognitionServiceManagerCallback.aidl b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl new file mode 100644 index 000000000000..d760810deda8 --- /dev/null +++ b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.speech; + +import android.speech.IRecognitionService; + +/** + * Callback for the service allowing speech recognition proxied by the system. + * + * {@hide} + */ +oneway interface IRecognitionServiceManagerCallback { + void onSuccess(in IRecognitionService service); + void onError(); +} diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java index 5fd192afed00..c97dbfe38ead 100644 --- a/core/java/android/speech/RecognitionService.java +++ b/core/java/android/speech/RecognitionService.java @@ -28,6 +28,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -50,7 +51,9 @@ public abstract class RecognitionService extends Service { /** * Name under which a RecognitionService component publishes information about itself. * This meta-data should reference an XML resource containing a - * <code><{@link android.R.styleable#RecognitionService recognition-service}></code> tag. + * <code><{@link android.R.styleable#RecognitionService recognition-service}></code> or + * <code><{@link android.R.styleable#RecognitionService on-device-recognition-service} + * ></code> tag. */ public static final String SERVICE_META_DATA = "android.speech"; @@ -182,6 +185,13 @@ public abstract class RecognitionService extends Service { private boolean checkPermissions(IRecognitionListener listener, boolean forDataDelivery, @NonNull String packageName, @Nullable String featureId) { if (DBG) Log.d(TAG, "checkPermissions"); + + final int callingUid = Binder.getCallingUid(); + if (callingUid == Process.SYSTEM_UID) { + // Assuming system has verified permissions of the caller. + return true; + } + if (forDataDelivery) { if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this, android.Manifest.permission.RECORD_AUDIO, packageName, featureId, @@ -342,6 +352,7 @@ public abstract class RecognitionService extends Service { * Return the Linux uid assigned to the process that sent you the current transaction that * is being processed. This is obtained from {@link Binder#getCallingUid()}. */ + // TODO(b/176578753): need to make sure this is fixed when proxied through system. public int getCallingUid() { return mCallingUid; } diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java index aea94bfb1bbb..de879c63a1a6 100644 --- a/core/java/android/speech/SpeechRecognizer.java +++ b/core/java/android/speech/SpeechRecognizer.java @@ -16,6 +16,7 @@ package android.speech; +import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -27,6 +28,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.ServiceManager; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; @@ -38,8 +40,9 @@ import java.util.Queue; /** * This class provides access to the speech recognition service. This service allows access to the * speech recognizer. Do not instantiate this class directly, instead, call - * {@link SpeechRecognizer#createSpeechRecognizer(Context)}. This class's methods must be - * invoked only from the main application thread. + * {@link SpeechRecognizer#createSpeechRecognizer(Context)}, or + * {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)}. This class's methods must be + * invoked only from the main application thread. * * <p>The implementation of this API is likely to stream audio to remote servers to perform speech * recognition. As such this API is not intended to be used for continuous recognition, which would @@ -122,8 +125,13 @@ public class SpeechRecognizer { /** Component to direct service intent to */ private final ComponentName mServiceComponent; + /** Whether to use on-device speech recognizer. */ + private final boolean mOnDevice; + + private IRecognitionServiceManager mManagerService; + /** Handler that will execute the main tasks */ - private Handler mHandler = new Handler() { + private Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -159,6 +167,17 @@ public class SpeechRecognizer { private SpeechRecognizer(final Context context, final ComponentName serviceComponent) { mContext = context; mServiceComponent = serviceComponent; + mOnDevice = false; + } + + /** + * The right way to create a {@code SpeechRecognizer} is by using + * {@link #createOnDeviceSpeechRecognizer} static factory method + */ + private SpeechRecognizer(final Context context, boolean onDevice) { + mContext = context; + mServiceComponent = null; + mOnDevice = onDevice; } /** @@ -194,6 +213,7 @@ public class SpeechRecognizer { * @return {@code true} if recognition is available, {@code false} otherwise */ public static boolean isRecognitionAvailable(final Context context) { + // TODO(b/176578753): make sure this works well with system speech recognizers. final List<ResolveInfo> list = context.getPackageManager().queryIntentServices( new Intent(RecognitionService.SERVICE_INTERFACE), 0); return list != null && list.size() != 0; @@ -231,13 +251,32 @@ public class SpeechRecognizer { public static SpeechRecognizer createSpeechRecognizer(final Context context, final ComponentName serviceComponent) { if (context == null) { - throw new IllegalArgumentException("Context cannot be null)"); + throw new IllegalArgumentException("Context cannot be null"); } checkIsCalledFromMainThread(); return new SpeechRecognizer(context, serviceComponent); } /** + * Factory method to create a new {@code SpeechRecognizer}. + * + * <p>Please note that {@link #setRecognitionListener(RecognitionListener)} should be called + * before dispatching any command to the created {@code SpeechRecognizer}, otherwise no + * notifications will be received. + * + * @param context in which to create {@code SpeechRecognizer} + * @return a new on-device {@code SpeechRecognizer}. + */ + @NonNull + public static SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull final Context context) { + if (context == null) { + throw new IllegalArgumentException("Context cannot be null"); + } + checkIsCalledFromMainThread(); + return new SpeechRecognizer(context, /* onDevice */ true); + } + + /** * Sets the listener that will receive all the callbacks. The previous unfinished commands will * be executed with the old listener, while any following command will be executed with the new * listener. @@ -265,34 +304,72 @@ public class SpeechRecognizer { } checkIsCalledFromMainThread(); if (mConnection == null) { // first time connection - mConnection = new Connection(); - - Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE); - - if (mServiceComponent == null) { - String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.VOICE_RECOGNITION_SERVICE); - - if (TextUtils.isEmpty(serviceComponent)) { - Log.e(TAG, "no selected voice recognition service"); - mListener.onError(ERROR_CLIENT); - return; - } - - serviceIntent.setComponent(ComponentName.unflattenFromString(serviceComponent)); + // TODO(b/176578753): both flows should go through system service. + if (mOnDevice) { + connectToSystemService(); } else { - serviceIntent.setComponent(mServiceComponent); + connectToService(); } - if (!mContext.bindService(serviceIntent, mConnection, - Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) { - Log.e(TAG, "bind to recognition service failed"); - mConnection = null; - mService = null; + } + putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent)); + } + + private void connectToSystemService() { + mManagerService = IRecognitionServiceManager.Stub.asInterface( + ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE)); + + if (mManagerService == null) { + mListener.onError(ERROR_CLIENT); + return; + } + + try { + // TODO(b/176578753): this has to supply information on whether to use on-device impl. + mManagerService.createSession(new IRecognitionServiceManagerCallback.Stub(){ + @Override + public void onSuccess(IRecognitionService service) throws RemoteException { + mService = service; + } + + @Override + public void onError() throws RemoteException { + Log.e(TAG, "Bind to system recognition service failed"); + mListener.onError(ERROR_CLIENT); + } + }); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + private void connectToService() { + mConnection = new Connection(); + + Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE); + + if (mServiceComponent == null) { + String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE); + + if (TextUtils.isEmpty(serviceComponent)) { + Log.e(TAG, "no selected voice recognition service"); mListener.onError(ERROR_CLIENT); return; } + + serviceIntent.setComponent( + ComponentName.unflattenFromString(serviceComponent)); + } else { + serviceIntent.setComponent(mServiceComponent); + } + if (!mContext.bindService(serviceIntent, mConnection, + Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) { + Log.e(TAG, "bind to recognition service failed"); + mConnection = null; + mService = null; + mListener.onError(ERROR_CLIENT); + return; } - putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent)); } /** @@ -378,7 +455,7 @@ public class SpeechRecognizer { mListener.onError(ERROR_CLIENT); } } - + private boolean checkOpenConnection() { if (mService != null) { return true; @@ -433,7 +510,7 @@ public class SpeechRecognizer { private final static int MSG_RMS_CHANGED = 8; private final static int MSG_ON_EVENT = 9; - private final Handler mInternalHandler = new Handler() { + private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { if (mInternalListener == null) { diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index f4b90e1f7b44..bc03222f390d 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -22,6 +22,7 @@ import android.annotation.RequiresPermission; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.hardware.Battery; import android.hardware.SensorManager; import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputManager; @@ -73,6 +74,7 @@ public final class InputDevice implements Parcelable { private final boolean mHasMicrophone; private final boolean mHasButtonUnderPad; private final boolean mHasSensor; + private final boolean mHasBattery; private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>(); @GuardedBy("mMotionRanges") @@ -84,6 +86,9 @@ public final class InputDevice implements Parcelable { @GuardedBy("mMotionRanges") private SensorManager mSensorManager; + @GuardedBy("mMotionRanges") + private Battery mBattery; + /** * A mask for input source classes. * @@ -323,6 +328,13 @@ public final class InputDevice implements Parcelable { public static final int SOURCE_HDMI = 0x02000000 | SOURCE_CLASS_BUTTON; /** + * The input source is a sensor associated with the input device. + * + * @see #SOURCE_CLASS_NONE + */ + public static final int SOURCE_SENSOR = 0x04000000 | SOURCE_CLASS_NONE; + + /** * A special input source constant that is used when filtering input devices * to match devices that provide any type of input source. */ @@ -448,7 +460,7 @@ public final class InputDevice implements Parcelable { public InputDevice(int id, int generation, int controllerNumber, String name, int vendorId, int productId, String descriptor, boolean isExternal, int sources, int keyboardType, KeyCharacterMap keyCharacterMap, boolean hasVibrator, boolean hasMicrophone, - boolean hasButtonUnderPad, boolean hasSensor) { + boolean hasButtonUnderPad, boolean hasSensor, boolean hasBattery) { mId = id; mGeneration = generation; mControllerNumber = controllerNumber; @@ -464,6 +476,7 @@ public final class InputDevice implements Parcelable { mHasMicrophone = hasMicrophone; mHasButtonUnderPad = hasButtonUnderPad; mHasSensor = hasSensor; + mHasBattery = hasBattery; mIdentifier = new InputDeviceIdentifier(descriptor, vendorId, productId); } @@ -483,6 +496,7 @@ public final class InputDevice implements Parcelable { mHasMicrophone = in.readInt() != 0; mHasButtonUnderPad = in.readInt() != 0; mHasSensor = in.readInt() != 0; + mHasBattery = in.readInt() != 0; mIdentifier = new InputDeviceIdentifier(mDescriptor, mVendorId, mProductId); int numRanges = in.readInt(); @@ -830,6 +844,22 @@ public final class InputDevice implements Parcelable { } /** + * Gets the battery object associated with the device, if there is one. + * Even if the device does not have a battery, the result is never null. + * Use {@link Battery#hasBattery} to determine whether a battery is + * present. + * + * @return The battery object associated with the device, never null. + */ + @NonNull + public Battery getBattery() { + if (mBattery == null) { + mBattery = InputManager.getInstance().getInputDeviceBattery(mId, mHasBattery); + } + return mBattery; + } + + /** * Gets the sensor manager service associated with the input device. * Even if the device does not have a sensor, the result is never null. * Use {@link SensorManager#getSensorList} to get a full list of all supported sensors. @@ -1051,6 +1081,7 @@ public final class InputDevice implements Parcelable { out.writeInt(mHasMicrophone ? 1 : 0); out.writeInt(mHasButtonUnderPad ? 1 : 0); out.writeInt(mHasSensor ? 1 : 0); + out.writeInt(mHasBattery ? 1 : 0); final int numRanges = mMotionRanges.size(); out.writeInt(numRanges); @@ -1097,6 +1128,8 @@ public final class InputDevice implements Parcelable { description.append(" Has Sensor: ").append(mHasSensor).append("\n"); + description.append(" Has battery: ").append(mHasBattery).append("\n"); + description.append(" Has mic: ").append(mHasMicrophone).append("\n"); description.append(" Sources: 0x").append(Integer.toHexString(mSources)).append(" ("); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 1327f9c6e827..6edae93c3542 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1555,17 +1555,26 @@ public interface WindowManager extends ViewManager { * <li><b>Fully transparent windows</b>: This window has {@link LayoutParams#alpha} equal * to 0. * <li><b>One SAW window with enough transparency</b>: This window is of type {@link - * #TYPE_APPLICATION_OVERLAY}, has {@link LayoutParams#alpha} below or equal to <b>0.8</b> - * and it's the <b>only</b> window of type {@link #TYPE_APPLICATION_OVERLAY} from this UID - * in the touch path. + * #TYPE_APPLICATION_OVERLAY}, has {@link LayoutParams#alpha} below or equal to the + * <a href="#MaximumOpacity">maximum obscuring opacity</a> (see below) and it's the + * <b>only</b> window of type {@link #TYPE_APPLICATION_OVERLAY} from this UID in the touch + * path. * <li><b>Multiple SAW windows with enough transparency</b>: The multiple overlapping * {@link #TYPE_APPLICATION_OVERLAY} windows in the * touch path from this UID have a <b>combined obscuring opacity</b> below or equal to - * <b>0.8</b>. See section below on how to compute this value. + * the <a href="#MaximumOpacity">maximum obscuring opacity</a>. See section + * <a href="#ObscuringOpacity">Combined obscuring opacity</a> below on how to compute this + * value. * </ol> * <p>If none of these cases hold, the touch will not be delivered and a message will be * logged to logcat.</p> * + * <a name="MaximumOpacity"></a> + * <h3>Maximum obscuring opacity</h3> + * <p>This value is <b>0.8</b>. Apps that want to gather this value from the system rather + * than hard-coding it might want to use {@link + * android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch()}.</p> + * * <a name="ObscuringOpacity"></a> * <h3>Combined obscuring opacity</h3> * diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index a21c68b4f01a..fcf8bb4e748b 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -41,7 +41,6 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; -import android.util.SparseLongArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; @@ -379,6 +378,7 @@ public class BatteryStatsHelper { mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge() * mPowerProfile.getBatteryCapacity()) / 100; + // Create list of (almost all) sippers, calculate their usage, and put them in mUsageList. processAppUsage(asUsers); Collections.sort(mUsageList); @@ -556,8 +556,7 @@ public class BatteryStatsHelper { } /** - * Mark the {@link BatterySipper} that we should hide and smear the screen usage based on - * foreground activity time. + * Mark the {@link BatterySipper} that we should hide. * * @param sippers sipper list that need to check and remove * @return the total power of the hidden items of {@link BatterySipper} @@ -565,7 +564,6 @@ public class BatteryStatsHelper { */ public double removeHiddenBatterySippers(List<BatterySipper> sippers) { double proportionalSmearPowerMah = 0; - BatterySipper screenSipper = null; for (int i = sippers.size() - 1; i >= 0; i--) { final BatterySipper sipper = sippers.get(i); sipper.shouldHide = shouldHideSipper(sipper); @@ -581,45 +579,11 @@ public class BatteryStatsHelper { proportionalSmearPowerMah += sipper.totalPowerMah; } } - - if (sipper.drainType == BatterySipper.DrainType.SCREEN) { - screenSipper = sipper; - } } - - smearScreenBatterySipper(sippers, screenSipper); - return proportionalSmearPowerMah; } /** - * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity - * time. - */ - public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) { - long totalActivityTimeMs = 0; - final SparseLongArray activityTimeArray = new SparseLongArray(); - for (int i = 0, size = sippers.size(); i < size; i++) { - final BatteryStats.Uid uid = sippers.get(i).uidObj; - if (uid != null) { - final long timeMs = getProcessForegroundTimeMs(uid, - BatteryStats.STATS_SINCE_CHARGED); - activityTimeArray.put(uid.getUid(), timeMs); - totalActivityTimeMs += timeMs; - } - } - - if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) { - final double screenPowerMah = screenSipper.totalPowerMah; - for (int i = 0, size = sippers.size(); i < size; i++) { - final BatterySipper sipper = sippers.get(i); - sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0) - / totalActivityTimeMs; - } - } - } - - /** * Check whether we should hide the battery sipper. */ public boolean shouldHideSipper(BatterySipper sipper) { @@ -682,33 +646,6 @@ public class BatteryStatsHelper { } @VisibleForTesting - public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { - final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); - if (timer != null) { - return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); - } - - return 0; - } - - @VisibleForTesting - public long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) { - final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime()); - final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP}; - - long timeUs = 0; - for (int type : foregroundTypes) { - final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which); - timeUs += localTime; - } - - // Return the min value of STATE_TOP time and foreground activity time, since both of these - // time have some errors. - return convertUsToMs( - Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs))); - } - - @VisibleForTesting public void setPackageManager(PackageManager packageManager) { mPackageManager = packageManager; } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index d475c658a3a0..2b034b0667d6 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -1099,16 +1099,6 @@ public class BatteryStatsImpl extends BatteryStats { private long[] mCpuFreqs; /** - * Times spent by the system server process grouped by cluster and CPU speed. - */ - private LongSamplingCounterArray mSystemServerCpuTimesUs; - - /** - * Times spent by the system server threads grouped by cluster and CPU speed. - */ - private LongSamplingCounterArray mSystemServerThreadCpuTimesUs; - - /** * Times spent by the system server threads handling incoming binder requests. */ private LongSamplingCounterArray mBinderThreadCpuTimesUs; @@ -10856,6 +10846,14 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * Starts tracking CPU time-in-state for threads of the system server process, + * keeping a separate account of threads receiving incoming binder calls. + */ + public void startTrackingSystemServerCpuTime() { + mSystemServerCpuThreadReader.startTrackingThreadCpuTime(); + } + public void setCallback(BatteryCallback cb) { mCallback = cb; } @@ -11508,8 +11506,6 @@ public class BatteryStatsImpl extends BatteryStats { MeasuredEnergyStats.resetIfNotNull(mGlobalMeasuredEnergyStats); - resetIfNotNull(mSystemServerCpuTimesUs, false, elapsedRealtimeUs); - resetIfNotNull(mSystemServerThreadCpuTimesUs, false, elapsedRealtimeUs); resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs); mLastHistoryStepDetails = null; @@ -12695,27 +12691,17 @@ public class BatteryStatsImpl extends BatteryStats { return; } - if (mSystemServerCpuTimesUs == null) { - mSystemServerCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase); - mSystemServerThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase); + if (mBinderThreadCpuTimesUs == null) { mBinderThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase); } - mSystemServerCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.processCpuTimesUs); - mSystemServerThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.threadCpuTimesUs); mBinderThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.binderThreadCpuTimesUs); if (DEBUG_BINDER_STATS) { - Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)"); - long totalCpuTimeMs = 0; - long totalThreadTimeMs = 0; + Slog.d(TAG, "System server threads per CPU cluster (incoming binder threads)"); long binderThreadTimeMs = 0; int cpuIndex = 0; - final long[] systemServerCpuTimesUs = - mSystemServerCpuTimesUs.getCountsLocked(0); - final long[] systemServerThreadCpuTimesUs = - mSystemServerThreadCpuTimesUs.getCountsLocked(0); - final long[] binderThreadCpuTimesUs = - mBinderThreadCpuTimesUs.getCountsLocked(0); + final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked( + BatteryStats.STATS_SINCE_CHARGED); int index = 0; int numCpuClusters = mPowerProfile.getNumCpuClusters(); for (int cluster = 0; cluster < numCpuClusters; cluster++) { @@ -12726,28 +12712,15 @@ public class BatteryStatsImpl extends BatteryStats { if (speed != 0) { sb.append(", "); } - long totalCountMs = systemServerThreadCpuTimesUs[index] / 1000; long binderCountMs = binderThreadCpuTimesUs[index] / 1000; - sb.append(String.format("%d/%d(%.1f%%)", - binderCountMs, - totalCountMs, - totalCountMs != 0 ? (double) binderCountMs * 100 / totalCountMs : 0)); + sb.append(TextUtils.formatSimple("%10d", binderCountMs)); - totalCpuTimeMs += systemServerCpuTimesUs[index] / 1000; - totalThreadTimeMs += totalCountMs; binderThreadTimeMs += binderCountMs; index++; } cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster); Slog.d(TAG, sb.toString()); } - - Slog.d(TAG, "Total system server CPU time (ms): " + totalCpuTimeMs); - Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTimeMs); - Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)", - binderThreadTimeMs, - binderThreadTimeMs != 0 - ? (double) binderThreadTimeMs * 100 / totalThreadTimeMs : 0)); } } @@ -14022,60 +13995,16 @@ public class BatteryStatsImpl extends BatteryStats { } + /** + * Estimates the time spent by the system server handling incoming binder requests. + */ @Override public long[] getSystemServiceTimeAtCpuSpeeds() { - // Estimates the time spent by the system server handling incoming binder requests. - // - // The data that we can get from the kernel is this: - // - CPU duration for a (thread - cluster - CPU speed) combination - // - CPU duration for a (UID - cluster - CPU speed) combination - // - // The configuration we have in the Power Profile is this: - // - Average CPU power for a (cluster - CPU speed) combination. - // - // The model used by BatteryStats can be illustrated with this example: - // - // - Let's say the system server has 10 threads. - // - These 10 threads spent 1000 ms of CPU time in aggregate - // - Of the 10 threads 4 were execute exclusively incoming binder calls. - // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate - // - The real time spent by the system server process doing all of this is, say, 200 ms. - // - // We will assume that power consumption is proportional to the time spent by the CPU - // across all threads. This is a crude assumption, but we don't have more detailed data. - // Thus, - // binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime - // - // In our example, - // binderRealTime = 200 * 600 / 1000 = 120ms - // - // We can then multiply this estimated time by the average power to obtain an estimate - // of the total power consumed by incoming binder calls for the given cluster/speed - // combination. - - if (mSystemServerCpuTimesUs == null) { + if (mBinderThreadCpuTimesUs == null) { return null; } - final long[] systemServerCpuTimesUs = mSystemServerCpuTimesUs.getCountsLocked( - BatteryStats.STATS_SINCE_CHARGED); - final long [] systemServerThreadCpuTimesUs = mSystemServerThreadCpuTimesUs.getCountsLocked( - BatteryStats.STATS_SINCE_CHARGED); - final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked( - BatteryStats.STATS_SINCE_CHARGED); - - final int size = systemServerCpuTimesUs.length; - final long[] results = new long[size]; - - for (int i = 0; i < size; i++) { - if (systemServerThreadCpuTimesUs[i] == 0) { - continue; - } - - results[i] = systemServerCpuTimesUs[i] * binderThreadCpuTimesUs[i] - / systemServerThreadCpuTimesUs[i]; - } - return results; + return mBinderThreadCpuTimesUs.getCountsLocked(BatteryStats.STATS_SINCE_CHARGED); } /** @@ -14506,7 +14435,7 @@ public class BatteryStatsImpl extends BatteryStats { } updateSystemServiceCallStats(); - if (mSystemServerThreadCpuTimesUs != null) { + if (mBinderThreadCpuTimesUs != null) { pw.println("Per UID System server binder time in ms:"); long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds(); for (int i = 0; i < size; i++) { @@ -16097,9 +16026,6 @@ public class BatteryStatsImpl extends BatteryStats { mUidStats.append(uid, u); } - mSystemServerCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase); - mSystemServerThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, - mOnBatteryTimeBase); mBinderThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase); } @@ -16308,8 +16234,6 @@ public class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } - LongSamplingCounterArray.writeToParcel(out, mSystemServerCpuTimesUs); - LongSamplingCounterArray.writeToParcel(out, mSystemServerThreadCpuTimesUs); LongSamplingCounterArray.writeToParcel(out, mBinderThreadCpuTimesUs); } diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java index e6a962312a00..4d2a08a4bcf3 100644 --- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java @@ -16,23 +16,12 @@ package com.android.internal.os; -import static android.os.Process.PROC_OUT_LONG; -import static android.os.Process.PROC_SPACE_TERM; - import android.annotation.Nullable; -import android.os.Process; -import android.system.Os; -import android.system.OsConstants; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; -import java.nio.file.DirectoryIteratorException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Arrays; /** @@ -45,93 +34,65 @@ public class KernelSingleProcessCpuThreadReader { private static final String TAG = "KernelSingleProcCpuThreadRdr"; private static final boolean DEBUG = false; - private static final boolean NATIVE_ENABLED = true; - - /** - * The name of the file to read CPU statistics from, must be found in {@code - * /proc/$PID/task/$TID} - */ - private static final String CPU_STATISTICS_FILENAME = "time_in_state"; - - private static final String PROC_STAT_FILENAME = "stat"; - - /** Directory under /proc/$PID containing CPU stats files for threads */ - public static final String THREAD_CPU_STATS_DIRECTORY = "task"; - - /** Default mount location of the {@code proc} filesystem */ - private static final Path DEFAULT_PROC_PATH = Paths.get("/proc"); - - /** The initial {@code time_in_state} file for {@link ProcTimeInStateReader} */ - private static final Path INITIAL_TIME_IN_STATE_PATH = Paths.get("self/time_in_state"); - - /** See https://man7.org/linux/man-pages/man5/proc.5.html */ - private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] { - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM, - PROC_SPACE_TERM | PROC_OUT_LONG, // 14: utime - PROC_SPACE_TERM | PROC_OUT_LONG, // 15: stime - // Ignore remaining fields - }; - - private final long[] mProcessFullStatsData = new long[2]; - - private static final int PROCESS_FULL_STAT_UTIME = 0; - private static final int PROCESS_FULL_STAT_STIME = 1; - - /** Used to read and parse {@code time_in_state} files */ - private final ProcTimeInStateReader mProcTimeInStateReader; private final int mPid; - /** Where the proc filesystem is mounted */ - private final Path mProcPath; + private final CpuTimeInStateReader mCpuTimeInStateReader; - // How long a CPU jiffy is in milliseconds. - private final long mJiffyMillis; + private int[] mSelectedThreadNativeTids = new int[0]; // Sorted - // Path: /proc/<pid>/stat - private final String mProcessStatFilePath; + /** + * Count of frequencies read from the {@code time_in_state} file. + */ + private int mFrequencyCount; - // Path: /proc/<pid>/task - private final Path mThreadsDirectoryPath; + private boolean mIsTracking; /** - * Count of frequencies read from the {@code time_in_state} file. Read from {@link - * #mProcTimeInStateReader#getCpuFrequenciesKhz()}. + * A CPU time-in-state provider for testing. Imitates the behavior of the corresponding + * methods in frameworks/native/libs/cputimeinstate/cputimeinstate.c */ - private int mFrequencyCount; + @VisibleForTesting + public interface CpuTimeInStateReader { + /** + * Returns the overall number of cluster-frequency combinations. + */ + int getCpuFrequencyCount(); + + /** + * Returns true to indicate success. + * + * Called from native. + */ + boolean startTrackingProcessCpuTimes(int tgid); + + /** + * Returns true to indicate success. + * + * Called from native. + */ + boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey); + + /** + * Must return an array of strings formatted like this: + * "aggKey:t0_0 t0_1...:t1_0 t1_1..." + * Times should be provided in nanoseconds. + * + * Called from native. + */ + String[] getAggregatedTaskCpuFreqTimes(int pid); + } /** * Create with a path where `proc` is mounted. Used primarily for testing * * @param pid PID of the process whose threads are to be read. - * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc}) */ @VisibleForTesting - public KernelSingleProcessCpuThreadReader( - int pid, - Path procPath) throws IOException { + public KernelSingleProcessCpuThreadReader(int pid, + @Nullable CpuTimeInStateReader cpuTimeInStateReader) throws IOException { mPid = pid; - mProcPath = procPath; - mProcTimeInStateReader = new ProcTimeInStateReader( - mProcPath.resolve(INITIAL_TIME_IN_STATE_PATH)); - long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK); - mJiffyMillis = 1000 / jiffyHz; - mProcessStatFilePath = - mProcPath.resolve(String.valueOf(mPid)).resolve(PROC_STAT_FILENAME).toString(); - mThreadsDirectoryPath = - mProcPath.resolve(String.valueOf(mPid)).resolve(THREAD_CPU_STATS_DIRECTORY); + mCpuTimeInStateReader = cpuTimeInStateReader; } /** @@ -142,7 +103,7 @@ public class KernelSingleProcessCpuThreadReader { @Nullable public static KernelSingleProcessCpuThreadReader create(int pid) { try { - return new KernelSingleProcessCpuThreadReader(pid, DEFAULT_PROC_PATH); + return new KernelSingleProcessCpuThreadReader(pid, null); } catch (IOException e) { Slog.e(TAG, "Failed to initialize KernelSingleProcessCpuThreadReader", e); return null; @@ -150,146 +111,98 @@ public class KernelSingleProcessCpuThreadReader { } /** - * Get the CPU frequencies that correspond to the times reported in {@link - * ProcessCpuUsage#processCpuTimesMillis} etc. + * Starts tracking aggregated CPU time-in-state of all threads of the process with the PID + * supplied in the constructor. + */ + public void startTrackingThreadCpuTimes() { + if (!mIsTracking) { + if (!startTrackingProcessCpuTimes(mPid, mCpuTimeInStateReader)) { + Slog.e(TAG, "Failed to start tracking process CPU times for " + mPid); + } + if (mSelectedThreadNativeTids.length > 0) { + if (!startAggregatingThreadCpuTimes(mSelectedThreadNativeTids, + mCpuTimeInStateReader)) { + Slog.e(TAG, "Failed to start tracking aggregated thread CPU times for " + + Arrays.toString(mSelectedThreadNativeTids)); + } + } + mIsTracking = true; + } + } + + /** + * @param nativeTids an array of native Thread IDs whose CPU times should + * be aggregated as a group. This is expected to be a subset + * of all thread IDs owned by the process. + */ + public void setSelectedThreadIds(int[] nativeTids) { + mSelectedThreadNativeTids = nativeTids.clone(); + if (mIsTracking) { + startAggregatingThreadCpuTimes(mSelectedThreadNativeTids, mCpuTimeInStateReader); + } + } + + /** + * Get the CPU frequencies that correspond to the times reported in {@link ProcessCpuUsage}. */ public int getCpuFrequencyCount() { if (mFrequencyCount == 0) { - mFrequencyCount = mProcTimeInStateReader.getFrequenciesKhz().length; + mFrequencyCount = getCpuFrequencyCount(mCpuTimeInStateReader); } return mFrequencyCount; } /** - * Get the total and per-thread CPU usage of the process with the PID specified in the - * constructor. - * - * @param selectedThreadIds a SORTED array of native Thread IDs whose CPU times should - * be aggregated as a group. This is expected to be a subset - * of all thread IDs owned by the process. + * Get the total CPU usage of the process with the PID specified in the + * constructor. The CPU usage time is aggregated across all threads and may + * exceed the time the entire process has been running. */ @Nullable - public ProcessCpuUsage getProcessCpuUsage(int[] selectedThreadIds) { + public ProcessCpuUsage getProcessCpuUsage() { if (DEBUG) { - Slog.d(TAG, "Reading CPU thread usages with directory " + mProcPath + " process ID " - + mPid); - } - - int cpuFrequencyCount = getCpuFrequencyCount(); - ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount); - - if (NATIVE_ENABLED) { - boolean result = readProcessCpuUsage(mProcPath.toString(), mPid, - selectedThreadIds, processCpuUsage.processCpuTimesMillis, - processCpuUsage.threadCpuTimesMillis, - processCpuUsage.selectedThreadCpuTimesMillis); - if (!result) { - return null; - } - return processCpuUsage; + Slog.d(TAG, "Reading CPU thread usages for PID " + mPid); } - if (!isSorted(selectedThreadIds)) { - throw new IllegalArgumentException("selectedThreadIds is not sorted: " - + Arrays.toString(selectedThreadIds)); - } - - if (!Process.readProcFile(mProcessStatFilePath, PROCESS_FULL_STATS_FORMAT, null, - mProcessFullStatsData, null)) { - Slog.e(TAG, "Failed to read process stat file " + mProcessStatFilePath); - return null; - } + ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(getCpuFrequencyCount()); - long utime = mProcessFullStatsData[PROCESS_FULL_STAT_UTIME]; - long stime = mProcessFullStatsData[PROCESS_FULL_STAT_STIME]; - - long processCpuTimeMillis = (utime + stime) * mJiffyMillis; - - try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) { - for (Path threadDirectory : threadPaths) { - readThreadCpuUsage(processCpuUsage, selectedThreadIds, threadDirectory); - } - } catch (IOException | DirectoryIteratorException e) { - // Expected when a process finishes + boolean result = readProcessCpuUsage(mPid, + processCpuUsage.threadCpuTimesMillis, + processCpuUsage.selectedThreadCpuTimesMillis, + mCpuTimeInStateReader); + if (!result) { return null; } - // Estimate per cluster per frequency CPU time for the entire process - // by distributing the total process CPU time proportionately to how much - // CPU time its threads took on those clusters/frequencies. This algorithm - // works more accurately when when we have equally distributed concurrency. - // TODO(b/169279846): obtain actual process CPU times from the kernel - long totalCpuTimeAllThreads = 0; - for (int i = cpuFrequencyCount - 1; i >= 0; i--) { - totalCpuTimeAllThreads += processCpuUsage.threadCpuTimesMillis[i]; - } - - for (int i = cpuFrequencyCount - 1; i >= 0; i--) { - processCpuUsage.processCpuTimesMillis[i] = - processCpuTimeMillis * processCpuUsage.threadCpuTimesMillis[i] - / totalCpuTimeAllThreads; + if (DEBUG) { + Slog.d(TAG, "threadCpuTimesMillis = " + + Arrays.toString(processCpuUsage.threadCpuTimesMillis)); + Slog.d(TAG, "selectedThreadCpuTimesMillis = " + + Arrays.toString(processCpuUsage.selectedThreadCpuTimesMillis)); } return processCpuUsage; } - /** - * Reads a thread's CPU usage and aggregates the per-cluster per-frequency CPU times. - * - * @param threadDirectory the {@code /proc} directory of the thread - */ - private void readThreadCpuUsage(ProcessCpuUsage processCpuUsage, int[] selectedThreadIds, - Path threadDirectory) { - // Get the thread ID from the directory name - final int threadId; - try { - final String directoryName = threadDirectory.getFileName().toString(); - threadId = Integer.parseInt(directoryName); - } catch (NumberFormatException e) { - Slog.w(TAG, "Failed to parse thread ID when iterating over /proc/*/task", e); - return; - } - - // Get the CPU statistics from the directory - final Path threadCpuStatPath = threadDirectory.resolve(CPU_STATISTICS_FILENAME); - final long[] cpuUsages = mProcTimeInStateReader.getUsageTimesMillis(threadCpuStatPath); - if (cpuUsages == null) { - return; - } - - final int cpuFrequencyCount = getCpuFrequencyCount(); - final boolean isSelectedThread = Arrays.binarySearch(selectedThreadIds, threadId) >= 0; - for (int i = cpuFrequencyCount - 1; i >= 0; i--) { - processCpuUsage.threadCpuTimesMillis[i] += cpuUsages[i]; - if (isSelectedThread) { - processCpuUsage.selectedThreadCpuTimesMillis[i] += cpuUsages[i]; - } - } - } - /** CPU usage of a process, all of its threads and a selected subset of its threads */ public static class ProcessCpuUsage { - public long[] processCpuTimesMillis; public long[] threadCpuTimesMillis; public long[] selectedThreadCpuTimesMillis; public ProcessCpuUsage(int cpuFrequencyCount) { - processCpuTimesMillis = new long[cpuFrequencyCount]; threadCpuTimesMillis = new long[cpuFrequencyCount]; selectedThreadCpuTimesMillis = new long[cpuFrequencyCount]; } } - private static boolean isSorted(int[] array) { - for (int i = 0; i < array.length - 1; i++) { - if (array[i] > array[i + 1]) { - return false; - } - } - return true; - } + private native int getCpuFrequencyCount(CpuTimeInStateReader reader); + + private native boolean startTrackingProcessCpuTimes(int pid, CpuTimeInStateReader reader); + + private native boolean startAggregatingThreadCpuTimes(int[] selectedThreadIds, + CpuTimeInStateReader reader); - private native boolean readProcessCpuUsage(String procPath, int pid, int[] selectedThreadIds, - long[] processCpuTimesMillis, long[] threadCpuTimesMillis, - long[] selectedThreadCpuTimesMillis); + private native boolean readProcessCpuUsage(int pid, + long[] threadCpuTimesMillis, + long[] selectedThreadCpuTimesMillis, + CpuTimeInStateReader reader); } diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index 25f6b4d16971..9c4a26724ba8 100644 --- a/core/java/com/android/internal/os/ScreenPowerCalculator.java +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -21,9 +21,14 @@ import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.SystemBatteryConsumer; +import android.os.SystemClock; import android.os.UserHandle; +import android.text.format.DateUtils; import android.util.Log; import android.util.SparseArray; +import android.util.SparseLongArray; + +import com.android.internal.annotations.VisibleForTesting; import java.util.List; @@ -57,6 +62,7 @@ public class ScreenPowerCalculator extends PowerCalculator { .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah); } + // TODO(b/178140704): Attribute screen usage similar to smearScreenBatterySipper. } /** @@ -73,6 +79,8 @@ public class ScreenPowerCalculator extends PowerCalculator { bs.usageTimeMs = durationMs; bs.sumPower(); sippers.add(bs); + + smearScreenBatterySipper(sippers, bs); } } @@ -96,4 +104,60 @@ public class ScreenPowerCalculator extends PowerCalculator { } return power; } + + /** + * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity + * time, and store this in the {@link BatterySipper#screenPowerMah} field. + */ + @VisibleForTesting + public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) { + + long totalActivityTimeMs = 0; + final SparseLongArray activityTimeArray = new SparseLongArray(); + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatteryStats.Uid uid = sippers.get(i).uidObj; + if (uid != null) { + final long timeMs = getProcessForegroundTimeMs(uid); + activityTimeArray.put(uid.getUid(), timeMs); + totalActivityTimeMs += timeMs; + } + } + + if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) { + final double screenPowerMah = screenSipper.totalPowerMah; + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper sipper = sippers.get(i); + sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0) + / totalActivityTimeMs; + } + } + } + + /** Get the minimum of the uid's ForegroundActivity time and its TOP time. */ + @VisibleForTesting + public long getProcessForegroundTimeMs(BatteryStats.Uid uid) { + final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000; + final int[] foregroundTypes = {BatteryStats.Uid.PROCESS_STATE_TOP}; + + long timeUs = 0; + for (int type : foregroundTypes) { + final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, + BatteryStats.STATS_SINCE_CHARGED); + timeUs += localTime; + } + + // Return the min value of STATE_TOP time and foreground activity time, since both of these + // time have some errors. + return Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)) / 1000; + } + + /** Get the ForegroundActivity time of the given uid. */ + @VisibleForTesting + public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { + final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); + if (timer == null) { + return 0; + } + return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); + } } diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java index fbbee94feacc..fbad75e93a17 100644 --- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java +++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java @@ -22,8 +22,6 @@ import android.os.Process; import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; -import java.nio.file.Path; -import java.util.Arrays; /** * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage @@ -31,9 +29,7 @@ import java.util.Arrays; */ public class SystemServerCpuThreadReader { private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader; - private int[] mBinderThreadNativeTids = new int[0]; // Sorted - private long[] mLastProcessCpuTimeUs; private long[] mLastThreadCpuTimesUs; private long[] mLastBinderThreadCpuTimesUs; @@ -41,8 +37,6 @@ public class SystemServerCpuThreadReader { * Times (in microseconds) spent by the system server UID. */ public static class SystemServiceCpuThreadTimes { - // The entire process - public long[] processCpuTimesUs; // All threads public long[] threadCpuTimesUs; // Just the threads handling incoming binder calls @@ -61,8 +55,10 @@ public class SystemServerCpuThreadReader { } @VisibleForTesting - public SystemServerCpuThreadReader(Path procPath, int pid) throws IOException { - this(new KernelSingleProcessCpuThreadReader(pid, procPath)); + public SystemServerCpuThreadReader(int pid, + KernelSingleProcessCpuThreadReader.CpuTimeInStateReader cpuTimeInStateReader) + throws IOException { + this(new KernelSingleProcessCpuThreadReader(pid, cpuTimeInStateReader)); } @VisibleForTesting @@ -70,9 +66,15 @@ public class SystemServerCpuThreadReader { mKernelCpuThreadReader = kernelCpuThreadReader; } + /** + * Start tracking CPU time-in-state for the process specified in the constructor. + */ + public void startTrackingThreadCpuTime() { + mKernelCpuThreadReader.startTrackingThreadCpuTimes(); + } + public void setBinderThreadNativeTids(int[] nativeTids) { - mBinderThreadNativeTids = nativeTids.clone(); - Arrays.sort(mBinderThreadNativeTids); + mKernelCpuThreadReader.setSelectedThreadIds(nativeTids); } /** @@ -81,33 +83,27 @@ public class SystemServerCpuThreadReader { @Nullable public SystemServiceCpuThreadTimes readDelta() { final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount(); - if (mLastProcessCpuTimeUs == null) { - mLastProcessCpuTimeUs = new long[numCpuFrequencies]; + if (mLastThreadCpuTimesUs == null) { mLastThreadCpuTimesUs = new long[numCpuFrequencies]; mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies]; - mDeltaCpuThreadTimes.processCpuTimesUs = new long[numCpuFrequencies]; mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies]; mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies]; } final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage = - mKernelCpuThreadReader.getProcessCpuUsage(mBinderThreadNativeTids); + mKernelCpuThreadReader.getProcessCpuUsage(); if (processCpuUsage == null) { return null; } for (int i = numCpuFrequencies - 1; i >= 0; i--) { - long processCpuTimesUs = processCpuUsage.processCpuTimesMillis[i] * 1000; long threadCpuTimesUs = processCpuUsage.threadCpuTimesMillis[i] * 1000; long binderThreadCpuTimesUs = processCpuUsage.selectedThreadCpuTimesMillis[i] * 1000; - mDeltaCpuThreadTimes.processCpuTimesUs[i] = - Math.max(0, processCpuTimesUs - mLastProcessCpuTimeUs[i]); mDeltaCpuThreadTimes.threadCpuTimesUs[i] = Math.max(0, threadCpuTimesUs - mLastThreadCpuTimesUs[i]); mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = Math.max(0, binderThreadCpuTimesUs - mLastBinderThreadCpuTimesUs[i]); - mLastProcessCpuTimeUs[i] = processCpuTimesUs; mLastThreadCpuTimesUs[i] = threadCpuTimesUs; mLastBinderThreadCpuTimesUs[i] = binderThreadCpuTimesUs; } diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp index 4eaa016df6f2..9cc72437a023 100644 --- a/core/jni/android_view_InputDevice.cpp +++ b/core/jni/android_view_InputDevice.cpp @@ -70,7 +70,8 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi deviceInfo.isExternal(), deviceInfo.getSources(), deviceInfo.getKeyboardType(), kcmObj.get(), deviceInfo.hasVibrator(), hasMic, - deviceInfo.hasButtonUnderPad(), deviceInfo.hasSensor())); + deviceInfo.hasButtonUnderPad(), deviceInfo.hasSensor(), + deviceInfo.hasBattery())); const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges(); for (const InputDeviceInfo::MotionRange& range: ranges) { @@ -90,9 +91,10 @@ int register_android_view_InputDevice(JNIEnv* env) gInputDeviceClassInfo.clazz = FindClassOrDie(env, "android/view/InputDevice"); gInputDeviceClassInfo.clazz = MakeGlobalRefOrDie(env, gInputDeviceClassInfo.clazz); - gInputDeviceClassInfo.ctor = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>", - "(IIILjava/lang/String;IILjava/lang/" - "String;ZIILandroid/view/KeyCharacterMap;ZZZZ)V"); + gInputDeviceClassInfo.ctor = + GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>", + "(IIILjava/lang/String;IILjava/lang/" + "String;ZIILandroid/view/KeyCharacterMap;ZZZZZ)V"); gInputDeviceClassInfo.addMotionRange = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "addMotionRange", "(IIFFFFF)V"); diff --git a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp index 52bed6bcfce3..dfae68429f6d 100644 --- a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp +++ b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp @@ -26,244 +26,308 @@ #include <android_runtime/Log.h> #include <nativehelper/ScopedPrimitiveArray.h> -#include <nativehelper/ScopedUtfChars.h> namespace android { +static constexpr uint16_t DEFAULT_THREAD_AGGREGATION_KEY = 0; +static constexpr uint16_t SELECTED_THREAD_AGGREGATION_KEY = 1; + +static constexpr uint64_t NSEC_PER_MSEC = 1000000; + // Number of milliseconds in a jiffy - the unit of time measurement for processes and threads static const uint32_t gJiffyMillis = (uint32_t)(1000 / sysconf(_SC_CLK_TCK)); -// Given a PID, returns a vector of all TIDs for the process' tasks. Thread IDs are -// file names in the /proc/<pid>/task directory. -static bool getThreadIds(const std::string &procPath, const pid_t pid, - std::vector<pid_t> &outThreadIds) { - std::string taskPath = android::base::StringPrintf("%s/%u/task", procPath.c_str(), pid); +// Abstract class for readers of CPU time-in-state. There are two implementations of +// this class: BpfCpuTimeInStateReader and MockCpuTimeInStateReader. The former is used +// by the production code. The latter is used by unit tests to provide mock +// CPU time-in-state data via a Java implementation. +class ICpuTimeInStateReader { +public: + virtual ~ICpuTimeInStateReader() {} + + // Returns the overall number of cluser-frequency combinations + virtual size_t getCpuFrequencyCount(); + + // Marks the CPU time-in-state tracking for threads of the specified TGID + virtual bool startTrackingProcessCpuTimes(pid_t) = 0; + + // Marks the thread specified by its PID for CPU time-in-state tracking. + virtual bool startAggregatingTaskCpuTimes(pid_t, uint16_t) = 0; + + // Retrieves the accumulated time-in-state data, which is organized as a map + // from aggregation keys to vectors of vectors using the format: + // { aggKey0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...], + // aggKey1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... } + // where ti_j_k is the ns tid i spent running on the jth cluster at the cluster's kth lowest + // freq. + virtual std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> + getAggregatedTaskCpuFreqTimes(pid_t, const std::vector<uint16_t> &); +}; - struct dirent **dirlist; - int threadCount = scandir(taskPath.c_str(), &dirlist, NULL, NULL); - if (threadCount == -1) { - ALOGE("Cannot read directory %s", taskPath.c_str()); - return false; +// ICpuTimeInStateReader that uses eBPF to provide a map of aggregated CPU time-in-state values. +// See cputtimeinstate.h/.cpp +class BpfCpuTimeInStateReader : public ICpuTimeInStateReader { +public: + size_t getCpuFrequencyCount() { + std::optional<std::vector<std::vector<uint32_t>>> cpuFreqs = android::bpf::getCpuFreqs(); + if (!cpuFreqs) { + ALOGE("Cannot obtain CPU frequency count"); + return 0; + } + + size_t freqCount = 0; + for (auto cluster : *cpuFreqs) { + freqCount += cluster.size(); + } + + return freqCount; } - outThreadIds.reserve(threadCount); + bool startTrackingProcessCpuTimes(pid_t tgid) { + return android::bpf::startTrackingProcessCpuTimes(tgid); + } - for (int i = 0; i < threadCount; i++) { - pid_t tid; - if (android::base::ParseInt<pid_t>(dirlist[i]->d_name, &tid)) { - outThreadIds.push_back(tid); - } - free(dirlist[i]); + bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) { + return android::bpf::startAggregatingTaskCpuTimes(pid, aggregationKey); } - free(dirlist); - return true; -} + std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> + getAggregatedTaskCpuFreqTimes(pid_t pid, const std::vector<uint16_t> &aggregationKeys) { + return android::bpf::getAggregatedTaskCpuFreqTimes(pid, aggregationKeys); + } +}; -// Reads contents of a time_in_state file and returns times as a vector of times per frequency -// A time_in_state file contains pairs of frequency - time (in jiffies): -// -// cpu0 -// 300000 30 -// 403200 0 -// cpu4 -// 710400 10 -// 825600 20 -// 940800 30 -// -static bool getThreadTimeInState(const std::string &procPath, const pid_t pid, const pid_t tid, - const size_t frequencyCount, - std::vector<uint64_t> &outThreadTimeInState) { - std::string timeInStateFilePath = - android::base::StringPrintf("%s/%u/task/%u/time_in_state", procPath.c_str(), pid, tid); - std::string data; - - if (!android::base::ReadFileToString(timeInStateFilePath, &data)) { - ALOGE("Cannot read file: %s", timeInStateFilePath.c_str()); - return false; +// ICpuTimeInStateReader that uses JNI to provide a map of aggregated CPU time-in-state +// values. +// This version of CpuTimeInStateReader is used exclusively for providing mock data in tests. +class MockCpuTimeInStateReader : public ICpuTimeInStateReader { +private: + JNIEnv *mEnv; + jobject mCpuTimeInStateReader; + +public: + MockCpuTimeInStateReader(JNIEnv *env, jobject cpuTimeInStateReader) + : mEnv(env), mCpuTimeInStateReader(cpuTimeInStateReader) {} + + size_t getCpuFrequencyCount(); + + bool startTrackingProcessCpuTimes(pid_t tgid); + + bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey); + + std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> + getAggregatedTaskCpuFreqTimes(pid_t tgid, const std::vector<uint16_t> &aggregationKeys); +}; + +static ICpuTimeInStateReader *getCpuTimeInStateReader(JNIEnv *env, + jobject cpuTimeInStateReaderObject) { + if (cpuTimeInStateReaderObject) { + return new MockCpuTimeInStateReader(env, cpuTimeInStateReaderObject); + } else { + return new BpfCpuTimeInStateReader(); } +} - auto lines = android::base::Split(data, "\n"); - size_t index = 0; - for (const auto &line : lines) { - if (line.empty()) { - continue; - } +static jint getCpuFrequencyCount(JNIEnv *env, jclass, jobject cpuTimeInStateReaderObject) { + std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader( + getCpuTimeInStateReader(env, cpuTimeInStateReaderObject)); + return cpuTimeInStateReader->getCpuFrequencyCount(); +} - auto numbers = android::base::Split(line, " "); - if (numbers.size() != 2) { - continue; - } - uint64_t timeInState; - if (!android::base::ParseUint<uint64_t>(numbers[1], &timeInState)) { - ALOGE("Invalid time_in_state file format: %s", timeInStateFilePath.c_str()); +static jboolean startTrackingProcessCpuTimes(JNIEnv *env, jclass, jint tgid, + jobject cpuTimeInStateReaderObject) { + std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader( + getCpuTimeInStateReader(env, cpuTimeInStateReaderObject)); + return cpuTimeInStateReader->startTrackingProcessCpuTimes(tgid); +} + +static jboolean startAggregatingThreadCpuTimes(JNIEnv *env, jclass, jintArray selectedThreadIdArray, + jobject cpuTimeInStateReaderObject) { + ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray); + std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader( + getCpuTimeInStateReader(env, cpuTimeInStateReaderObject)); + + for (int i = 0; i < selectedThreadIds.size(); i++) { + if (!cpuTimeInStateReader->startAggregatingTaskCpuTimes(selectedThreadIds[i], + SELECTED_THREAD_AGGREGATION_KEY)) { return false; } - if (index < frequencyCount) { - outThreadTimeInState[index] = timeInState; - } - index++; } + return true; +} +// Converts time-in-state data from a vector of vectors to a flat array. +// Also converts from nanoseconds to milliseconds. +static bool flattenTimeInStateData(ScopedLongArrayRW &cpuTimesMillis, + const std::vector<std::vector<uint64_t>> &data) { + size_t frequencyCount = cpuTimesMillis.size(); + size_t index = 0; + for (const auto &cluster : data) { + for (const uint64_t &timeNanos : cluster) { + if (index < frequencyCount) { + cpuTimesMillis[index] = timeNanos / NSEC_PER_MSEC; + } + index++; + } + } if (index != frequencyCount) { - ALOGE("Incorrect number of frequencies %u in %s. Expected %u", - (uint32_t)outThreadTimeInState.size(), timeInStateFilePath.c_str(), - (uint32_t)frequencyCount); + ALOGE("CPU time-in-state reader returned data for %zu frequencies; expected: %zu", index, + frequencyCount); return false; } return true; } -static int pidCompare(const void *a, const void *b) { - return (*(pid_t *)a - *(pid_t *)b); -} - -static inline bool isSelectedThread(const pid_t tid, const pid_t *selectedThreadIds, - const size_t selectedThreadCount) { - return bsearch(&tid, selectedThreadIds, selectedThreadCount, sizeof(pid_t), pidCompare) != NULL; -} - -// Reads all /proc/<pid>/task/*/time_in_state files and aggregates per-frequency +// Reads all CPU time-in-state data accumulated by BPF and aggregates per-frequency // time in state data for all threads. Also, separately aggregates time in state for // selected threads whose TIDs are passes as selectedThreadIds. -static void aggregateThreadCpuTimes(const std::string &procPath, const pid_t pid, - const std::vector<pid_t> &threadIds, - const size_t frequencyCount, const pid_t *selectedThreadIds, - const size_t selectedThreadCount, - uint64_t *threadCpuTimesMillis, - uint64_t *selectedThreadCpuTimesMillis) { - for (size_t j = 0; j < frequencyCount; j++) { - threadCpuTimesMillis[j] = 0; - selectedThreadCpuTimesMillis[j] = 0; - } +static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jint pid, + jlongArray threadCpuTimesMillisArray, + jlongArray selectedThreadCpuTimesMillisArray, + jobject cpuTimeInStateReaderObject) { + ScopedLongArrayRW threadCpuTimesMillis(env, threadCpuTimesMillisArray); + ScopedLongArrayRW selectedThreadCpuTimesMillis(env, selectedThreadCpuTimesMillisArray); + std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader( + getCpuTimeInStateReader(env, cpuTimeInStateReaderObject)); - for (size_t i = 0; i < threadIds.size(); i++) { - pid_t tid = threadIds[i]; - std::vector<uint64_t> timeInState(frequencyCount); - if (!getThreadTimeInState(procPath, pid, tid, frequencyCount, timeInState)) { - continue; - } + const size_t frequencyCount = cpuTimeInStateReader->getCpuFrequencyCount(); - bool selectedThread = isSelectedThread(tid, selectedThreadIds, selectedThreadCount); - for (size_t j = 0; j < frequencyCount; j++) { - threadCpuTimesMillis[j] += timeInState[j]; - if (selectedThread) { - selectedThreadCpuTimesMillis[j] += timeInState[j]; - } - } + if (threadCpuTimesMillis.size() != frequencyCount) { + ALOGE("Invalid threadCpuTimesMillis array length: %zu frequencies; expected: %zu", + threadCpuTimesMillis.size(), frequencyCount); + return false; } + + if (selectedThreadCpuTimesMillis.size() != frequencyCount) { + ALOGE("Invalid selectedThreadCpuTimesMillis array length: %zu frequencies; expected: %zu", + selectedThreadCpuTimesMillis.size(), frequencyCount); + return false; + } + for (size_t i = 0; i < frequencyCount; i++) { - threadCpuTimesMillis[i] *= gJiffyMillis; - selectedThreadCpuTimesMillis[i] *= gJiffyMillis; + threadCpuTimesMillis[i] = 0; + selectedThreadCpuTimesMillis[i] = 0; } -} -// Reads process utime and stime from the /proc/<pid>/stat file. -// Format of this file is described in https://man7.org/linux/man-pages/man5/proc.5.html. -static bool getProcessCpuTime(const std::string &procPath, const pid_t pid, - uint64_t &outTimeMillis) { - std::string statFilePath = android::base::StringPrintf("%s/%u/stat", procPath.c_str(), pid); - std::string data; - if (!android::base::ReadFileToString(statFilePath, &data)) { + std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> data = + cpuTimeInStateReader->getAggregatedTaskCpuFreqTimes(pid, + {DEFAULT_THREAD_AGGREGATION_KEY, + SELECTED_THREAD_AGGREGATION_KEY}); + if (!data) { + ALOGE("Cannot read thread CPU times for PID %d", pid); return false; } - auto fields = android::base::Split(data, " "); - uint64_t utime, stime; - - // Field 14 (counting from 1) is utime - process time in user space, in jiffies - // Field 15 (counting from 1) is stime - process time in system space, in jiffies - if (fields.size() < 15 || !android::base::ParseUint(fields[13], &utime) || - !android::base::ParseUint(fields[14], &stime)) { - ALOGE("Invalid file format %s", statFilePath.c_str()); + if (!flattenTimeInStateData(threadCpuTimesMillis, (*data)[DEFAULT_THREAD_AGGREGATION_KEY])) { return false; } - outTimeMillis = (utime + stime) * gJiffyMillis; - return true; -} + if (!flattenTimeInStateData(selectedThreadCpuTimesMillis, + (*data)[SELECTED_THREAD_AGGREGATION_KEY])) { + return false; + } -// Estimates per cluster per frequency CPU time for the entire process -// by distributing the total process CPU time proportionately to how much -// CPU time its threads took on those clusters/frequencies. This algorithm -// works more accurately when when we have equally distributed concurrency. -// TODO(b/169279846): obtain actual process CPU times from the kernel -static void estimateProcessTimeInState(const uint64_t processCpuTimeMillis, - const uint64_t *threadCpuTimesMillis, - const size_t frequencyCount, - uint64_t *processCpuTimesMillis) { - uint64_t totalCpuTimeAllThreads = 0; + // threadCpuTimesMillis returns CPU times for _all_ threads, including the selected ones for (size_t i = 0; i < frequencyCount; i++) { - totalCpuTimeAllThreads += threadCpuTimesMillis[i]; + threadCpuTimesMillis[i] += selectedThreadCpuTimesMillis[i]; } - if (totalCpuTimeAllThreads != 0) { - for (size_t i = 0; i < frequencyCount; i++) { - processCpuTimesMillis[i] = - processCpuTimeMillis * threadCpuTimesMillis[i] / totalCpuTimeAllThreads; - } - } else { - for (size_t i = 0; i < frequencyCount; i++) { - processCpuTimesMillis[i] = 0; - } - } + return true; } -static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jstring procPath, jint pid, - jintArray selectedThreadIdArray, - jlongArray processCpuTimesMillisArray, - jlongArray threadCpuTimesMillisArray, - jlongArray selectedThreadCpuTimesMillisArray) { - ScopedUtfChars procPathChars(env, procPath); - ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray); - ScopedLongArrayRW processCpuTimesMillis(env, processCpuTimesMillisArray); - ScopedLongArrayRW threadCpuTimesMillis(env, threadCpuTimesMillisArray); - ScopedLongArrayRW selectedThreadCpuTimesMillis(env, selectedThreadCpuTimesMillisArray); +static const JNINativeMethod g_single_methods[] = { + {"getCpuFrequencyCount", + "(Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)I", + (void *)getCpuFrequencyCount}, + {"startTrackingProcessCpuTimes", + "(ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z", + (void *)startTrackingProcessCpuTimes}, + {"startAggregatingThreadCpuTimes", + "([ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z", + (void *)startAggregatingThreadCpuTimes}, + {"readProcessCpuUsage", + "(I[J[J" + "Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z", + (void *)readProcessCpuUsage}, +}; - std::string procPathStr(procPathChars.c_str()); +int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv *env) { + return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleProcessCpuThreadReader", + g_single_methods, NELEM(g_single_methods)); +} - // Get all thread IDs for the process. - std::vector<pid_t> threadIds; - if (!getThreadIds(procPathStr, pid, threadIds)) { - ALOGE("Could not obtain thread IDs from: %s", procPathStr.c_str()); +size_t MockCpuTimeInStateReader::getCpuFrequencyCount() { + jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader); + jmethodID mid = mEnv->GetMethodID(cls, "getCpuFrequencyCount", "()I"); + if (mid == 0) { + ALOGE("Couldn't find the method getCpuFrequencyCount"); return false; } + return (size_t)mEnv->CallIntMethod(mCpuTimeInStateReader, mid); +} - size_t frequencyCount = processCpuTimesMillis.size(); - - if (threadCpuTimesMillis.size() != frequencyCount) { - ALOGE("Invalid array length: threadCpuTimesMillis"); +bool MockCpuTimeInStateReader::startTrackingProcessCpuTimes(pid_t tgid) { + jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader); + jmethodID mid = mEnv->GetMethodID(cls, "startTrackingProcessCpuTimes", "(I)Z"); + if (mid == 0) { + ALOGE("Couldn't find the method startTrackingProcessCpuTimes"); return false; } - if (selectedThreadCpuTimesMillis.size() != frequencyCount) { - ALOGE("Invalid array length: selectedThreadCpuTimesMillisArray"); + return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, tgid); +} + +bool MockCpuTimeInStateReader::startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) { + jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader); + jmethodID mid = mEnv->GetMethodID(cls, "startAggregatingTaskCpuTimes", "(II)Z"); + if (mid == 0) { + ALOGE("Couldn't find the method startAggregatingTaskCpuTimes"); return false; } + return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, pid, aggregationKey); +} - aggregateThreadCpuTimes(procPathStr, pid, threadIds, frequencyCount, selectedThreadIds.get(), - selectedThreadIds.size(), - reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()), - reinterpret_cast<uint64_t *>(selectedThreadCpuTimesMillis.get())); - - uint64_t processCpuTime; - bool ret = getProcessCpuTime(procPathStr, pid, processCpuTime); - if (ret) { - estimateProcessTimeInState(processCpuTime, - reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()), - frequencyCount, - reinterpret_cast<uint64_t *>(processCpuTimesMillis.get())); +std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> +MockCpuTimeInStateReader::getAggregatedTaskCpuFreqTimes( + pid_t pid, const std::vector<uint16_t> &aggregationKeys) { + jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader); + jmethodID mid = + mEnv->GetMethodID(cls, "getAggregatedTaskCpuFreqTimes", "(I)[Ljava/lang/String;"); + if (mid == 0) { + ALOGE("Couldn't find the method getAggregatedTaskCpuFreqTimes"); + return {}; } - return ret; -} -static const JNINativeMethod g_single_methods[] = { - {"readProcessCpuUsage", "(Ljava/lang/String;I[I[J[J[J)Z", (void *)readProcessCpuUsage}, -}; + std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map; -int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv *env) { - return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleProcessCpuThreadReader", - g_single_methods, NELEM(g_single_methods)); + jobjectArray stringArray = + (jobjectArray)mEnv->CallObjectMethod(mCpuTimeInStateReader, mid, pid); + int size = mEnv->GetArrayLength(stringArray); + for (int i = 0; i < size; i++) { + ScopedUtfChars line(mEnv, (jstring)mEnv->GetObjectArrayElement(stringArray, i)); + uint16_t aggregationKey; + std::vector<std::vector<uint64_t>> times; + + // Each string is formatted like this: "aggKey:t0_0 t0_1...:t1_0 t1_1..." + auto fields = android::base::Split(line.c_str(), ":"); + android::base::ParseUint(fields[0], &aggregationKey); + + for (int j = 1; j < fields.size(); j++) { + auto numbers = android::base::Split(fields[j], " "); + + std::vector<uint64_t> chunk; + for (int k = 0; k < numbers.size(); k++) { + uint64_t time; + android::base::ParseUint(numbers[k], &time); + chunk.emplace_back(time); + } + times.emplace_back(chunk); + } + + map.emplace(aggregationKey, times); + } + + return map; } } // namespace android diff --git a/core/res/res/values-es-rMX/donottranslate-cldr.xml b/core/res/res/values-es-rMX/donottranslate-cldr.xml new file mode 100755 index 000000000000..db438f22208f --- /dev/null +++ b/core/res/res/values-es-rMX/donottranslate-cldr.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_day_year">%-e %B %Y</string> + <string name="time_of_day">%H:%M:%S</string> + <string name="date_and_time">%-e %b %Y, %H:%M:%S</string> + <string name="date_time">%1$s, %2$s</string> +</resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 243c2448a3b1..415a0a2ed595 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8288,6 +8288,23 @@ </declare-styleable> <!-- =============================== --> + <!-- System Speech Recognition attributes --> + <!-- =============================== --> + <eat-comment /> + + <!-- Use <code>on-device-recognition-service</code> as the root tag of the XML resource that + describes a {@link android.service.speech.RecognitionService}, which is referenced + from its {@link android.service.speech.RecognitionService#SERVICE_META_DATA} meta-data + entry. + @hide @SystemApi + --> + <declare-styleable name="OnDeviceRecognitionService"> + <!-- Fully qualified class name of an activity that allows the user to modify + the settings for this service. --> + <attr name="settingsActivity" /> + </declare-styleable> + + <!-- =============================== --> <!-- Content Capture attributes --> <!-- =============================== --> <eat-comment /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 4e272f110a6e..71b59e4d643f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3800,6 +3800,12 @@ --> <string name="config_defaultSearchUiService" translatable="false"></string> + <!-- The package name for the system's speech recognition service. + This service must be trusted, as it can be activated without explicit consent of the user. + Example: "com.android.speech/.RecognitionService" + --> + <string name="config_defaultOnDeviceSpeechRecognitionService" translatable="false"></string> + <string name="config_defaultMusicRecognitionService" translatable="false"></string> <!-- The package name for the default retail demo app. @@ -4657,4 +4663,7 @@ <bool name="config_telephony5gStandalone">false</bool> <!-- Whether the device enable the non-standalone (NSA) mode of 5G NR.--> <bool name="config_telephony5gNonStandalone">false</bool> + + <!-- Whether to select voice/data/sms preference without user confirmation --> + <bool name="config_voice_data_sms_auto_fallback">false</bool> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 98b36c5c9cbf..f6d1b7da78f0 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1585,6 +1585,8 @@ <!-- Template to be used to name enrolled fingerprints by default. --> <string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string> + <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their fingerprint. [CHAR LIMIT=70] --> + <string name="fingerprint_dialog_default_subtitle">Use your fingerprint to continue</string> <!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings --> <string-array name="fingerprint_error_vendor"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e92eb9722d96..d7005e4ad173 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2487,6 +2487,7 @@ <java-symbol type="string" name="fingerprint_error_lockout" /> <java-symbol type="string" name="fingerprint_error_lockout_permanent" /> <java-symbol type="string" name="fingerprint_name_template" /> + <java-symbol type="string" name="fingerprint_dialog_default_subtitle" /> <java-symbol type="string" name="fingerprint_authenticated" /> <java-symbol type="string" name="fingerprint_error_no_fingerprints" /> <java-symbol type="string" name="fingerprint_error_hw_not_present" /> @@ -3489,6 +3490,7 @@ <java-symbol type="string" name="notification_channel_do_not_disturb" /> <java-symbol type="string" name="notification_channel_accessibility_magnification" /> <java-symbol type="string" name="config_defaultAutofillService" /> + <java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" /> <java-symbol type="string" name="config_defaultTextClassifierPackage" /> <java-symbol type="string" name="config_defaultWellbeingPackage" /> <java-symbol type="string" name="config_defaultContentCaptureService" /> @@ -4165,4 +4167,6 @@ <java-symbol type="bool" name="config_telephony5gStandalone" /> <java-symbol type="bool" name="config_telephony5gNonStandalone" /> + + <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" /> </resources> diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java index b319886ae466..341ee37aee39 100644 --- a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java +++ b/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java @@ -149,7 +149,7 @@ public class InputDeviceSensorManagerTest { 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */, 0 /* sources */, 0 /* keyboardType */, null /* keyCharacterMap */, false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */, - true /* hasSensor */); + true /* hasSensor */, false /* hasBattery */); assertTrue(d.hasSensor()); return d; } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java index fbe16f2c39d1..d2107eaefefc 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java @@ -21,12 +21,10 @@ import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -194,7 +192,6 @@ public class BatteryStatsHelperTest extends TestCase { sippers.add(mBluetoothBatterySipper); sippers.add(mIdleBatterySipper); doReturn(true).when(mBatteryStatsHelper).isTypeSystem(mSystemBatterySipper); - doNothing().when(mBatteryStatsHelper).smearScreenBatterySipper(any(), any()); final double totalUsage = mBatteryStatsHelper.removeHiddenBatterySippers(sippers); @@ -208,19 +205,20 @@ public class BatteryStatsHelperTest extends TestCase { @Test public void testSmearScreenBatterySipper() { + final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class); final BatterySipper sipperNull = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO, - BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */); + BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */, spc); final BatterySipper sipperBg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO, - BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */); + BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */, spc); final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY, - BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */); + BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */, spc); final List<BatterySipper> sippers = new ArrayList<>(); sippers.add(sipperNull); sippers.add(sipperBg); sippers.add(sipperFg); - mBatteryStatsHelper.smearScreenBatterySipper(sippers, mScreenBatterySipper); + spc.smearScreenBatterySipper(sippers, mScreenBatterySipper); assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0); assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0); @@ -249,13 +247,13 @@ public class BatteryStatsHelperTest extends TestCase { @Test public void testGetProcessForegroundTimeMs_largerActivityTime_returnMinTime() { - doReturn(TIME_STATE_FOREGROUND_US + 500).when(mBatteryStatsHelper) + final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class); + doReturn(TIME_STATE_FOREGROUND_US + 500).when(spc) .getForegroundActivityTotalTimeUs(eq(mUid), anyLong()); doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP), anyLong(), anyInt()); - final long time = mBatteryStatsHelper.getProcessForegroundTimeMs(mUid, - BatteryStats.STATS_SINCE_CHARGED); + final long time = spc.getProcessForegroundTimeMs(mUid); assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS); } @@ -291,15 +289,14 @@ public class BatteryStatsHelperTest extends TestCase { } private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah, - int uidCode, boolean isUidNull) { + int uidCode, boolean isUidNull, ScreenPowerCalculator spc) { final BatterySipper sipper = mock(BatterySipper.class); sipper.drainType = BatterySipper.DrainType.APP; sipper.totalPowerMah = totalPowerMah; doReturn(uidCode).when(sipper).getUid(); if (!isUidNull) { final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS); - doReturn(activityTime).when(mBatteryStatsHelper).getProcessForegroundTimeMs(eq(uid), - anyInt()); + doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid)); doReturn(uidCode).when(uid).getUid(); sipper.uidObj = uid; } diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java index b5720a262b3d..2de800bb6d00 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java @@ -19,122 +19,87 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.os.FileUtils; - -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.File; import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class KernelSingleProcessCpuThreadReaderTest { - private File mProcDirectory; - - @Before - public void setUp() { - Context context = InstrumentationRegistry.getContext(); - mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE); - } - - @After - public void tearDown() throws Exception { - FileUtils.deleteContents(mProcDirectory); - } - @Test public void getProcessCpuUsage() throws IOException { - setupDirectory(42, - new int[] {42, 1, 2, 3}, - new int[] {1000, 2000}, - // Units are 10ms aka 10000Us - new int[][] {{100, 200}, {0, 200}, {100, 300}, {0, 600}}, - new int[] {4500, 500}); + // Units are nanoseconds + MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(4, new String[] { + "0:1000000000 2000000000 3000000000:4000000000", + "1:100000000 200000000 300000000:400000000", + }); KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(42, - mProcDirectory.toPath()); + mockReader); + reader.setSelectedThreadIds(new int[] {2, 3}); + reader.startTrackingThreadCpuTimes(); KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage = - reader.getProcessCpuUsage(new int[] {2, 3}); - assertThat(processCpuUsage.threadCpuTimesMillis).isEqualTo(new long[] {2000, 13000}); - assertThat(processCpuUsage.selectedThreadCpuTimesMillis).isEqualTo(new long[] {1000, 9000}); - assertThat(processCpuUsage.processCpuTimesMillis).isEqualTo(new long[] {6666, 43333}); + reader.getProcessCpuUsage(); + assertThat(mockReader.mTrackedTgid).isEqualTo(42); + // The strings are formatted as <TID TGID AGG_KEY>, where AGG_KEY is 1 for binder + // threads and 0 for all other threads. + assertThat(mockReader.mTrackedTasks).containsExactly( + "2 1", + "3 1"); + assertThat(processCpuUsage.threadCpuTimesMillis).isEqualTo( + new long[] {1100, 2200, 3300, 4400}); + assertThat(processCpuUsage.selectedThreadCpuTimesMillis).isEqualTo( + new long[] {100, 200, 300, 400}); } @Test public void getCpuFrequencyCount() throws IOException { - setupDirectory(13, - new int[] {13}, - new int[] {1000, 2000, 3000}, - new int[][] {{100, 200, 300}}, - new int[] {14, 15}); + MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(3, new String[0]); KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(13, - mProcDirectory.toPath()); + mockReader); int cpuFrequencyCount = reader.getCpuFrequencyCount(); assertThat(cpuFrequencyCount).isEqualTo(3); } - private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies, - int[][] threadCpuTimes, int[] processCpuTimes) - throws IOException { - - assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs()); - - try (OutputStream timeInStateStream = - Files.newOutputStream( - mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) { - for (int i = 0; i < cpuFrequencies.length; i++) { - final String line = cpuFrequencies[i] + " 0\n"; - timeInStateStream.write(line.getBytes()); - } + public static class MockCpuTimeInStateReader implements + KernelSingleProcessCpuThreadReader.CpuTimeInStateReader { + private final int mCpuFrequencyCount; + private final String[] mAggregatedTaskCpuFreqTimes; + public int mTrackedTgid; + public List<String> mTrackedTasks = new ArrayList<>(); + + public MockCpuTimeInStateReader(int cpuFrequencyCount, + String[] aggregatedTaskCpuFreqTimes) { + mCpuFrequencyCount = cpuFrequencyCount; + mAggregatedTaskCpuFreqTimes = aggregatedTaskCpuFreqTimes; } - Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid)); + @Override + public int getCpuFrequencyCount() { + return mCpuFrequencyCount; + } - // Make /proc/$PID - assertTrue(processPath.toFile().mkdirs()); + @Override + public boolean startTrackingProcessCpuTimes(int tgid) { + mTrackedTgid = tgid; + return true; + } - // Write /proc/$PID/stat. Only the fields 14-17 matter. - try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) { - timeInStateStream.write( - (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 " - + processCpuTimes[0] + " " - + processCpuTimes[1] + " " - + "16 17 18 19 20 ...").getBytes()); + public boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey) { + mTrackedTasks.add(pid + " " + aggregationKey); + return true; } - // Make /proc/$PID/task - final Path selfThreadsPath = processPath.resolve("task"); - assertTrue(selfThreadsPath.toFile().mkdirs()); - - // Make thread directories - for (int i = 0; i < threadIds.length; i++) { - // Make /proc/$PID/task/$TID - final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i])); - assertTrue(threadPath.toFile().mkdirs()); - - // Make /proc/$PID/task/$TID/time_in_state - try (OutputStream timeInStateStream = - Files.newOutputStream(threadPath.resolve("time_in_state"))) { - for (int j = 0; j < cpuFrequencies.length; j++) { - final String line = cpuFrequencies[j] + " " + threadCpuTimes[i][j] + "\n"; - timeInStateStream.write(line.getBytes()); - } - } + public String[] getAggregatedTaskCpuFreqTimes(int pid) { + return mAggregatedTaskCpuFreqTimes; } } } diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java index 121c637310c6..d116d4dcbe92 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java @@ -16,146 +16,86 @@ package com.android.internal.os; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; -import android.content.Context; -import android.os.FileUtils; - -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.File; import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; @SmallTest @RunWith(AndroidJUnit4.class) public class SystemServerCpuThreadReaderTest { - private File mProcDirectory; - - @Before - public void setUp() { - Context context = InstrumentationRegistry.getContext(); - mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE); - } - - @After - public void tearDown() throws Exception { - FileUtils.deleteContents(mProcDirectory); - } @Test - public void testReaderDelta_firstTime() throws IOException { + public void testReadDelta() throws IOException { int pid = 42; - setupDirectory( - pid, - new int[] {42, 1, 2, 3}, - new int[] {1000, 2000}, - // Units are 10ms aka 10000Us - new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}}, - new int[] {1400, 1500}); - - SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader( - mProcDirectory.toPath(), pid); + + MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(4); + // Units are nanoseconds + mockReader.setAggregatedTaskCpuFreqTimes(new String[] { + "0:1000000000 2000000000 3000000000:4000000000", + "1:100000000 200000000 300000000:400000000", + }); + + SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(pid, mockReader); reader.setBinderThreadNativeTids(new int[] {1, 3}); + + // The first invocation of readDelta populates the "last" snapshot SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = reader.readDelta(); - assertArrayEquals(new long[] {100 * 10000, 1100 * 10000}, - systemServiceCpuThreadTimes.threadCpuTimesUs); - assertArrayEquals(new long[] {0, 600 * 10000}, - systemServiceCpuThreadTimes.binderThreadCpuTimesUs); - } - - @Test - public void testReaderDelta_nextTime() throws IOException { - int pid = 42; - setupDirectory( - pid, - new int[] {42, 1, 2, 3}, - new int[] {1000, 2000}, - new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}}, - new int[] {1400, 1500}); - - SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader( - mProcDirectory.toPath(), pid); - reader.setBinderThreadNativeTids(new int[] {1, 3}); - // First time, populate "last" snapshot - reader.readDelta(); + assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs) + .isEqualTo(new long[] {1100000, 2200000, 3300000, 4400000}); + assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs) + .isEqualTo(new long[] {100000, 200000, 300000, 400000}); - FileUtils.deleteContents(mProcDirectory); - setupDirectory( - pid, - new int[] {42, 1, 2, 3}, - new int[] {1000, 2000}, - new int[][] {{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}}, - new int[] {2400, 2500}); + mockReader.setAggregatedTaskCpuFreqTimes(new String[] { + "0:1010000000 2020000000 3030000000:4040000000", + "1:101000000 202000000 303000000:404000000", + }); - // Second time, get the actual delta - SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = - reader.readDelta(); + // The second invocation gets the actual delta + systemServiceCpuThreadTimes = reader.readDelta(); - assertArrayEquals(new long[] {3100 * 10000, 2500 * 10000}, - systemServiceCpuThreadTimes.threadCpuTimesUs); - assertArrayEquals(new long[] {1800 * 10000, 1400 * 10000}, - systemServiceCpuThreadTimes.binderThreadCpuTimesUs); + assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs) + .isEqualTo(new long[] {11000, 22000, 33000, 44000}); + assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs) + .isEqualTo(new long[] {1000, 2000, 3000, 4000}); } - private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies, int[][] cpuTimes, - int[] processCpuTimes) - throws IOException { + public static class MockCpuTimeInStateReader implements + KernelSingleProcessCpuThreadReader.CpuTimeInStateReader { + private final int mCpuFrequencyCount; + private String[] mAggregatedTaskCpuFreqTimes; - assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs()); + MockCpuTimeInStateReader(int frequencyCount) { + mCpuFrequencyCount = frequencyCount; + } + + @Override + public int getCpuFrequencyCount() { + return mCpuFrequencyCount; + } + + @Override + public boolean startTrackingProcessCpuTimes(int tgid) { + return true; + } - try (OutputStream timeInStateStream = - Files.newOutputStream( - mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) { - for (int i = 0; i < cpuFrequencies.length; i++) { - final String line = cpuFrequencies[i] + " 0\n"; - timeInStateStream.write(line.getBytes()); - } + public boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey) { + return true; } - Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid)); - // Make /proc/$PID - assertTrue(processPath.toFile().mkdirs()); - - // Write /proc/$PID/stat. Only the fields 14-17 matter. - try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) { - timeInStateStream.write( - (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 " - + processCpuTimes[0] + " " - + processCpuTimes[1] + " " - + "16 17 18 19 20 ...").getBytes()); + public void setAggregatedTaskCpuFreqTimes(String[] mAggregatedTaskCpuFreqTimes) { + this.mAggregatedTaskCpuFreqTimes = mAggregatedTaskCpuFreqTimes; } - // Make /proc/$PID/task - final Path selfThreadsPath = processPath.resolve("task"); - assertTrue(selfThreadsPath.toFile().mkdirs()); - - // Make thread directories - for (int i = 0; i < threadIds.length; i++) { - // Make /proc/$PID/task/$TID - final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i])); - assertTrue(threadPath.toFile().mkdirs()); - - // Make /proc/$PID/task/$TID/time_in_state - try (OutputStream timeInStateStream = - Files.newOutputStream(threadPath.resolve("time_in_state"))) { - for (int j = 0; j < cpuFrequencies.length; j++) { - final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n"; - timeInStateStream.write(line.getBytes()); - } - } + public String[] getAggregatedTaskCpuFreqTimes(int pid) { + return mAggregatedTaskCpuFreqTimes; } } } diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java index a5cafb974b0e..24741fe110ce 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -62,7 +62,6 @@ public class SystemServicePowerCalculatorTest { public void testCalculateApp() { // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total mMockSystemServerCpuThreadReader.setCpuTimes( - new long[] {10000, 15000, 20000, 25000, 30000, 35000, 40000}, new long[] {30000, 40000, 50000, 60000, 70000, 80000, 90000}, new long[] {20000, 30000, 40000, 50000, 60000, 70000, 80000}); @@ -144,9 +143,7 @@ public class SystemServicePowerCalculatorTest { super(null); } - public void setCpuTimes(long[] processCpuTimesUs, long[] threadCpuTimesUs, - long[] binderThreadCpuTimesUs) { - mThreadTimes.processCpuTimesUs = processCpuTimesUs; + public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) { mThreadTimes.threadCpuTimesUs = threadCpuTimesUs; mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs; } diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java index 496e4707dbff..ad4c3fe86175 100644 --- a/graphics/java/android/graphics/RenderEffect.java +++ b/graphics/java/android/graphics/RenderEffect.java @@ -190,7 +190,7 @@ public final class RenderEffect { } /** - * Create a filter that applies the color filter to the provided RenderEffect + * Create a {@link RenderEffect} that applies the color filter to the provided RenderEffect * * @param colorFilter ColorFilter applied to the content in the input RenderEffect * @param renderEffect Source to be transformed by the specified {@link ColorFilter} @@ -209,7 +209,7 @@ public final class RenderEffect { } /** - * Create a filter that applies the color filter to the contents of the + * Create a {@link RenderEffect} that applies the color filter to the contents of the * {@link android.graphics.RenderNode} that this RenderEffect is installed on * @param colorFilter ColorFilter applied to the content in the input RenderEffect */ @@ -224,7 +224,7 @@ public final class RenderEffect { } /** - * {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances + * Create a {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances * combined by the specified {@link BlendMode} * * @param dst The Dst pixels used in blending @@ -248,8 +248,8 @@ public final class RenderEffect { } /** - * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are - * treated as the source bitmap passed to 'outer', i.e. + * Create a {@link RenderEffect} that composes 'inner' with 'outer', such that the results of + * 'inner' are treated as the source bitmap passed to 'outer', i.e. * * <pre> * {@code @@ -278,6 +278,18 @@ public final class RenderEffect { ); } + /** + * Create a {@link RenderEffect} that renders the contents of the input {@link Shader}. + * This is useful to create an input for other {@link RenderEffect} types such as + * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} + * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or + * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)}. + */ + @NonNull + public static RenderEffect createShaderEffect(@NonNull Shader shader) { + return new RenderEffect(nativeCreateShaderEffect(shader.getNativeInstance())); + } + private final long mNativeRenderEffect; /* only constructed from static factory methods */ @@ -305,5 +317,6 @@ public final class RenderEffect { private static native long nativeCreateColorFilterEffect(long colorFilter, long nativeInput); private static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode); private static native long nativeCreateChainEffect(long outer, long inner); + private static native long nativeCreateShaderEffect(long shader); private static native long nativeGetFinalizer(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index eaa704f22410..367f7e79d0de 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -262,7 +262,6 @@ public class OneHandedController { final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction); mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); mTimeoutHandler.resetTimer(); - mOneHandedUiEventLogger.writeEvent( OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN); } @@ -273,6 +272,9 @@ public class OneHandedController { if (mDisplayAreaOrganizer.isInOneHanded()) { mDisplayAreaOrganizer.scheduleOffset(0, 0); mTimeoutHandler.removeTimer(); + // Log metrics for Gesture navigation mode. + mOneHandedUiEventLogger.writeEvent( + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT); } } diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 615bf4de9fcc..58cbea6c99fa 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -615,6 +615,7 @@ cc_test { "tests/unit/CommonPoolTests.cpp", "tests/unit/DamageAccumulatorTests.cpp", "tests/unit/DeferredLayerUpdaterTests.cpp", + "tests/unit/EglManagerTests.cpp", "tests/unit/FatVectorTests.cpp", "tests/unit/GraphicsStatsServiceTests.cpp", "tests/unit/LayerUpdateQueueTests.cpp", diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 3aa5b4bf72f8..ca5f853066f2 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -17,8 +17,10 @@ #pragma once #include "pipeline/skia/SkiaDisplayList.h" +#include "canvas/CanvasOpBuffer.h" #include <memory> +#include <variant> namespace android { namespace uirenderer { @@ -28,29 +30,25 @@ class Tree; }; typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; -/** - * Data structure that holds the list of commands used in display list stream - */ -//using DisplayList = skiapipeline::SkiaDisplayList; -class DisplayList { +class SkiaDisplayListWrapper { public: // Constructs an empty (invalid) DisplayList - explicit DisplayList() {} + explicit SkiaDisplayListWrapper() {} // Constructs a DisplayList from a SkiaDisplayList - explicit DisplayList(std::unique_ptr<skiapipeline::SkiaDisplayList> impl) + explicit SkiaDisplayListWrapper(std::unique_ptr<skiapipeline::SkiaDisplayList> impl) : mImpl(std::move(impl)) {} // Move support - DisplayList(DisplayList&& other) : mImpl(std::move(other.mImpl)) {} - DisplayList& operator=(DisplayList&& other) { + SkiaDisplayListWrapper(SkiaDisplayListWrapper&& other) : mImpl(std::move(other.mImpl)) {} + SkiaDisplayListWrapper& operator=(SkiaDisplayListWrapper&& other) { mImpl = std::move(other.mImpl); return *this; } // No copy support - DisplayList(const DisplayList& other) = delete; - DisplayList& operator=(const DisplayList&) = delete; + SkiaDisplayListWrapper(const SkiaDisplayListWrapper& other) = delete; + SkiaDisplayListWrapper& operator=(const SkiaDisplayListWrapper&) = delete; void updateChildren(std::function<void(RenderNode*)> updateFn) { mImpl->updateChildren(std::move(updateFn)); @@ -137,7 +135,7 @@ public: void applyColorTransform(ColorTransform transform) { if (mImpl) { - mImpl->mDisplayList.applyColorTransform(transform); + mImpl->applyColorTransform(transform); } } @@ -145,5 +143,172 @@ private: std::unique_ptr<skiapipeline::SkiaDisplayList> mImpl; }; + +/** + * Data structure that holds the list of commands used in display list stream + */ +//using DisplayList = skiapipeline::SkiaDisplayList; +class MultiDisplayList { +private: + using SkiaDisplayList = skiapipeline::SkiaDisplayList; + + struct EmptyList { + bool hasText() const { return false; } + void updateChildren(std::function<void(RenderNode*)> updateFn) {} + bool isEmpty() const { return true; } + bool containsProjectionReceiver() const { return false; } + bool hasVectorDrawables() const { return false; } + size_t getUsedSize() const { return 0; } + size_t getAllocatedSize() const { return 0; } + void output(std::ostream& output, uint32_t level) const { } + bool hasFunctor() const { return false; } + bool prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) { + return false; + } + void syncContents(const WebViewSyncData& data) { } + void applyColorTransform(ColorTransform transform) { } + }; + + std::variant<EmptyList, std::unique_ptr<SkiaDisplayList>, CanvasOpBuffer> mImpls; + + template <typename T> + static constexpr T& get(T& t) { return t; } + template <typename T> + static constexpr const T& get(const T& t) { return t; } + + template <typename T> + static constexpr T& get(std::unique_ptr<T>& t) { return *t; } + template <typename T> + static constexpr const T& get(const std::unique_ptr<T>& t) { return *t; } + + template <typename T> + auto apply(T&& t) { + return std::visit([&t](auto& it) -> auto { + return t(get(it)); + }, mImpls); + } + + template <typename T> + auto apply(T&& t) const { + return std::visit([&t](const auto& it) -> auto { + return t(get(it)); + }, mImpls); + } + +public: + // Constructs an empty (invalid) DisplayList + explicit MultiDisplayList() {} + + // Constructs a DisplayList from a SkiaDisplayList + explicit MultiDisplayList(std::unique_ptr<SkiaDisplayList> impl) + : mImpls(std::move(impl)) {} + + explicit MultiDisplayList(CanvasOpBuffer&& opBuffer) : mImpls(std::move(opBuffer)) {} + + // Move support + MultiDisplayList(MultiDisplayList&& other) : mImpls(std::move(other.mImpls)) {} + MultiDisplayList& operator=(MultiDisplayList&& other) { + mImpls = std::move(other.mImpls); + return *this; + } + + // No copy support + MultiDisplayList(const MultiDisplayList& other) = delete; + MultiDisplayList& operator=(const MultiDisplayList&) = delete; + + void updateChildren(std::function<void(RenderNode*)> updateFn) { + apply([&](auto& it) { it.updateChildren(std::move(updateFn)); }); + } + + [[nodiscard]] explicit operator bool() const { + return isValid(); + } + + // If true this DisplayList contains a backing content, even if that content is empty + // If false, there this DisplayList is in an "empty" state + [[nodiscard]] bool isValid() const { + return mImpls.index() != 0; + } + + [[nodiscard]] bool isEmpty() const { + return apply([](const auto& it) -> auto { return it.isEmpty(); }); + } + + [[nodiscard]] bool hasContent() const { + return !isEmpty(); + } + + [[nodiscard]] bool containsProjectionReceiver() const { + return apply([](const auto& it) -> auto { return it.containsProjectionReceiver(); }); + } + + [[nodiscard]] SkiaDisplayList* asSkiaDl() { + return std::get<1>(mImpls).get(); + } + + [[nodiscard]] const SkiaDisplayList* asSkiaDl() const { + return std::get<1>(mImpls).get(); + } + + [[nodiscard]] bool hasVectorDrawables() const { + return apply([](const auto& it) -> auto { return it.hasVectorDrawables(); }); + } + + void clear(RenderNode* owningNode = nullptr) { + if (owningNode && mImpls.index() == 1) { + auto& skiaDl = std::get<1>(mImpls); + if (skiaDl->reuseDisplayList(owningNode)) { + skiaDl.release(); + } + } + mImpls = EmptyList{}; + } + + [[nodiscard]] size_t getUsedSize() const { + return apply([](const auto& it) -> auto { return it.getUsedSize(); }); + } + + [[nodiscard]] size_t getAllocatedSize() const { + return apply([](const auto& it) -> auto { return it.getAllocatedSize(); }); + } + + void output(std::ostream& output, uint32_t level) const { + apply([&](const auto& it) { it.output(output, level); }); + } + + [[nodiscard]] bool hasFunctor() const { + return apply([](const auto& it) -> auto { return it.hasFunctor(); }); + } + + bool prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) { + return apply([&](auto& it) -> auto { + return it.prepareListAndChildren(observer, info, functorsNeedLayer, std::move(childFn)); + }); + } + + void syncContents(const WebViewSyncData& data) { + apply([&](auto& it) { it.syncContents(data); }); + } + + [[nodiscard]] bool hasText() const { + return apply([](const auto& it) -> auto { return it.hasText(); }); + } + + void applyColorTransform(ColorTransform transform) { + apply([=](auto& it) { it.applyColorTransform(transform); }); + } + + [[nodiscard]] CanvasOpBuffer& asOpBuffer() { + return std::get<CanvasOpBuffer>(mImpls); + } +}; + +// For now stick to the original single-type container to avoid any regressions +using DisplayList = SkiaDisplayListWrapper; + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 1ebc48900349..1a8d9eb1f74f 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -700,11 +700,14 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get()); } + SkFilterMode filter = paint && paint->isFilterBitmap() ? SkFilterMode::kLinear + : SkFilterMode::kNearest; + lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); auto image = bitmap.makeImage(); apply_looper(paint, [&](const SkPaint& p) { - mCanvas->drawImageLattice(image.get(), lattice, dst, &p); + mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p); }); } diff --git a/libs/hwui/canvas/CanvasFrontend.h b/libs/hwui/canvas/CanvasFrontend.h index d749d2f2596b..f9a610129d3a 100644 --- a/libs/hwui/canvas/CanvasFrontend.h +++ b/libs/hwui/canvas/CanvasFrontend.h @@ -147,8 +147,7 @@ class CanvasFrontend final : public CanvasStateHelper { public: template<class... Args> CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height), - mReceiver(std::forward<Args>(args)...) { } - ~CanvasFrontend() = default; + mReceiver(std::in_place, std::forward<Args>(args)...) { } void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) { if (internalSave(flagsToSaveEntry(flags))) { @@ -186,7 +185,10 @@ public: submit(std::move(op)); } - const CanvasOpReceiver& receiver() const { return *mReceiver; } + const CanvasOpReceiver& receiver() const { + LOG_ALWAYS_FATAL_IF(!mReceiver.has_value()); + return *mReceiver; + } CanvasOpReceiver finish() { auto ret = std::move(mReceiver.value()); @@ -205,6 +207,7 @@ private: template <CanvasOpType T> void submit(CanvasOp<T>&& op) { + LOG_ALWAYS_FATAL_IF(!mReceiver.has_value()); mReceiver->push_container(CanvasOpContainer(std::move(op), transform())); } }; diff --git a/libs/hwui/canvas/CanvasOpBuffer.cpp b/libs/hwui/canvas/CanvasOpBuffer.cpp index 7054e47eac89..6089c572b0d5 100644 --- a/libs/hwui/canvas/CanvasOpBuffer.cpp +++ b/libs/hwui/canvas/CanvasOpBuffer.cpp @@ -22,4 +22,32 @@ namespace android::uirenderer { template class OpBuffer<CanvasOpType, CanvasOpContainer>; +void CanvasOpBuffer::updateChildren(std::function<void(RenderNode*)> updateFn) { + // TODO: Do we need a fast-path for finding children? + if (mHas.children) { + for (auto& iter : filter<CanvasOpType::DrawRenderNode>()) { + updateFn(iter->renderNode.get()); + } + } +} + +void CanvasOpBuffer::output(std::ostream& output, uint32_t level) const { + LOG_ALWAYS_FATAL("TODO"); +} + +bool CanvasOpBuffer::prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) { + LOG_ALWAYS_FATAL("TODO"); + return false; +} + +void CanvasOpBuffer::syncContents(const WebViewSyncData& data) { + LOG_ALWAYS_FATAL("TODO"); +} + +void CanvasOpBuffer::applyColorTransform(ColorTransform transform) { + LOG_ALWAYS_FATAL("TODO"); +} + } // namespace android::uirenderer diff --git a/libs/hwui/canvas/CanvasOpBuffer.h b/libs/hwui/canvas/CanvasOpBuffer.h index 07e079a7d57f..af797ca8288b 100644 --- a/libs/hwui/canvas/CanvasOpBuffer.h +++ b/libs/hwui/canvas/CanvasOpBuffer.h @@ -19,10 +19,17 @@ #include <SkMatrix.h> #include "CanvasOpTypes.h" +#include "CanvasTransform.h" #include "OpBuffer.h" +#include "TreeInfo.h" +#include "private/hwui/WebViewFunctor.h" + +#include <functional> namespace android::uirenderer { +class RenderNode; + template <CanvasOpType T> struct CanvasOp; @@ -53,12 +60,74 @@ public: }; extern template class OpBuffer<CanvasOpType, CanvasOpContainer>; -class CanvasOpBuffer final : public OpBuffer<CanvasOpType, CanvasOpContainer> { +class CanvasOpBuffer final : private OpBuffer<CanvasOpType, CanvasOpContainer> { +private: + using SUPER = OpBuffer<CanvasOpType, CanvasOpContainer>; + public: + // Expose select superclass methods publicly + using SUPER::for_each; + using SUPER::size; + using SUPER::resize; + template <CanvasOpType T> void push(CanvasOp<T>&& op) { push_container(CanvasOpContainer<T>(std::move(op))); } + + template <CanvasOpType T> + void push_container(CanvasOpContainer<T>&& op) { + if constexpr (IsDrawOp(T)) { + mHas.content = true; + } + if constexpr (T == CanvasOpType::DrawRenderNode) { + mHas.children = true; + // use staging property, since recording on UI thread + if (op->renderNode->stagingProperties().isProjectionReceiver()) { + mHas.projectionReceiver = true; + } + } + SUPER::push_container(std::move(op)); + } + + void clear() { + mHas = Contains{}; + SUPER::clear(); + } + + void updateChildren(std::function<void(RenderNode*)> updateFn); + bool prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn); + void syncContents(const WebViewSyncData& data); + void applyColorTransform(ColorTransform transform); + + [[nodiscard]] bool isEmpty() const { return !mHas.content; } + [[nodiscard]] bool hasText() const { return mHas.text; } + [[nodiscard]] bool hasVectorDrawables() const { return mHas.vectorDrawable; } + [[nodiscard]] bool containsProjectionReceiver() const { return mHas.projectionReceiver; } + [[nodiscard]] bool hasFunctor() const { return mHas.functor; } + + [[nodiscard]] size_t getUsedSize() const { + return size(); + } + + [[nodiscard]] size_t getAllocatedSize() const { + return capacity(); + } + + void output(std::ostream& output, uint32_t level) const; + +private: + struct Contains { + bool content : 1 = false; + bool children : 1 = false; + bool projectionReceiver : 1 = false; + bool text : 1 = false; + bool vectorDrawable : 1 = false; + bool functor : 1 = false; + }; + Contains mHas; }; } // namespace android::uirenderer diff --git a/libs/hwui/canvas/CanvasOpRasterizer.cpp b/libs/hwui/canvas/CanvasOpRasterizer.cpp index 0093c38cf8a8..9297604197c0 100644 --- a/libs/hwui/canvas/CanvasOpRasterizer.cpp +++ b/libs/hwui/canvas/CanvasOpRasterizer.cpp @@ -33,21 +33,15 @@ void rasterizeCanvasBuffer(const CanvasOpBuffer& source, SkCanvas* destination) SkMatrix& currentGlobalTransform = globalMatrixStack.emplace_back(SkMatrix::I()); source.for_each([&]<CanvasOpType T>(const CanvasOpContainer<T> * op) { - if constexpr ( - T == CanvasOpType::BeginZ || - T == CanvasOpType::EndZ || - T == CanvasOpType::DrawLayer - ) { - // Do beginZ or endZ - LOG_ALWAYS_FATAL("TODO"); - return; - } else { + if constexpr (CanvasOpTraits::can_draw<CanvasOp<T>>) { // Generic OP // First apply the current transformation destination->setMatrix(SkMatrix::Concat(currentGlobalTransform, op->transform())); // Now draw it (*op)->draw(destination); + return; } + LOG_ALWAYS_FATAL("TODO, unable to rasterize %d", static_cast<int>(T)); }); } diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h index cde50bd66a15..b55ef9d5537c 100644 --- a/libs/hwui/canvas/CanvasOpTypes.h +++ b/libs/hwui/canvas/CanvasOpTypes.h @@ -35,7 +35,8 @@ enum class CanvasOpType : int8_t { ClipPath, // Drawing ops - DrawColor, + DRAW_OP_BEGIN, + DrawColor = DRAW_OP_BEGIN, DrawRect, DrawRegion, DrawRoundRect, @@ -59,10 +60,16 @@ enum class CanvasOpType : int8_t { DrawImageLattice, DrawPicture, DrawLayer, + DrawRenderNode, + DRAW_OP_END = DrawRenderNode, // TODO: Rest COUNT // must be last }; +static constexpr bool IsDrawOp(CanvasOpType t) { + return CanvasOpType::DRAW_OP_BEGIN <= t && t <= CanvasOpType::DRAW_OP_END; +} + } // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h index fa0c45b5221c..77e59acd9a95 100644 --- a/libs/hwui/canvas/CanvasOps.h +++ b/libs/hwui/canvas/CanvasOps.h @@ -24,13 +24,15 @@ #include <SkImage.h> #include <SkPicture.h> #include <SkRuntimeEffect.h> -#include <hwui/Bitmap.h> + #include <log/log.h> -#include "CanvasProperty.h" -#include "Points.h" +#include "hwui/Bitmap.h" +#include "CanvasProperty.h" #include "CanvasOpTypes.h" #include "Layer.h" +#include "Points.h" +#include "RenderNode.h" #include <experimental/type_traits> #include <utility> @@ -408,21 +410,24 @@ struct CanvasOp<CanvasOpType::DrawImageLattice> { const sk_sp<Bitmap>& bitmap, SkRect dst, SkCanvas::Lattice lattice, + SkFilterMode filter, SkPaint paint ): dst(dst), lattice(lattice), + filter(filter), bitmap(bitmap), image(bitmap->makeImage()), paint(std::move(paint)) {} SkRect dst; SkCanvas::Lattice lattice; + SkFilterMode filter; const sk_sp<Bitmap> bitmap; const sk_sp<SkImage> image; SkPaint paint; void draw(SkCanvas* canvas) const { - canvas->drawImageLattice(image.get(), lattice, dst, &paint); + canvas->drawImageLattice(image.get(), lattice, dst, filter, &paint); } ASSERT_DRAWABLE() }; @@ -440,6 +445,11 @@ struct CanvasOp<CanvasOpType::DrawLayer> { sp<Layer> layer; }; +template<> +struct CanvasOp<CanvasOpType::DrawRenderNode> { + sp<RenderNode> renderNode; +}; + // cleanup our macros #undef ASSERT_DRAWABLE diff --git a/libs/hwui/canvas/OpBuffer.h b/libs/hwui/canvas/OpBuffer.h index 1237d69a3c24..8b5cdbbec958 100644 --- a/libs/hwui/canvas/OpBuffer.h +++ b/libs/hwui/canvas/OpBuffer.h @@ -64,7 +64,7 @@ public: static constexpr auto STARTING_SIZE = PadAlign(sizeof(BufferHeader)); using ItemHeader = OpBufferItemHeader<ItemTypes>; - OpBuffer() = default; + explicit OpBuffer() = default; // Prevent copying by default OpBuffer(const OpBuffer&) = delete; @@ -135,7 +135,7 @@ public: template <typename F> void for_each(F&& f) const { - for_each(std::forward<F>(f), ItemTypesSequence{}); + do_for_each(std::forward<F>(f), ItemTypesSequence{}); } void clear(); @@ -225,7 +225,7 @@ private: } template <typename F, std::size_t... I> - void for_each(F&& f, std::index_sequence<I...>) const { + void do_for_each(F&& f, std::index_sequence<I...>) const { // Validate we're not empty if (isEmpty()) return; diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp index 97c40d695f97..fa1752cc47d6 100644 --- a/libs/hwui/jni/RenderEffect.cpp +++ b/libs/hwui/jni/RenderEffect.cpp @@ -115,6 +115,18 @@ static jlong createChainEffect( return reinterpret_cast<jlong>(composeFilter.release()); } +static jlong createShaderEffect( + JNIEnv* env, + jobject, + jlong shaderHandle +) { + auto* shader = reinterpret_cast<const SkShader*>(shaderHandle); + sk_sp<SkImageFilter> shaderFilter = SkImageFilters::Shader( + sk_ref_sp(shader), nullptr + ); + return reinterpret_cast<jlong>(shaderFilter.release()); +} + static void RenderEffect_safeUnref(SkImageFilter* filter) { SkSafeUnref(filter); } @@ -130,7 +142,8 @@ static const JNINativeMethod gRenderEffectMethods[] = { {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect}, {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect}, {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect}, - {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect} + {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect}, + {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect} }; int register_android_graphics_RenderEffect(JNIEnv* env) { diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index 483264f95e60..1136e58e43d8 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -16,6 +16,8 @@ #pragma once +#include <deque> + #include "RecordingCanvas.h" #include "RenderNodeDrawable.h" #include "TreeInfo.h" @@ -23,8 +25,6 @@ #include "utils/LinearAllocator.h" #include "utils/Pair.h" -#include <deque> - namespace android { namespace uirenderer { @@ -46,8 +46,10 @@ class FunctorDrawable; class SkiaDisplayList { public: - size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); } - size_t getAllocatedSize() { return allocator.allocatedSize() + mDisplayList.allocatedSize(); } + size_t getUsedSize() const { return allocator.usedSize() + mDisplayList.usedSize(); } + size_t getAllocatedSize() const { + return allocator.allocatedSize() + mDisplayList.allocatedSize(); + } ~SkiaDisplayList() { /* Given that we are using a LinearStdAllocator to store some of the @@ -109,6 +111,10 @@ public: */ void syncContents(const WebViewSyncData& data); + void applyColorTransform(ColorTransform transform) { + mDisplayList.applyColorTransform(transform); + } + /** * ONLY to be called by RenderNode::prepareTree in order to prepare this * list while the UI thread is blocked. Here we can upload mutable bitmaps @@ -154,12 +160,12 @@ public: std::deque<RenderNodeDrawable> mChildNodes; std::deque<FunctorDrawable*> mChildFunctors; std::vector<SkImage*> mMutableImages; + private: std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables; + public: - void appendVD(VectorDrawableRoot* r) { - appendVD(r, SkMatrix::I()); - } + void appendVD(VectorDrawableRoot* r) { appendVD(r, SkMatrix::I()); } void appendVD(VectorDrawableRoot* r, const SkMatrix& mat) { mVectorDrawables.push_back(Pair<VectorDrawableRoot*, SkMatrix>(r, mat)); diff --git a/libs/hwui/tests/unit/CanvasFrontendTests.cpp b/libs/hwui/tests/unit/CanvasFrontendTests.cpp index 05b11795d90d..4ddcf6f6ac8a 100644 --- a/libs/hwui/tests/unit/CanvasFrontendTests.cpp +++ b/libs/hwui/tests/unit/CanvasFrontendTests.cpp @@ -124,12 +124,12 @@ TEST(CanvasFrontend, transform) { TEST(CanvasFrontend, drawOpTransform) { CanvasFrontend<CanvasOpBuffer> opCanvas(100, 100); - const auto& receiver = opCanvas.receiver(); + const auto &receiver = opCanvas.receiver(); auto makeDrawRect = [] { return CanvasOp<CanvasOpType::DrawRect>{ - .rect = SkRect::MakeWH(50, 50), - .paint = SkPaint(SkColors::kBlack), + .rect = SkRect::MakeWH(50, 50), + .paint = SkPaint(SkColors::kBlack), }; }; @@ -167,14 +167,14 @@ TEST(CanvasFrontend, drawOpTransform) { { // First result should be identity - const auto& result = transforms[0]; + const auto &result = transforms[0]; EXPECT_EQ(SkMatrix::kIdentity_Mask, result.getType()); EXPECT_EQ(SkMatrix::I(), result); } { // Should be translate 10, 10 - const auto& result = transforms[1]; + const auto &result = transforms[1]; EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType()); SkMatrix m; m.setTranslate(10, 10); @@ -183,7 +183,7 @@ TEST(CanvasFrontend, drawOpTransform) { { // Should be translate 10, 10 + scale 2, 4 - const auto& result = transforms[2]; + const auto &result = transforms[2]; EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask, result.getType()); SkMatrix m; m.setTranslate(10, 10); @@ -193,7 +193,7 @@ TEST(CanvasFrontend, drawOpTransform) { { // Should be translate 10, 10 + translate 20, 15 - const auto& result = transforms[3]; + const auto &result = transforms[3]; EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType()); SkMatrix m; m.setTranslate(30, 25); @@ -202,9 +202,9 @@ TEST(CanvasFrontend, drawOpTransform) { { // Should be translate 10, 10 + translate 20, 15 + rotate 90 - const auto& result = transforms[4]; + const auto &result = transforms[4]; EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask, - result.getType()); + result.getType()); SkMatrix m; m.setTranslate(30, 25); m.preRotate(90.f); diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp index a09e7429b4be..12fa32b41008 100644 --- a/libs/hwui/tests/unit/CanvasOpTests.cpp +++ b/libs/hwui/tests/unit/CanvasOpTests.cpp @@ -149,7 +149,7 @@ TEST(CanvasOp, simpleDrawPoints) { CanvasOpBuffer buffer; EXPECT_EQ(buffer.size(), 0); size_t numPts = 3; - auto pts = sk_ref_sp( + auto pts = sk_sp<Points>( new Points({ {32, 16}, {48, 48}, @@ -192,7 +192,7 @@ TEST(CanvasOp, simpleDrawLines) { CanvasOpBuffer buffer; EXPECT_EQ(buffer.size(), 0); size_t numPts = 3; - auto pts = sk_ref_sp( + auto pts = sk_sp<Points>( new Points({ {32, 16}, {48, 48}, @@ -528,6 +528,7 @@ TEST(CanvasOp, simpleDrawImageLattice) { bitmap, SkRect::MakeWH(5, 1), lattice, + SkFilterMode::kNearest, SkPaint{} } ); diff --git a/libs/hwui/tests/unit/EglManagerTests.cpp b/libs/hwui/tests/unit/EglManagerTests.cpp new file mode 100644 index 000000000000..f7f240663397 --- /dev/null +++ b/libs/hwui/tests/unit/EglManagerTests.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "renderthread/EglManager.h" +#include "tests/common/TestContext.h" + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; +using namespace android::uirenderer::test; + +TEST(EglManager, doesSurfaceLeak) { + EglManager eglManager; + eglManager.initialize(); + + ASSERT_TRUE(eglManager.hasEglContext()); + + auto colorSpace = SkColorSpace::MakeSRGB(); + for (int i = 0; i < 100; i++) { + TestContext context; + auto result = + eglManager.createSurface(context.surface().get(), ColorMode::Default, colorSpace); + EXPECT_TRUE(result); + EGLSurface surface = result.unwrap(); + eglManager.destroySurface(surface); + } + + eglManager.destroy(); +}
\ No newline at end of file diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 6dd57b19a41b..8c999c41bf7b 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -404,6 +404,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { EXPECT_TRUE(pipeline->isSurfaceReady()); renderThread.destroyRenderingContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); + LOG_ALWAYS_FATAL_IF(pipeline->isSurfaceReady()); } RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) { diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index d40de76431e4..205c1f4b4057 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -354,7 +354,7 @@ public final class AudioDeviceInfo { /** * @hide - * @return the internal device tyoe + * @return the internal device type */ public int getInternalType() { return mPort.type(); diff --git a/media/java/android/media/AudioProfile.java b/media/java/android/media/AudioProfile.java index 3cd615b658b2..9774e8032c95 100644 --- a/media/java/android/media/AudioProfile.java +++ b/media/java/android/media/AudioProfile.java @@ -18,6 +18,9 @@ package android.media; import android.annotation.NonNull; +import java.util.Arrays; +import java.util.stream.Collectors; + /** * An AudioProfile is specific to an audio format and lists supported sampling rates and * channel masks for that format. An {@link AudioDeviceInfo} has a list of supported AudioProfiles. @@ -63,4 +66,29 @@ public class AudioProfile { public @NonNull int[] getSampleRates() { return mSamplingRates; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("{"); + sb.append(AudioFormat.toLogFriendlyEncoding(mFormat)); + if (mSamplingRates != null && mSamplingRates.length > 0) { + sb.append(", sampling rates=").append(Arrays.toString(mSamplingRates)); + } + if (mChannelMasks != null && mChannelMasks.length > 0) { + sb.append(", channel masks=").append(toHexString(mChannelMasks)); + } + if (mChannelIndexMasks != null && mChannelIndexMasks.length > 0) { + sb.append(", channel index masks=").append(Arrays.toString(mChannelIndexMasks)); + } + sb.append("}"); + return sb.toString(); + } + + private static String toHexString(int[] ints) { + if (ints == null || ints.length == 0) { + return ""; + } + return Arrays.stream(ints).mapToObj(anInt -> String.format("0x%02X, ", anInt)) + .collect(Collectors.joining()); + } } diff --git a/media/java/android/media/MediaServiceManager.java b/media/java/android/media/MediaServiceManager.java index 96bff4f92668..b899559d2e50 100644 --- a/media/java/android/media/MediaServiceManager.java +++ b/media/java/android/media/MediaServiceManager.java @@ -33,6 +33,7 @@ import android.os.ServiceManager; public class MediaServiceManager { private static final String MEDIA_SESSION_SERVICE = "media_session"; private static final String MEDIA_TRANSCODING_SERVICE = "media.transcoding"; + private static final String MEDIA_COMMUNICATION_SERVICE = "media_communication"; /** * @hide @@ -79,4 +80,12 @@ public class MediaServiceManager { public ServiceRegisterer getMediaTranscodingServiceRegisterer() { return new ServiceRegisterer(MEDIA_TRANSCODING_SERVICE); } + + /** + * Returns {@link ServiceRegisterer} for MEDIA_COMMUNICATION_SERVICE. + */ + @NonNull + public ServiceRegisterer getMediaCommunicationServiceRegisterer() { + return new ServiceRegisterer(MEDIA_COMMUNICATION_SERVICE); + } } diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index 6b329f8ec694..b1baf94f844b 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -23,6 +23,8 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.Rect; @@ -39,6 +41,7 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; +import android.util.Xml; import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; @@ -99,6 +102,7 @@ public class TvView extends ViewGroup { private int mSurfaceWidth; private int mSurfaceHeight; private final AttributeSet mAttrs; + private final XmlResourceParser mParser; private final int mDefStyleAttr; private int mWindowZOrder; private boolean mUseRequestedSurfaceLayout; @@ -168,7 +172,16 @@ public class TvView extends ViewGroup { public TvView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mAttrs = attrs; + int sourceResId = Resources.getAttributeSetSourceResId(attrs); + if (sourceResId != Resources.ID_NULL) { + Log.d(TAG, "Build local AttributeSet"); + mParser = context.getResources().getXml(sourceResId); + mAttrs = Xml.asAttributeSet(mParser); + } else { + Log.d(TAG, "Use passed in AttributeSet"); + mParser = null; + mAttrs = attrs; + } mDefStyleAttr = defStyleAttr; resetSurfaceView(); mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE); diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 96371e505a77..9f327c93f63b 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -209,7 +209,7 @@ public class Filter implements AutoCloseable { prefix = "MONITOR_EVENT_", value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE}) @Retention(RetentionPolicy.SOURCE) - public @interface MonitorEventTypeMask {} + public @interface MonitorEventMask {} /** * Monitor scrambling status change. @@ -239,7 +239,7 @@ public class Filter implements AutoCloseable { int type, int subType, FilterConfiguration settings); private native int nativeGetId(); private native long nativeGetId64Bit(); - private native int nativeConfigureMonitorEvent(int monitorEventTypesMask); + private native int nativeConfigureMonitorEvent(int monitorEventMask); private native int nativeSetDataSource(Filter source); private native int nativeStartFilter(); private native int nativeStopFilter(); @@ -344,19 +344,19 @@ public class Filter implements AutoCloseable { * will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version * information. * - * @param monitorEventTypesMask Types of event to be monitored. Set corresponding bit to - * monitor it. Reset to stop monitoring. + * @param monitorEventMask Types of event to be monitored. Set corresponding bit to + * monitor it. Reset to stop monitoring. * @return result status of the operation. */ @Result - public int configureMonitorEvent(@MonitorEventTypeMask int monitorEventTypesMask) { + public int setMonitorEventMask(@MonitorEventMask int monitorEventMask) { synchronized (mLock) { TunerUtils.checkResourceState(TAG, mIsClosed); if (!TunerVersionChecker.checkHigherOrEqualVersionTo( - TunerVersionChecker.TUNER_VERSION_1_1, "configureMonitorEvent")) { + TunerVersionChecker.TUNER_VERSION_1_1, "setMonitorEventMask")) { return Tuner.RESULT_UNAVAILABLE; } - return nativeConfigureMonitorEvent(monitorEventTypesMask); + return nativeConfigureMonitorEvent(monitorEventMask); } } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index efd941e3bd18..757a90e656c7 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -749,7 +749,8 @@ public class SettingsBackupTest { Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER, Settings.Secure.SUPPRESS_DOZE, Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, - Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT); + Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, + Settings.Secure.TRANSFORM_ENABLED); @Test public void systemSettingsBackedUpOrDenied() { diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml index 827cf4a9d3b6..7bc7ba1e444d 100644 --- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml +++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml @@ -16,7 +16,7 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="#242424" /> <!-- 14% of white --> + <solid android:color="?android:attr/colorAccent" /> <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding" android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" /> <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" /> diff --git a/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml b/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml new file mode 100644 index 000000000000..1511659ea42f --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml @@ -0,0 +1,24 @@ +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/signal_icon_size" + android:height="@dimen/signal_icon_size" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M14.21,12.81c0.36,-0.16 0.69,-0.36 0.97,-0.61c0.41,-0.38 0.72,-0.83 0.94,-1.37c0.21,-0.54 0.32,-1.14 0.32,-1.79c0,-0.92 -0.16,-1.7 -0.49,-2.33c-0.32,-0.64 -0.79,-1.12 -1.43,-1.45c-0.62,-0.33 -1.4,-0.49 -2.32,-0.49H8.23V19h1.8v-5.76h2.5L15.06,19h1.92v-0.12L14.21,12.81zM10.03,11.71V6.32h2.18c0.59,0 1.06,0.11 1.42,0.34c0.36,0.22 0.62,0.54 0.78,0.95c0.16,0.41 0.24,0.89 0.24,1.44c0,0.49 -0.09,0.93 -0.27,1.34c-0.18,0.4 -0.46,0.73 -0.82,0.97c-0.36,0.23 -0.82,0.35 -1.37,0.35H10.03z"/> +</vector> diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml index bfd079b59054..5552020f22cb 100644 --- a/packages/SystemUI/res/layout/mobile_signal_group.xml +++ b/packages/SystemUI/res/layout/mobile_signal_group.xml @@ -77,4 +77,11 @@ android:contentDescription="@string/data_connection_roaming" android:visibility="gone" /> </FrameLayout> + <ImageView + android:id="@+id/mobile_roaming_large" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/stat_sys_roaming_large" + android:contentDescription="@string/data_connection_roaming" + android:visibility="gone" /> </com.android.keyguard.AlphaOptimizedLinearLayout> diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml index 42d541e3afc9..10d49b38ae75 100644 --- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml +++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml @@ -85,6 +85,13 @@ android:contentDescription="@string/data_connection_roaming" android:visibility="gone" /> </FrameLayout> + <ImageView + android:id="@+id/mobile_roaming_large" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/stat_sys_roaming_large" + android:contentDescription="@string/data_connection_roaming" + android:visibility="gone" /> </com.android.keyguard.AlphaOptimizedLinearLayout> </com.android.systemui.statusbar.StatusBarMobileView> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index 49f91096543a..01e54ff60582 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -31,4 +31,9 @@ <!-- AOD/Lockscreen alternate layout --> <bool name="flag_keyguard_layout">false</bool> + + <bool name="flag_brightness_slider">false</bool> + + <!-- People Tile flag --> + <bool name="flag_conversations">false</bool> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 036fcf3bec27..78f7966558ab 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -127,7 +127,7 @@ public class SystemUIApplication extends Application implements } // If SHOW_PEOPLE_SPACE is true, enable People Space widget provider. - // TODO(b/170396074): Remove this when we don't need a widget anymore. + // TODO(b/170396074): Migrate to new feature flag (go/silk-flags-howto) try { int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(), Settings.Global.SHOW_PEOPLE_SPACE, 1); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 055270ddf0b8..7d06dd6ab085 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -23,7 +23,6 @@ import static android.hardware.biometrics.BiometricManager.Authenticators; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; -import android.app.IActivityTaskManager; import android.app.TaskStackListener; import android.content.BroadcastReceiver; import android.content.Context; @@ -137,7 +136,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mActivityTaskManager.getTasks(1); if (!runningTasks.isEmpty()) { final String topPackage = runningTasks.get(0).topActivity.getPackageName(); - if (!topPackage.contentEquals(clientPackage)) { + if (!topPackage.contentEquals(clientPackage) + && !Utils.isSystem(mContext, clientPackage)) { Log.w(TAG, "Evicting client due to: " + topPackage); mCurrentDialog.dismissWithoutCallback(true /* animate */); mCurrentDialog = null; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java index fd5e85a953ad..076c7cbe3937 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java @@ -16,13 +16,16 @@ package com.android.systemui.biometrics; +import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.hardware.biometrics.BiometricManager.Authenticators; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.PackageManager; import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorPropertiesInternal; import android.os.UserManager; @@ -116,4 +119,10 @@ public class Utils { return false; } + + static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) { + final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) + == PackageManager.PERMISSION_GRANTED; + return hasPermission && "android".equals(clientPackage); + } } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt index 870e714ee24c..ec9291eba850 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt @@ -21,6 +21,7 @@ import android.view.ViewGroup import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout +import com.android.settingslib.Utils import com.android.systemui.R class OngoingPrivacyChip @JvmOverloads constructor( @@ -36,8 +37,9 @@ class OngoingPrivacyChip @JvmOverloads constructor( R.dimen.ongoing_appops_chip_icon_margin_collapsed) private val iconSize = context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size) - private val iconColor = context.resources.getColor( - R.color.status_bar_clock_color, context.theme) + private val iconColor = Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) + private val expandedIconColor = Utils + .getColorAttrDefaultColor(context, android.R.attr.textColorPrimaryInverse) private val sidePadding = context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg) @@ -73,7 +75,7 @@ class OngoingPrivacyChip @JvmOverloads constructor( iconsContainer.removeAllViews() chipBuilder.generateIcons().forEachIndexed { i, it -> it.mutate() - it.setTint(iconColor) + it.setTint(if (expanded) expandedIconColor else iconColor) val image = ImageView(context).apply { setImageDrawable(it) scaleType = ImageView.ScaleType.CENTER_INSIDE diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java index 39b92dc4f662..7d8d86fe691e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.ColorStateList; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.FeatureFlagUtils; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; @@ -65,10 +66,13 @@ public class QSCarrier extends LinearLayout { super.onFinishInflate(); mDualToneHandler = new DualToneHandler(getContext()); mMobileGroup = findViewById(R.id.mobile_combo); + if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) { + mMobileRoaming = findViewById(R.id.mobile_roaming_large); + } else { + mMobileRoaming = findViewById(R.id.mobile_roaming); + } mMobileSignal = findViewById(R.id.mobile_signal); - mMobileRoaming = findViewById(R.id.mobile_roaming); mCarrierText = findViewById(R.id.qs_carrier_text); - mMobileSignal.setImageDrawable(new SignalDrawable(mContext)); int colorForeground = Utils.getColorAttrDefaultColor(mContext, diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java index ebdcc0006dce..77200ccaf5cb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java @@ -71,7 +71,7 @@ public class QSCarrierGroupController { boolean activityIn, boolean activityOut, CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming) { + boolean isWide, int subId, boolean roaming, boolean showTriangle) { int slotIndex = getSlotIndex(subId); if (slotIndex >= SIM_SLOTS) { Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index f742752d80be..720c5dc7026f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -268,7 +268,7 @@ public class CellularTile extends QSTileImpl<SignalState> { int qsType, boolean activityIn, boolean activityOut, CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming) { + boolean isWide, int subId, boolean roaming, boolean showTriangle) { if (qsIcon == null) { // Not data sim, don't display. return; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java index 15232789cc64..5e1ca294c0e3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -249,7 +249,7 @@ public class InternetTile extends QSTileImpl<SignalState> { int qsType, boolean activityIn, boolean activityOut, CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming) { + boolean isWide, int subId, boolean roaming, boolean showTriangle) { if (DEBUG) { Log.d(TAG, "setMobileDataIndicators: " + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + "," @@ -263,7 +263,8 @@ public class InternetTile extends QSTileImpl<SignalState> { + "description = " + description + "," + "isWide = " + isWide + "," + "subId = " + subId + "," - + "roaming = " + roaming); + + "roaming = " + roaming + "," + + "showTriangle = " + showTriangle); } if (qsIcon == null) { // Not data sim, don't display. diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java index fb281169b2f1..63e27796f441 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java @@ -47,6 +47,7 @@ import javax.inject.Inject; /** Quick settings tile: Enable/Disable NFC **/ public class NfcTile extends QSTileImpl<BooleanState> { + private static final String NFC = "nfc"; private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_nfc); private NfcAdapter mAdapter; @@ -89,7 +90,13 @@ public class NfcTile extends QSTileImpl<BooleanState> { @Override public boolean isAvailable() { - return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC); + String stockTiles = mContext.getString(R.string.quick_settings_tiles_stock); + // For the restore from backup case + // Return false when "nfc" is not listed in quick_settings_tiles_stock. + if (stockTiles.contains(NFC)) { + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC); + } + return false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java index 11ee94c97d7c..8dcc8b46f024 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java @@ -17,6 +17,7 @@ package com.android.systemui.settings.brightness; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.util.settings.SecureSettings; import javax.inject.Inject; @@ -28,11 +29,13 @@ import javax.inject.Inject; public class BrightnessControllerSettings { private static final String THICK_BRIGHTNESS_SLIDER = "sysui_thick_brightness"; + private final FeatureFlags mFeatureFlags; private final boolean mUseThickSlider; private final boolean mUseMirrorOnThickSlider; @Inject - public BrightnessControllerSettings(SecureSettings settings) { + public BrightnessControllerSettings(SecureSettings settings, FeatureFlags featureFlags) { + mFeatureFlags = featureFlags; mUseThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 0; mUseMirrorOnThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 2; } @@ -41,11 +44,11 @@ public class BrightnessControllerSettings { // restart systemui after changing it. /** */ boolean useThickSlider() { - return mUseThickSlider; + return mUseThickSlider && mFeatureFlags.useNewBrightnessSlider(); } /** */ boolean useMirrorOnThickSlider() { - return mUseMirrorOnThickSlider; + return !useThickSlider() || (useThickSlider() && mUseMirrorOnThickSlider); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index 30b31580e130..e7b60c3a0d67 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -61,4 +61,13 @@ public class FeatureFlags { public boolean isKeyguardLayoutEnabled() { return mFlagReader.isEnabled(R.bool.flag_keyguard_layout); } + + /** b/178485354 */ + public boolean useNewBrightnessSlider() { + return mFlagReader.isEnabled(R.bool.flag_brightness_slider); + } + + public boolean isPeopleTileEnabled() { + return mFlagReader.isEnabled(R.bool.flag_conversations); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java index fdb793e6da95..924eb263de50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java @@ -131,6 +131,7 @@ public class RemoteInputController { */ public void removeRemoteInput(NotificationEntry entry, Object token) { Objects.requireNonNull(entry); + if (entry.mRemoteEditImeVisible) return; pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java index 239addd4ee1d..d562726681f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -27,6 +27,7 @@ import android.content.res.ColorStateList; import android.graphics.Color; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.FeatureFlagUtils; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -64,7 +65,6 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, LayoutInflater inflater = LayoutInflater.from(context); StatusBarMobileView v = (StatusBarMobileView) inflater.inflate(R.layout.status_bar_mobile_signal_group, null); - v.setSlot(slot); v.init(); v.setVisibleState(STATE_ICON); @@ -104,7 +104,11 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, mMobileGroup = findViewById(R.id.mobile_group); mMobile = findViewById(R.id.mobile_signal); mMobileType = findViewById(R.id.mobile_type); - mMobileRoaming = findViewById(R.id.mobile_roaming); + if (FeatureFlagUtils.isEnabled(getContext(), FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) { + mMobileRoaming = findViewById(R.id.mobile_roaming_large); + } else { + mMobileRoaming = findViewById(R.id.mobile_roaming); + } mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space); mIn = findViewById(R.id.mobile_in); mOut = findViewById(R.id.mobile_out); @@ -160,7 +164,7 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, } else { mMobileType.setVisibility(View.GONE); } - + mMobile.setVisibility(mState.showTriangle ? View.VISIBLE : View.GONE); mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE); mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE); mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); @@ -191,6 +195,7 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, } } + mMobile.setVisibility(state.showTriangle ? View.VISIBLE : View.GONE); mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE); mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE); mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); @@ -200,7 +205,8 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, needsLayout |= state.roaming != mState.roaming || state.activityIn != mState.activityIn - || state.activityOut != mState.activityOut; + || state.activityOut != mState.activityOut + || state.showTriangle != mState.showTriangle; mState = state; return needsLayout; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index bafa4a254d79..8a22b9f6891f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -179,6 +179,8 @@ public final class NotificationEntry extends ListEntry { private boolean mShelfIconVisible; private boolean mIsAlerting; + public boolean mRemoteEditImeVisible; + /** * @param sbn the StatusBarNotification from system server * @param ranking also from system server diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java index 4fde1187477c..db49e4476a99 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java @@ -21,7 +21,6 @@ import android.util.Log; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; -import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.notification.collection.NotifCollection; @@ -50,7 +49,6 @@ public class NotifPipelineInitializer implements Dumpable { private final ShadeListBuilder mListBuilder; private final NotifCoordinators mNotifPluggableCoordinators; private final NotifInflaterImpl mNotifInflater; - private final PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; private final DumpManager mDumpManager; private final ShadeViewManagerFactory mShadeViewManagerFactory; private final FeatureFlags mFeatureFlags; @@ -64,7 +62,6 @@ public class NotifPipelineInitializer implements Dumpable { ShadeListBuilder listBuilder, NotifCoordinators notifCoordinators, NotifInflaterImpl notifInflater, - PeopleSpaceWidgetManager peopleSpaceWidgetManager, DumpManager dumpManager, ShadeViewManagerFactory shadeViewManagerFactory, FeatureFlags featureFlags) { @@ -75,7 +72,6 @@ public class NotifPipelineInitializer implements Dumpable { mNotifPluggableCoordinators = notifCoordinators; mDumpManager = dumpManager; mNotifInflater = notifInflater; - mPeopleSpaceWidgetManager = peopleSpaceWidgetManager; mShadeViewManagerFactory = shadeViewManagerFactory; mFeatureFlags = featureFlags; } @@ -103,7 +99,6 @@ public class NotifPipelineInitializer implements Dumpable { mListBuilder.attach(mNotifCollection); mNotifCollection.attach(mGroupCoalescer); mGroupCoalescer.attach(notificationService); - mPeopleSpaceWidgetManager.attach(notificationService); Log.d(TAG, "Notif pipeline initialized"); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt index 54ce4ede9770..0ad6507fb01e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.init import android.service.notification.StatusBarNotification import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.people.widget.PeopleSpaceWidgetManager import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.NotificationListener @@ -73,7 +74,8 @@ class NotificationsControllerImpl @Inject constructor( private val headsUpController: HeadsUpController, private val headsUpViewBinder: HeadsUpViewBinder, private val clickerBuilder: NotificationClicker.Builder, - private val animatedImageNotificationManager: AnimatedImageNotificationManager + private val animatedImageNotificationManager: AnimatedImageNotificationManager, + private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager ) : NotificationsController { override fun initialize( @@ -126,6 +128,10 @@ class NotificationsControllerImpl @Inject constructor( entryManager.attach(notificationListener) } + + if (featureFlags.isPeopleTileEnabled) { + peopleSpaceWidgetManager.attach(notificationListener) + } } override fun dump( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index d11e8641f987..5f90077640ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -221,7 +221,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba int qsType, boolean activityIn, boolean activityOut, CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming) { + boolean isWide, int subId, boolean roaming, boolean showTriangle) { if (DEBUG) { Log.d(TAG, "setMobileDataIndicators: " + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + "," @@ -235,7 +235,8 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba + "description = " + description + "," + "isWide = " + isWide + "," + "subId = " + subId + "," - + "roaming = " + roaming); + + "roaming = " + roaming + "," + + "showTriangle = " + showTriangle); } MobileIconState state = getState(subId); if (state == null) { @@ -250,6 +251,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba state.typeId = statusType; state.contentDescription = statusIcon.contentDescription; state.typeContentDescription = typeContentDescription; + state.showTriangle = showTriangle; state.roaming = roaming; state.activityIn = activityIn && mActivityEnabled; state.activityOut = activityOut && mActivityEnabled; @@ -551,6 +553,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba public int subId; public int strengthId; public int typeId; + public boolean showTriangle; public boolean roaming; public boolean needsLeadingPadding; public CharSequence typeContentDescription; @@ -569,20 +572,21 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba return false; } MobileIconState that = (MobileIconState) o; - return subId == that.subId && - strengthId == that.strengthId && - typeId == that.typeId && - roaming == that.roaming && - needsLeadingPadding == that.needsLeadingPadding && - Objects.equals(typeContentDescription, that.typeContentDescription); + return subId == that.subId + && strengthId == that.strengthId + && typeId == that.typeId + && showTriangle == that.showTriangle + && roaming == that.roaming + && needsLeadingPadding == that.needsLeadingPadding + && Objects.equals(typeContentDescription, that.typeContentDescription); } @Override public int hashCode() { return Objects - .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding, - typeContentDescription); + .hash(super.hashCode(), subId, strengthId, typeId, showTriangle, roaming, + needsLeadingPadding, typeContentDescription); } public MobileIconState copy() { @@ -596,6 +600,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba other.subId = subId; other.strengthId = strengthId; other.typeId = typeId; + other.showTriangle = showTriangle; other.roaming = roaming; other.needsLeadingPadding = needsLeadingPadding; other.typeContentDescription = typeContentDescription; @@ -613,8 +618,9 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba } @Override public String toString() { - return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming=" - + roaming + ", typeId=" + typeId + ", visible=" + visible + ")"; + return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + + ", showTriangle=" + showTriangle + ", roaming=" + roaming + + ", typeId=" + typeId + ", visible=" + visible + ")"; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java index 5e88cd5c2423..08a4492fc15d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java @@ -124,12 +124,13 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa final int statusType, final int qsType, final boolean activityIn, final boolean activityOut, final CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, final CharSequence description, - final boolean isWide, final int subId, boolean roaming) { + final boolean isWide, final int subId, boolean roaming, boolean showTriangle) { post(() -> { for (SignalCallback signalCluster : mSignalCallbacks) { signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType, activityIn, activityOut, typeContentDescription, - typeContentDescriptionHtml, description, isWide, subId, roaming); + typeContentDescriptionHtml, description, isWide, subId, roaming, + showTriangle); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 2f66508dafee..39472ded4a3f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -284,13 +284,15 @@ public class MobileSignalController extends SignalController<MobileState, Mobile && !mCurrentState.carrierNetworkChangeMode && mCurrentState.activityOut; showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault; + boolean showTriangle = showDataIcon && !mCurrentState.airplaneMode; + int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0; + showDataIcon |= mCurrentState.roaming; IconState statusIcon = new IconState(showDataIcon && !mCurrentState.airplaneMode, getCurrentIconId(), contentDescription); - int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0; callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml, description, icons.isWide, mSubscriptionInfo.getSubscriptionId(), - mCurrentState.roaming); + mCurrentState.roaming, showTriangle); } else { boolean showDataIcon = mCurrentState.dataConnected || dataDisabled; IconState statusIcon = new IconState( @@ -316,10 +318,11 @@ public class MobileSignalController extends SignalController<MobileState, Mobile && mCurrentState.activityOut; showDataIcon &= mCurrentState.isDefault || dataDisabled; int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0; + boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode; callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml, description, icons.isWide, mSubscriptionInfo.getSubscriptionId(), - mCurrentState.roaming); + mCurrentState.roaming, showTriangle); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index f2b0d762b6fc..e60d5c5f2fa8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -66,12 +66,13 @@ public interface NetworkController extends CallbackController<SignalCallback>, D * @param isWide //TODO: unused? * @param subId subscription ID for which to update the UI * @param roaming indicates roaming + * @param showTriangle whether to show the mobile triangle the in status bar */ default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, int qsType, boolean activityIn, boolean activityOut, CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming) { + boolean isWide, int subId, boolean roaming, boolean showTriangle) { } default void setSubs(List<SubscriptionInfo> subs) {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 6c5251b02291..9380d9110c35 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.Nullable; @@ -50,6 +52,8 @@ import android.view.OnReceiveContentListener; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; +import android.view.WindowInsets; +import android.view.WindowInsetsAnimation; import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.EditorInfo; @@ -61,6 +65,8 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.NonNull; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.statusbar.IStatusBarService; @@ -76,6 +82,7 @@ import com.android.systemui.statusbar.phone.LightBarController; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.function.Consumer; /** @@ -135,6 +142,27 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText = (RemoteEditText) getChildAt(0); mEditText.setInnerFocusable(false); + mEditText.setWindowInsetsAnimationCallback( + new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { + @NonNull + @Override + public WindowInsets onProgress(@NonNull WindowInsets insets, + @NonNull List<WindowInsetsAnimation> runningAnimations) { + return insets; + } + + @Override + public void onEnd(@NonNull WindowInsetsAnimation animation) { + super.onEnd(animation); + if (animation.getTypeMask() == WindowInsets.Type.ime()) { + mEntry.mRemoteEditImeVisible = + mEditText.getRootWindowInsets().isVisible(WindowInsets.Type.ime()); + if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) { + mController.removeRemoteInput(mEntry, mToken); + } + } + } + }); } protected Intent prepareRemoteInputFromText() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index 7042e2f9e102..9669522675ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -150,7 +150,7 @@ public class WifiSignalController extends callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription, dataContentDescriptionHtml, description, icons.isWide, - mCurrentState.subId, /* roaming= */ false); + mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true); } private int getCurrentIconIdForCarrierWifi() { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 81ac21c00823..71835b6c3291 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -252,6 +252,7 @@ public final class WMShell extends SystemUI public void onStop() { mSysUiMainExecutor.execute(() -> { if (oneHanded.isOneHandedEnabled()) { + // Log metrics for 3-button navigation mode. oneHanded.stopOneHanded( OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT); } else if (oneHanded.isSwipeToNotificationEnabled()) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java index 8a412bff03ac..b452d3a79814 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java @@ -218,7 +218,7 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { mSignalCallback.setMobileDataIndicators( mock(NetworkController.IconState.class), mock(NetworkController.IconState.class), - 0, 0, true, true, "", "", "", true, 0, true); + 0, 0, true, true, "", "", "", true, 0, true, true); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java new file mode 100644 index 000000000000..b37ac4a2b759 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSTileHost; +import com.android.systemui.qs.logging.QSLogger; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +public class NfcTileTest extends SysuiTestCase { + + private static final String TILES_STOCK_WITHOUT_NFC = "wifi,cell,battery,dnd,flashlight,bt"; + private static final String TILES_STOCK_WITH_NFC = "wifi,cell,battery,dnd,nfc,flashlight,bt"; + + @Mock + private Context mMockContext; + @Mock + private PackageManager mPackageManager; + @Mock + private ActivityStarter mActivityStarter; + @Mock + private QSTileHost mHost; + @Mock + private MetricsLogger mMetricsLogger; + @Mock + private StatusBarStateController mStatusBarStateController; + @Mock + private QSLogger mQSLogger; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + + private TestableLooper mTestableLooper; + private NfcTile mNfcTile; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mTestableLooper = TestableLooper.get(this); + + when(mHost.getContext()).thenReturn(mMockContext); + when(mMockContext.getPackageManager()).thenReturn(mPackageManager); + + mNfcTile = new NfcTile( + mHost, + mTestableLooper.getLooper(), + new Handler(mTestableLooper.getLooper()), + mMetricsLogger, + mStatusBarStateController, + mActivityStarter, + mQSLogger, + mBroadcastDispatcher + ); + } + + @Test + public void testIsAvailable_stockWithoutNfc_returnsFalse() { + when(mMockContext.getString(R.string.quick_settings_tiles_stock)).thenReturn( + TILES_STOCK_WITHOUT_NFC); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)).thenReturn(true); + assertFalse(mNfcTile.isAvailable()); + } + + @Test + public void testIsAvailable_stockWithNfc_returnsTrue() { + when(mMockContext.getString(R.string.quick_settings_tiles_stock)).thenReturn( + TILES_STOCK_WITH_NFC); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)).thenReturn(true); + assertTrue(mNfcTile.isAvailable()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java index ebc45f41ce61..c212cf3ee769 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java @@ -125,7 +125,7 @@ public class CallbackHandlerTest extends SysuiTestCase { int subId = 5; boolean roaming = true; mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription, - typeDescriptionHtml, description, wide, subId, roaming); + typeDescriptionHtml, description, wide, subId, roaming, true); waitForCallbacks(); ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class); @@ -143,7 +143,7 @@ public class CallbackHandlerTest extends SysuiTestCase { Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(), qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(), outArg.capture(), typeContentArg.capture(), typeContentHtmlArg.capture(), - descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming)); + descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming), eq(true)); assertEquals(status, statusArg.getValue()); assertEquals(qs, qsArg.getValue()); assertEquals(type, (int) typeIconArg.getValue()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index deabcbe520b6..da1f5d392872 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -489,7 +489,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { anyInt(), typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(), any(CharSequence.class), any(CharSequence.class), any(CharSequence.class), - anyBoolean(), anyInt(), anyBoolean()); + anyBoolean(), anyInt(), anyBoolean(), anyBoolean()); IconState iconState = iconArg.getValue(); int state = SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(), false); @@ -523,7 +523,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { typeIconArg.capture(), anyInt(), anyBoolean(), anyBoolean(), any(CharSequence.class), any(CharSequence.class), any(), - anyBoolean(), anyInt(), eq(roaming)); + anyBoolean(), anyInt(), eq(roaming), anyBoolean()); IconState iconState = iconArg.getValue(); int state = icon == -1 ? 0 : SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(), @@ -544,7 +544,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { typeIconArg.capture(), anyInt(), anyBoolean(), anyBoolean(), any(CharSequence.class), any(CharSequence.class), any(), - anyBoolean(), anyInt(), anyBoolean()); + anyBoolean(), anyInt(), anyBoolean(), anyBoolean()); IconState iconState = iconArg.getValue(); int state = SignalDrawable.getState( level, CellSignalStrength.getNumSignalStrengthLevels(), !inet); @@ -591,7 +591,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { dataOutArg.capture(), typeContentDescriptionArg.capture(), typeContentDescriptionHtmlArg.capture(), - any(), anyBoolean(), anyInt(), anyBoolean()); + any(), anyBoolean(), anyInt(), anyBoolean(), anyBoolean()); IconState iconState = iconArg.getValue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java index 2b82f66cb4c2..10166cb0c43f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java @@ -134,7 +134,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { // Still be on wifi though. setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true); setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false); - verifyLastMobileDataIndicators(false, DEFAULT_LEVEL, 0, true); + verifyLastMobileDataIndicators(true, DEFAULT_LEVEL, 0, true); } @Test diff --git a/services/Android.bp b/services/Android.bp index b11a2e8bfa4f..631146b1506b 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -28,6 +28,7 @@ filegroup { ":services.profcollect-sources", ":services.restrictions-sources", ":services.searchui-sources", + ":services.speech-sources", ":services.startop.iorap-sources", ":services.systemcaptions-sources", ":services.translation-sources", @@ -35,6 +36,7 @@ filegroup { ":services.usb-sources", ":services.voiceinteraction-sources", ":services.wifi-sources", + ":service-media-s-sources", // TODO (b/177640454) ":service-permission-sources", ":service-statsd-sources", ], @@ -75,6 +77,7 @@ java_library { "services.profcollect", "services.restrictions", "services.searchui", + "services.speech", "services.startop", "services.systemcaptions", "services.translation", diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 04e08aeaf78e..bf7aea61cb06 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -17,9 +17,14 @@ package com.android.server.companion; +import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES; +import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED; +import static android.content.Context.BIND_IMPORTANT; + import static com.android.internal.util.CollectionUtils.emptyIfNull; import static com.android.internal.util.CollectionUtils.find; import static com.android.internal.util.CollectionUtils.forEach; +import static com.android.internal.util.CollectionUtils.map; import static com.android.internal.util.FunctionalUtils.uncheckExceptions; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; @@ -40,12 +45,18 @@ import android.app.PendingIntent; import android.app.role.RoleManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; import android.companion.Association; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; +import android.companion.CompanionDeviceService; import android.companion.DeviceNotAssociatedException; import android.companion.ICompanionDeviceDiscoveryService; import android.companion.ICompanionDeviceManager; +import android.companion.ICompanionDeviceService; import android.companion.IFindDeviceCallback; import android.content.ComponentName; import android.content.Context; @@ -77,6 +88,7 @@ import android.permission.PermissionControllerManager; import android.provider.Settings; import android.provider.SettingsStringUtil.ComponentNameSet; import android.text.BidiFormatter; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.ExceptionUtils; @@ -115,6 +127,7 @@ import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Objects; import java.util.Set; @@ -135,6 +148,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, ".DeviceDiscoveryService"); + private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000; + private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000; + private static final boolean DEBUG = false; private static final String LOG_TAG = "CompanionDeviceManagerService"; @@ -146,18 +162,23 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private static final String XML_ATTR_PACKAGE = "package"; private static final String XML_ATTR_DEVICE = "device"; private static final String XML_ATTR_PROFILE = "profile"; - private static final String XML_ATTR_PERSISTENT_PROFILE_GRANTS = "persistent_profile_grants"; + private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby"; private static final String XML_FILE_NAME = "companion_device_manager_associations.xml"; private final CompanionDeviceManagerImpl mImpl; private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>(); private PowerWhitelistManager mPowerWhitelistManager; private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors; + /** userId -> packageName -> serviceConnector */ + private PerUser<ArrayMap<String, ServiceConnector<ICompanionDeviceService>>> + mDeviceListenerServiceConnectors; private IAppOpsService mAppOpsManager; private RoleManager mRoleManager; private BluetoothAdapter mBluetoothAdapter; + private UserManager mUserManager; private IFindDeviceCallback mFindDeviceCallback; + private ScanCallback mBleScanCallback = new BleScanCallback(); private AssociationRequest mRequest; private String mCallingPackage; private AndroidFuture<Association> mOngoingDeviceDiscovery; @@ -166,8 +187,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener = new BluetoothDeviceConnectedListener(); private List<String> mCurrentlyConnectedDevices = new ArrayList<>(); + private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>(); + private UnbindDeviceListenersRunnable + mUnbindDeviceListenersRunnable = new UnbindDeviceListenersRunnable(); + private ArrayMap<String, TriggerDeviceDisappearedRunnable> mTriggerDeviceDisappearedRunnables = + new ArrayMap<>(); private final Object mLock = new Object(); + private final Handler mMainHandler = Handler.getMain(); /** userId -> [association] */ @GuardedBy("mLock") @@ -189,6 +216,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mPermissionControllerManager = requireNonNull( context.getSystemService(PermissionControllerManager.class)); + mUserManager = context.getSystemService(UserManager.class); Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO); mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() { @@ -201,6 +229,16 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } }; + mDeviceListenerServiceConnectors = new PerUser<ArrayMap<String, + ServiceConnector<ICompanionDeviceService>>>() { + @NonNull + @Override + protected ArrayMap<String, ServiceConnector<ICompanionDeviceService>> create( + int userId) { + return new ArrayMap<>(); + } + }; + registerPackageMonitor(); } @@ -208,10 +246,13 @@ public class CompanionDeviceManagerService extends SystemService implements Bind new PackageMonitor() { @Override public void onPackageRemoved(String packageName, int uid) { + int userId = getChangingUserId(); updateAssociations( as -> CollectionUtils.filter(as, a -> !Objects.equals(a.getPackageName(), packageName)), - getChangingUserId()); + userId); + + unbindDevicePresenceListener(packageName, userId); } @Override @@ -225,6 +266,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true); } + private void unbindDevicePresenceListener(String packageName, int userId) { + ServiceConnector<ICompanionDeviceService> deviceListener = + mDeviceListenerServiceConnectors.forUser(userId) + .remove(packageName); + if (deviceListener != null) { + deviceListener.unbind(); + } + } + @Override public void onStart() { publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl); @@ -238,6 +288,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind mBluetoothAdapter.registerBluetoothConnectionCallback( getContext().getMainExecutor(), mBluetoothDeviceConnectedListener); + initBleScanning(); + } else { + Log.w(LOG_TAG, "No BluetoothAdapter available"); } } } @@ -287,7 +340,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind @Override public void binderDied() { - Handler.getMain().post(this::cleanup); + mMainHandler.post(this::cleanup); } private void cleanup() { @@ -399,7 +452,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind checkCallerIsSystemOr(callingPackage, userId); checkUsesFeature(callingPackage, getCallingUserId()); } - return new ArrayList<>(CollectionUtils.map( + return new ArrayList<>(map( getAllAssociations(userId, callingPackage), a -> a.getDeviceMacAddress())); } @@ -506,22 +559,18 @@ public class CompanionDeviceManagerService extends SystemService implements Bind public void registerDevicePresenceListenerService( String packageName, String deviceAddress) throws RemoteException { - checkCanRegisterObserverService(packageName, deviceAddress); - - //TODO(eugenesusla) implement + registerDevicePresenceListenerActive(packageName, deviceAddress, true); } @Override public void unregisterDevicePresenceListenerService( String packageName, String deviceAddress) throws RemoteException { - checkCanRegisterObserverService(packageName, deviceAddress); - - //TODO(eugenesusla) implement + registerDevicePresenceListenerActive(packageName, deviceAddress, false); } - private void checkCanRegisterObserverService(String packageName, String deviceAddress) - throws RemoteException { + private void registerDevicePresenceListenerActive(String packageName, String deviceAddress, + boolean active) throws RemoteException { getContext().enforceCallingOrSelfPermission( android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE, "[un]registerDevicePresenceListenerService"); @@ -537,6 +586,20 @@ public class CompanionDeviceManagerService extends SystemService implements Bind + " is not associated with device " + deviceAddress + " for user " + userId)); } + + updateAssociations(associations -> map(associations, association -> { + if (Objects.equals(association.getPackageName(), packageName) + && Objects.equals(association.getDeviceMacAddress(), deviceAddress)) { + return new Association( + association.getUserId(), + association.getDeviceMacAddress(), + association.getPackageName(), + association.getDeviceProfile(), + active /* notifyOnDeviceNearby */); + } else { + return association; + } + })); } private void checkCanCallNotificationApi(String callingPackage) throws RemoteException { @@ -693,6 +756,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) { grantDeviceProfile(association); } + + if (association.isNotifyOnDeviceNearby()) { + restartBleScan(); + } } private void exemptFromAutoRevoke(String packageName, int uid) { @@ -795,9 +862,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind association.getDeviceMacAddress()); if (association.getDeviceProfile() != null) { tag.attribute(null, XML_ATTR_PROFILE, association.getDeviceProfile()); - tag.attribute(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS, + tag.attribute(null, XML_ATTR_NOTIFY_DEVICE_NEARBY, Boolean.toString( - association.isKeepProfilePrivilegesWhenDeviceAway())); + association.isNotifyOnDeviceNearby())); } tag.endTag(null, XML_TAG_ASSOCIATION); }); @@ -845,6 +912,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind a -> Objects.equals(packageFilter, a.getPackageName())); } + private Set<Association> getAllAssociations() { + ArraySet<Association> result = new ArraySet<>(); + for (UserInfo user : mUserManager.getAliveUsers()) { + result.addAll(getAllAssociations(user.id)); + } + return result; + } + private Set<Association> readAllAssociations(int userId) { final AtomicFile file = getStorageFileForUser(userId); @@ -865,7 +940,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE); final boolean persistentGrants = Boolean.valueOf( - parser.getAttributeValue(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS)); + parser.getAttributeValue(null, XML_ATTR_NOTIFY_DEVICE_NEARBY)); if (appPackage == null || deviceAddress == null) continue; @@ -896,6 +971,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } } + + onDeviceNearby(address); } private void grantDeviceProfile(Association association) { @@ -919,6 +996,210 @@ public class CompanionDeviceManagerService extends SystemService implements Bind void onDeviceDisconnected(String address) { mCurrentlyConnectedDevices.remove(address); + + onDeviceDisappeared(address); + } + + private ServiceConnector<ICompanionDeviceService> getDeviceListenerServiceConnector( + Association a) { + return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent( + a.getPackageName(), + pkg -> new ServiceConnector.Impl<>(getContext(), + new Intent(CompanionDeviceService.SERVICE_INTERFACE), + BIND_IMPORTANT, + a.getUserId(), + ICompanionDeviceService.Stub::asInterface)); + } + + private class BleScanCallback extends ScanCallback { + @Override + public void onScanResult(int callbackType, ScanResult result) { + if (DEBUG) { + Log.i(LOG_TAG, "onScanResult(callbackType = " + + callbackType + ", result = " + result + ")"); + } + + onDeviceNearby(result.getDevice().getAddress()); + } + + @Override + public void onBatchScanResults(List<ScanResult> results) { + for (int i = 0, size = results.size(); i < size; i++) { + onScanResult(CALLBACK_TYPE_ALL_MATCHES, results.get(i)); + } + } + + @Override + public void onScanFailed(int errorCode) { + if (errorCode == SCAN_FAILED_ALREADY_STARTED) { + // ignore - this might happen if BT tries to auto-restore scans for us in the + // future + } else { + Log.wtf(LOG_TAG, "Failed to start BLE scan: error " + errorCode); + } + } + } + + private class UnbindDeviceListenersRunnable implements Runnable { + + public String getJobId(String address) { + return "CDM_deviceGone_unbind_" + address; + } + + @Override + public void run() { + int size = mDevicesLastNearby.size(); + for (int i = 0; i < size; i++) { + String address = mDevicesLastNearby.keyAt(i); + Date lastNearby = mDevicesLastNearby.valueAt(i); + + if (System.currentTimeMillis() - lastNearby.getTime() + >= DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS) { + for (Association association : getAllAssociations(address)) { + if (association.isNotifyOnDeviceNearby()) { + getDeviceListenerServiceConnector(association).unbind(); + } + } + } + } + } + } + + private class TriggerDeviceDisappearedRunnable implements Runnable { + + private final String mAddress; + + TriggerDeviceDisappearedRunnable(String address) { + mAddress = address; + } + + public void schedule() { + mMainHandler.removeCallbacks(this); + mMainHandler.postDelayed(this, this, DEVICE_DISAPPEARED_TIMEOUT_MS); + } + + @Override + public void run() { + onDeviceDisappeared(mAddress); + } + } + + private Set<Association> getAllAssociations(String deviceAddress) { + List<UserInfo> aliveUsers = mUserManager.getAliveUsers(); + Set<Association> result = new ArraySet<>(); + for (int i = 0, size = aliveUsers.size(); i < size; i++) { + UserInfo user = aliveUsers.get(i); + for (Association association : getAllAssociations(user.id)) { + if (Objects.equals(association.getDeviceMacAddress(), deviceAddress)) { + result.add(association); + } + } + } + return result; + } + + private void onDeviceNearby(String address) { + Date timestamp = new Date(); + mDevicesLastNearby.put(address, timestamp); + + cancelUnbindDeviceListener(address); + + mTriggerDeviceDisappearedRunnables + .computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address)) + .schedule(); + + for (Association association : getAllAssociations(address)) { + if (association.isNotifyOnDeviceNearby()) { + if (DEBUG) { + Log.i(LOG_TAG, "Device " + address + + " managed by " + association.getPackageName() + + " is nearby on " + timestamp); + } + getDeviceListenerServiceConnector(association).run( + service -> service.onDeviceAppeared(association.getDeviceMacAddress())); + } + } + } + + private void onDeviceDisappeared(String address) { + boolean hasDeviceListeners = false; + for (Association association : getAllAssociations(address)) { + if (association.isNotifyOnDeviceNearby()) { + if (DEBUG) { + Log.i(LOG_TAG, "Device " + address + + " managed by " + association.getPackageName() + + " disappeared; last seen on " + mDevicesLastNearby.get(address)); + } + + getDeviceListenerServiceConnector(association).run( + service -> service.onDeviceDisappeared(address)); + hasDeviceListeners = true; + } + } + + cancelUnbindDeviceListener(address); + if (hasDeviceListeners) { + mMainHandler.postDelayed( + mUnbindDeviceListenersRunnable, + mUnbindDeviceListenersRunnable.getJobId(address), + DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS); + } + } + + private void cancelUnbindDeviceListener(String address) { + mMainHandler.removeCallbacks( + mUnbindDeviceListenersRunnable, mUnbindDeviceListenersRunnable.getJobId(address)); + } + + private void initBleScanning() { + boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback( + new BluetoothAdapter.ServiceLifecycleCallback() { + @Override + public void onBluetoothServiceUp() { + Log.i(LOG_TAG, "Bluetooth stack is up"); + startBleScan(); + } + + @Override + public void onBluetoothServiceDown() { + Log.w(LOG_TAG, "Bluetooth stack is down"); + } + }); + if (bluetoothReady) { + startBleScan(); + } + } + + void startBleScan() { + List<ScanFilter> filters = getBleScanFilters(); + if (filters.isEmpty()) { + return; + } + mBluetoothAdapter.getBluetoothLeScanner().startScan( + filters, + new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(), + mBleScanCallback); + } + + void restartBleScan() { + mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback); + startBleScan(); + } + + private List<ScanFilter> getBleScanFilters() { + ArrayList<ScanFilter> result = new ArrayList<>(); + ArraySet<String> addressesSeen = new ArraySet<>(); + for (Association association : getAllAssociations()) { + String address = association.getDeviceMacAddress(); + if (addressesSeen.contains(address)) { + continue; + } + if (association.isNotifyOnDeviceNearby()) { + result.add(new ScanFilter.Builder().setDeviceAddress(address).build()); + addressesSeen.add(address); + } + } + return result; } private AndroidFuture<String> getDeviceProfilePermissionDescription(String deviceProfile) { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 50e55579dd74..c2872405ae18 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -254,6 +254,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger( com.android.internal.R.integer.config_radioScanningTimeout) * 1000L); mStats.setPowerProfileLocked(new PowerProfile(context)); + mStats.startTrackingSystemServerCpuTime(); + mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats); } diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index 6905b3da9bc4..6851d7148191 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -90,6 +90,7 @@ class PreAuthInfo { int userId, PromptInfo promptInfo, String opPackageName, boolean checkDevicePolicyManager) throws RemoteException { + final boolean confirmationRequested = promptInfo.isConfirmationRequested(); final boolean biometricRequested = Utils.isBiometricRequested(promptInfo); final int requestedStrength = Utils.getPublicBiometricStrength(promptInfo); @@ -111,7 +112,7 @@ class PreAuthInfo { @AuthenticatorStatus int status = getStatusForBiometricAuthenticator( devicePolicyManager, settingObserver, sensor, userId, opPackageName, - checkDevicePolicyManager, requestedStrength); + checkDevicePolicyManager, requestedStrength, promptInfo.getSensorId()); Slog.d(TAG, "Package: " + opPackageName + " Sensor ID: " + sensor.id @@ -141,7 +142,11 @@ class PreAuthInfo { DevicePolicyManager devicePolicyManager, BiometricService.SettingObserver settingObserver, BiometricSensor sensor, int userId, String opPackageName, - boolean checkDevicePolicyManager, int requestedStrength) { + boolean checkDevicePolicyManager, int requestedStrength, int requestedSensorId) { + + if (requestedSensorId != BiometricManager.SENSOR_ID_ANY && sensor.id != requestedSensorId) { + return BIOMETRIC_NO_HARDWARE; + } final boolean wasStrongEnough = Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength); diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index d87af4280ca3..5cd0bbfa4500 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -399,10 +399,15 @@ public class Utils { } } - public static boolean isKeyguard(Context context, String clientPackage) { - final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) - == PackageManager.PERMISSION_GRANTED; - + /** + * Checks if a client package matches Keyguard and can perform internal biometric operations. + * + * @param context The system context. + * @param clientPackage The name of the package to be checked against Keyguard. + * @return Whether the given package matches Keyguard. + */ + public static boolean isKeyguard(@NonNull Context context, @Nullable String clientPackage) { + final boolean hasPermission = hasInternalPermission(context); final ComponentName keyguardComponent = ComponentName.unflattenFromString( context.getResources().getString(R.string.config_keyguardComponent)); final String keyguardPackage = keyguardComponent != null @@ -410,6 +415,34 @@ public class Utils { return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage); } + /** + * Checks if a client package matches the Android system and can perform internal biometric + * operations. + * + * @param context The system context. + * @param clientPackage The name of the package to be checked against the Android system. + * @return Whether the given package matches the Android system. + */ + public static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) { + return hasInternalPermission(context) && "android".equals(clientPackage); + } + + /** + * Checks if a client package matches Settings and can perform internal biometric operations. + * + * @param context The system context. + * @param clientPackage The name of the package to be checked against Settings. + * @return Whether the given package matches Settings. + */ + public static boolean isSettings(@NonNull Context context, @Nullable String clientPackage) { + return hasInternalPermission(context) && "com.android.settings".equals(clientPackage); + } + + private static boolean hasInternalPermission(@NonNull Context context) { + return context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) + == PackageManager.PERMISSION_GRANTED; + } + public static String getClientName(@Nullable BaseClientMonitor client) { return client != null ? client.getClass().getSimpleName() : "null"; } diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index 14433fb0ea9a..0536e78e58f6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -149,9 +149,10 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> pm.incrementAuthForUser(getTargetUserId(), authenticated); } - // Ensure authentication only succeeds if the client activity is on top or is keyguard. + // Ensure authentication only succeeds if the client activity is on top. boolean isBackgroundAuth = false; - if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) { + if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString()) + && !Utils.isSystem(getContext(), getOwnerString())) { final List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(1); if (tasks == null || tasks.isEmpty()) { @@ -166,7 +167,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> final String topPackage = topActivity.getPackageName(); if (!topPackage.contentEquals(getOwnerString())) { Slog.e(TAG, "Background authentication detected, top: " + topPackage - + ", client: " + this); + + ", client: " + getOwnerString()); isBackgroundAuth = true; } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 0265cb93ac8b..686f9f525426 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -25,6 +25,10 @@ import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR; +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; import android.annotation.NonNull; import android.annotation.Nullable; @@ -32,6 +36,7 @@ import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricService; @@ -49,6 +54,7 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Binder; import android.os.Build; +import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; import android.os.Process; @@ -80,6 +86,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.Executor; /** * A service to manage multiple clients that want to access the fingerprint HAL API. @@ -219,8 +226,8 @@ public class FingerprintService extends SystemService implements BiometricServic @SuppressWarnings("deprecation") @Override // Binder call public void authenticate(final IBinder token, final long operationId, - @FingerprintManager.SensorId final int sensorId, final int userId, - final IFingerprintServiceReceiver receiver, final String opPackageName) { + final int sensorId, final int userId, final IFingerprintServiceReceiver receiver, + final String opPackageName) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); @@ -236,7 +243,7 @@ public class FingerprintService extends SystemService implements BiometricServic final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName); // Clear calling identity when checking LockPatternUtils for StrongAuth flags. - final long identity = Binder.clearCallingIdentity(); + long identity = Binder.clearCallingIdentity(); try { if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) { // If this happens, something in KeyguardUpdateMonitor is wrong. @@ -266,9 +273,101 @@ public class FingerprintService extends SystemService implements BiometricServic return; } - provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, - 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName, - restricted, statsClient, isKeyguard); + final FingerprintSensorPropertiesInternal sensorProps = + provider.second.getSensorProperties(sensorId); + if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName) + && sensorProps != null && sensorProps.isAnyUdfpsType()) { + identity = Binder.clearCallingIdentity(); + try { + authenticateWithPrompt(operationId, sensorProps, userId, receiver); + } finally { + Binder.restoreCallingIdentity(identity); + } + } else { + provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, + 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName, + restricted, statsClient, isKeyguard); + } + } + + private void authenticateWithPrompt( + final long operationId, + @NonNull final FingerprintSensorPropertiesInternal props, + final int userId, + final IFingerprintServiceReceiver receiver) { + + final Context context = getUiContext(); + final Executor executor = context.getMainExecutor(); + + final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context) + .setTitle(context.getString(R.string.biometric_dialog_default_title)) + .setSubtitle(context.getString(R.string.fingerprint_dialog_default_subtitle)) + .setNegativeButton( + context.getString(R.string.cancel), + executor, + (dialog, which) -> { + try { + receiver.onError( + FINGERPRINT_ERROR_USER_CANCELED, 0 /* vendorCode */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in negative button onClick()", e); + } + }) + .setSensorId(props.sensorId) + .build(); + + final BiometricPrompt.AuthenticationCallback promptCallback = + new BiometricPrompt.AuthenticationCallback() { + @Override + public void onAuthenticationError(int errorCode, CharSequence errString) { + try { + if (FingerprintUtils.isKnownErrorCode(errorCode)) { + receiver.onError(errorCode, 0 /* vendorCode */); + } else { + receiver.onError(FINGERPRINT_ERROR_VENDOR, errorCode); + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationError()", e); + } + } + + @Override + public void onAuthenticationSucceeded( + BiometricPrompt.AuthenticationResult result) { + final Fingerprint fingerprint = new Fingerprint("", 0, 0L); + final boolean isStrong = props.sensorStrength == STRENGTH_STRONG; + try { + receiver.onAuthenticationSucceeded(fingerprint, userId, isStrong); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationSucceeded()", e); + } + } + + @Override + public void onAuthenticationFailed() { + try { + receiver.onAuthenticationFailed(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationFailed()", e); + } + } + + @Override + public void onAuthenticationAcquired(int acquireInfo) { + try { + if (FingerprintUtils.isKnownAcquiredCode(acquireInfo)) { + receiver.onAcquired(acquireInfo, 0 /* vendorCode */); + } else { + receiver.onAcquired(FINGERPRINT_ACQUIRED_VENDOR, acquireInfo); + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationAcquired()", e); + } + } + }; + + biometricPrompt.authenticateUserForOperation( + new CancellationSignal(), executor, promptCallback, userId, operationId); } @Override @@ -374,6 +473,7 @@ public class FingerprintService extends SystemService implements BiometricServic @Override // Binder call public void cancelAuthenticationFromService(final int sensorId, final IBinder token, final String opPackageName) { + Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); final ServiceProvider provider = getProviderForSensor(sensorId); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java index dc6fd3a1b26d..d69151da55f6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java @@ -16,8 +16,18 @@ package com.android.server.biometrics.sensors.fingerprint; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_IMAGER_DIRTY; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_INSUFFICIENT; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_PARTIAL; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_FAST; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_SLOW; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; + import android.annotation.Nullable; import android.content.Context; +import android.hardware.biometrics.fingerprint.V2_1.FingerprintError; import android.hardware.fingerprint.Fingerprint; import android.text.TextUtils; import android.util.SparseArray; @@ -138,5 +148,51 @@ public class FingerprintUtils implements BiometricUtils<Fingerprint> { return state; } } + + /** + * Checks if the given error code corresponds to a known fingerprint error. + * + * @param errorCode The error code to be checked. + * @return Whether the error code corresponds to a known error. + */ + public static boolean isKnownErrorCode(int errorCode) { + switch (errorCode) { + case FingerprintError.ERROR_HW_UNAVAILABLE: + case FingerprintError.ERROR_UNABLE_TO_PROCESS: + case FingerprintError.ERROR_TIMEOUT: + case FingerprintError.ERROR_NO_SPACE: + case FingerprintError.ERROR_CANCELED: + case FingerprintError.ERROR_UNABLE_TO_REMOVE: + case FingerprintError.ERROR_LOCKOUT: + case FingerprintError.ERROR_VENDOR: + return true; + + default: + return false; + } + } + + /** + * Checks if the given acquired code corresponds to a known fingerprint error. + * + * @param acquiredCode The acquired code to be checked. + * @return Whether the acquired code corresponds to a known error. + */ + public static boolean isKnownAcquiredCode(int acquiredCode) { + switch (acquiredCode) { + case FINGERPRINT_ACQUIRED_GOOD: + case FINGERPRINT_ACQUIRED_PARTIAL: + case FINGERPRINT_ACQUIRED_INSUFFICIENT: + case FINGERPRINT_ACQUIRED_IMAGER_DIRTY: + case FINGERPRINT_ACQUIRED_TOO_SLOW: + case FINGERPRINT_ACQUIRED_TOO_FAST: + case FINGERPRINT_ACQUIRED_VENDOR: + case FINGERPRINT_ACQUIRED_START: + return true; + + default: + return false; + } + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index f8450245a18d..fec2c4670439 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -96,7 +96,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi Slog.e(getTag(), "Task stack changed for client: " + client); continue; } - if (Utils.isKeyguard(mContext, client.getOwnerString())) { + if (Utils.isKeyguard(mContext, client.getOwnerString()) + || Utils.isSystem(mContext, client.getOwnerString())) { continue; // Keyguard is always allowed } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index a4a84019bae7..6eb68ca2c9f6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -123,7 +123,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider Slog.e(TAG, "Task stack changed for client: " + client); return; } - if (Utils.isKeyguard(mContext, client.getOwnerString())) { + if (Utils.isKeyguard(mContext, client.getOwnerString()) + || Utils.isSystem(mContext, client.getOwnerString())) { return; // Keyguard is always allowed } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 23c70ee69514..2e4200c1f7d9 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -289,6 +289,8 @@ public class InputManagerService extends IInputManager.Stub private static native void nativeCancelVibrate(long ptr, int deviceId, int token); private static native boolean nativeIsVibrating(long ptr, int deviceId); private static native int[] nativeGetVibratorIds(long ptr, int deviceId); + private static native int nativeGetBatteryCapacity(long ptr, int deviceId); + private static native int nativeGetBatteryStatus(long ptr, int deviceId); private static native void nativeReloadKeyboardLayouts(long ptr); private static native void nativeReloadDeviceAliases(long ptr); private static native String nativeDump(long ptr); @@ -1818,8 +1820,7 @@ public class InputManagerService extends IInputManager.Stub } private void updateMaximumObscuringOpacityForTouchFromSettings() { - final float opacity = InputManager.getInstance().getMaximumObscuringOpacityForTouch( - mContext); + final float opacity = InputManager.getInstance().getMaximumObscuringOpacityForTouch(); if (opacity < 0 || opacity > 1) { Log.e(TAG, "Invalid maximum obscuring opacity " + opacity + ", it should be >= 0 and <= 1, rejecting update."); @@ -2009,6 +2010,18 @@ public class InputManagerService extends IInputManager.Stub // Binder call @Override + public int getBatteryStatus(int deviceId) { + return nativeGetBatteryStatus(mPtr, deviceId); + } + + // Binder call + @Override + public int getBatteryCapacity(int deviceId) { + return nativeGetBatteryCapacity(mPtr, deviceId); + } + + // Binder call + @Override public void setPointerIconType(int iconId) { nativeSetPointerIconType(mPtr, iconId); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e32c00fe9a39..8fa3a00f3294 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -9302,21 +9302,30 @@ public class NotificationManagerService extends SystemService { Slog.v(TAG, "onNotificationEnqueuedLocked() called with: r = [" + r + "]"); } final StatusBarNotification sbn = r.getSbn(); - notifyAssistantLocked( - sbn, - r.getNotificationType(), - true /* sameUserOnly */, - (assistant, sbnHolder) -> { - try { - if (debug) { - Slog.v(TAG, - "calling onNotificationEnqueuedWithChannel " + sbnHolder); - } - assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel()); - } catch (RemoteException ex) { - Slog.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex); + + for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { + boolean sbnVisible = isVisibleToListener( + sbn, r.getNotificationType(), info) + && info.isSameUser(r.getUserId()); + if (sbnVisible) { + TrimCache trimCache = new TrimCache(sbn); + final INotificationListener assistant = (INotificationListener) info.service; + final StatusBarNotification sbnToPost = trimCache.ForListener(info); + final StatusBarNotificationHolder sbnHolder = + new StatusBarNotificationHolder(sbnToPost); + try { + if (debug) { + Slog.v(TAG, + "calling onNotificationEnqueuedWithChannel " + sbnHolder); } - }); + final NotificationRankingUpdate update = makeRankingUpdateLocked(info); + assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel(), + update); + } catch (RemoteException ex) { + Slog.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex); + } + } + } } @GuardedBy("mNotificationLock") diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 50aacd677fdc..f0659c507e0e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -28203,6 +28203,13 @@ public class PackageManagerService extends IPackageManager.Stub } continue; } + if (ps.appId < Process.FIRST_APPLICATION_UID) { + if (DEBUG_PER_UID_READ_TIMEOUTS) { + Slog.i(TAG, "PerUidReadTimeouts: package is system, appId=" + ps.appId); + } + continue; + } + final AndroidPackage pkg = ps.getPkg(); if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 2929568ab1d7..04f399ce42c9 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3165,6 +3165,17 @@ public final class Settings implements Watchable, Snappable { mReadMessages.append("Error reading: " + e.toString()); PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e); + } finally { + if (!mVersion.containsKey(StorageManager.UUID_PRIVATE_INTERNAL)) { + Slog.wtf(PackageManagerService.TAG, + "No internal VersionInfo found in settings, using current."); + findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent(); + } + if (!mVersion.containsKey(StorageManager.UUID_PRIMARY_PHYSICAL)) { + Slog.wtf(PackageManagerService.TAG, + "No external VersionInfo found in settings, using current."); + findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent(); + } } // If the build is setup to drop runtime permissions diff --git a/services/core/java/com/android/server/speech/Android.bp b/services/core/java/com/android/server/speech/Android.bp new file mode 100644 index 000000000000..379b0754b82a --- /dev/null +++ b/services/core/java/com/android/server/speech/Android.bp @@ -0,0 +1,13 @@ +filegroup { + name: "services.speech-sources", + srcs: ["java/**/*.java"], + path: "java", + visibility: ["//frameworks/base/services"], +} + +java_library_static { + name: "services.speech", + defaults: ["platform_service_defaults"], + srcs: [":services.speech-sources"], + libs: ["services.core"], +} diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java new file mode 100644 index 000000000000..96248c3711e3 --- /dev/null +++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.speech; + +import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.RemoteException; +import android.speech.IRecognitionListener; +import android.speech.IRecognitionService; +import android.speech.RecognitionService; +import android.util.Slog; + +import com.android.internal.infra.ServiceConnector; + +final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecognitionService> { + private static final String TAG = RemoteSpeechRecognitionService.class.getSimpleName(); + private static final boolean DEBUG = true; + + RemoteSpeechRecognitionService(Context context, ComponentName serviceName, int userId) { + super(context, + new Intent(RecognitionService.SERVICE_INTERFACE).setComponent(serviceName), + Context.BIND_AUTO_CREATE + | Context.BIND_FOREGROUND_SERVICE + | Context.BIND_INCLUDE_CAPABILITIES + | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, + userId, + IRecognitionService.Stub::asInterface); + + if (DEBUG) { + Slog.i(TAG, "Bound to recognition service at: " + serviceName.flattenToString()); + } + } + + void startListening(Intent recognizerIntent, IRecognitionListener listener, String packageName, + String featureId) throws RemoteException { + if (DEBUG) { + Slog.i(TAG, "#startListening for package: " + packageName + ", feature=" + featureId); + } + run(service -> service.startListening(recognizerIntent, listener, packageName, featureId)); + } + + void stopListening(IRecognitionListener listener, String packageName, String featureId) + throws RemoteException { + if (DEBUG) { + Slog.i(TAG, "#stopListening for package: " + packageName + ", feature=" + featureId); + } + run(service -> service.stopListening(listener, packageName, featureId)); + } + + void cancel(IRecognitionListener listener, String packageName, String featureId) + throws RemoteException { + if (DEBUG) { + Slog.i(TAG, "#cancel for package: " + packageName + ", feature=" + featureId); + } + run(service -> service.cancel(listener, packageName, featureId)); + } + + @Override // from ServiceConnector.Impl + protected void onServiceConnectionStatusChanged( + IRecognitionService service, boolean connected) { + if (!DEBUG) { + return; + } + + if (connected) { + Slog.i(TAG, "Connected to ASR service"); + } else { + Slog.w(TAG, "Disconnected from ASR service"); + } + } + + @Override // from AbstractRemoteService + protected long getAutoDisconnectTimeoutMs() { + return PERMANENT_BOUND_TIMEOUT_MS; + } +} diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java new file mode 100644 index 000000000000..592ba9e616b8 --- /dev/null +++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.speech; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.content.Context; +import android.os.UserHandle; +import android.speech.IRecognitionServiceManager; +import android.speech.IRecognitionServiceManagerCallback; + +import com.android.internal.R; +import com.android.server.infra.AbstractMasterSystemService; +import com.android.server.infra.FrameworkResourcesServiceNameResolver; + +/** + * System service implementation for Speech Recognizer. + * + * <p>This service uses RemoteSpeechRecognitionService to bind to a default implementation of + * ISpeechRecognition. It relays all the requests from the client to the default system impl of + * ISpeechRecognition service (denoted by {@code + * R.string.config_defaultOnDeviceSpeechRecognitionService}). + */ +public final class SpeechRecognitionManagerService extends + AbstractMasterSystemService<SpeechRecognitionManagerService, + SpeechRecognitionManagerServiceImpl> { + private static final String TAG = SpeechRecognitionManagerService.class.getSimpleName(); + + public SpeechRecognitionManagerService(@NonNull Context context) { + super(context, + new FrameworkResourcesServiceNameResolver( + context, + R.string.config_defaultOnDeviceSpeechRecognitionService), + /*disallowProperty=*/ null); + } + + @Override // from SystemService + public void onStart() { + SpeechRecognitionManagerServiceStub serviceStub = new SpeechRecognitionManagerServiceStub(); + publishBinderService(Context.SPEECH_RECOGNITION_SERVICE, serviceStub); + } + + @Override + protected SpeechRecognitionManagerServiceImpl newServiceLocked( + @UserIdInt int resolvedUserId, boolean disabled) { + return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId, disabled); + } + + final class SpeechRecognitionManagerServiceStub extends IRecognitionServiceManager.Stub { + + @Override + public void createSession(IRecognitionServiceManagerCallback callback) { + int userId = UserHandle.getCallingUserId(); + synchronized (mLock) { + SpeechRecognitionManagerServiceImpl service = getServiceForUserLocked(userId); + service.createSessionLocked(callback); + } + } + } +} diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java new file mode 100644 index 000000000000..bcaf174b1d92 --- /dev/null +++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.speech; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.RemoteException; +import android.speech.IRecognitionListener; +import android.speech.IRecognitionService; +import android.speech.IRecognitionServiceManagerCallback; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.infra.AbstractPerUserSystemService; + +final class SpeechRecognitionManagerServiceImpl extends + AbstractPerUserSystemService<SpeechRecognitionManagerServiceImpl, + SpeechRecognitionManagerService> { + + private static final String TAG = SpeechRecognitionManagerServiceImpl.class.getSimpleName(); + + @GuardedBy("mLock") + @Nullable + private RemoteSpeechRecognitionService mRemoteService; + + SpeechRecognitionManagerServiceImpl( + @NonNull SpeechRecognitionManagerService master, + @NonNull Object lock, @UserIdInt int userId, boolean disabled) { + super(master, lock, userId); + updateRemoteServiceLocked(); + } + + @GuardedBy("mLock") + @Override // from PerUserSystemService + protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) + throws PackageManager.NameNotFoundException { + try { + return AppGlobals.getPackageManager().getServiceInfo(serviceComponent, + PackageManager.GET_META_DATA, mUserId); + } catch (RemoteException e) { + throw new PackageManager.NameNotFoundException( + "Could not get service for " + serviceComponent); + } + } + + @GuardedBy("mLock") + @Override // from PerUserSystemService + protected boolean updateLocked(boolean disabled) { + final boolean enabledChanged = super.updateLocked(disabled); + updateRemoteServiceLocked(); + return enabledChanged; + } + + /** + * Updates the reference to the remote service. + */ + @GuardedBy("mLock") + private void updateRemoteServiceLocked() { + if (mRemoteService != null) { + if (mMaster.debug) { + Slog.d(TAG, "updateRemoteService(): destroying old remote service"); + } + mRemoteService.unbind(); + mRemoteService = null; + } + } + + void createSessionLocked(IRecognitionServiceManagerCallback callback) { + // TODO(b/176578753): check clients have record audio permission. + // TODO(b/176578753): verify caller package is the one supplied + + RemoteSpeechRecognitionService service = ensureRemoteServiceLocked(); + + if (service == null) { + tryRespondWithError(callback); + return; + } + + service.connect().thenAccept(binderService -> { + if (binderService != null) { + try { + callback.onSuccess(new IRecognitionService.Stub() { + @Override + public void startListening(Intent recognizerIntent, + IRecognitionListener listener, + String packageName, String featureId) throws RemoteException { + service.startListening( + recognizerIntent, listener, packageName, featureId); + } + + @Override + public void stopListening(IRecognitionListener listener, + String packageName, + String featureId) throws RemoteException { + service.stopListening(listener, packageName, featureId); + } + + @Override + public void cancel(IRecognitionListener listener, + String packageName, + String featureId) throws RemoteException { + service.cancel(listener, packageName, featureId); + } + }); + } catch (RemoteException e) { + Slog.e(TAG, "Error creating a speech recognition session", e); + tryRespondWithError(callback); + } + } else { + tryRespondWithError(callback); + } + }); + } + + @GuardedBy("mLock") + @Nullable + private RemoteSpeechRecognitionService ensureRemoteServiceLocked() { + if (mRemoteService == null) { + final String serviceName = getComponentNameLocked(); + if (serviceName == null) { + if (mMaster.verbose) { + Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name."); + } + return null; + } + final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName); + mRemoteService = + new RemoteSpeechRecognitionService(getContext(), serviceComponent, mUserId); + } + return mRemoteService; + } + + private static void tryRespondWithError(IRecognitionServiceManagerCallback callback) { + try { + callback.onError(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to respond with error"); + } + } +} diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java index 5723e1dcceb5..ee30fa2ac928 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java @@ -50,8 +50,6 @@ public final class ClientProfile { */ private final int mProcessId; - private boolean mIsForeground; - /** * All the clients that share the same resource would be under the same group id. * @@ -90,6 +88,12 @@ public final class ClientProfile { private int mUsingCiCamId = INVALID_RESOURCE_ID; /** + * If the priority is overwritten through + * {@link TunerResourceManagerService#setPriority(int, int)}. + */ + private boolean mIsPriorityOverwritten = false; + + /** * Optional arbitrary priority value given by the client. * * <p>This value can override the default priorotiy calculated from @@ -121,17 +125,10 @@ public final class ClientProfile { } /** - * Set the current isForeground status. - */ - public void setForeground(boolean isForeground) { - mIsForeground = isForeground; - } - - /** - * Get the previous recorded isForeground status. + * If the client priority is overwrttien. */ - public boolean isForeground() { - return mIsForeground; + public boolean isPriorityOverwritten() { + return mIsPriorityOverwritten; } public int getGroupId() { @@ -153,6 +150,17 @@ public final class ClientProfile { mPriority = priority; } + /** + * Overwrite the client priority. + */ + public void overwritePriority(int priority) { + if (priority < 0) { + return; + } + mIsPriorityOverwritten = true; + mPriority = priority; + } + public void setNiceValue(int niceValue) { mNiceValue = niceValue; } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 988582da53ea..0c04b075485a 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -507,9 +507,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde .useCase(profile.useCase) .processId(pid) .build(); - clientProfile.setForeground(checkIsForeground(pid)); clientProfile.setPriority( - getClientPriority(profile.useCase, clientProfile.isForeground())); + getClientPriority(profile.useCase, checkIsForeground(pid))); addClientProfile(clientId[0], clientProfile, listener); } @@ -547,8 +546,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde return false; } - profile.setForeground(checkIsForeground(profile.getProcessId())); - profile.setPriority(priority); + profile.overwritePriority(priority); profile.setNiceValue(niceValue); return true; @@ -694,7 +692,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) { // Record the frontend id with the lowest client priority among all the // in use frontends when no available frontend has been found. - int priority = getOwnerClientPriority(fr.getOwnerClientId()); + int priority = updateAndGetOwnerClientPriority(fr.getOwnerClientId()); if (currentLowestPriority > priority) { inUseLowestPriorityFrHandle = fr.getHandle(); currentLowestPriority = priority; @@ -760,7 +758,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } else { // Record the lnb id with the lowest client priority among all the // in use lnb when no available lnb has been found. - int priority = getOwnerClientPriority(lnb.getOwnerClientId()); + int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId()); if (currentLowestPriority > priority) { inUseLowestPriorityLnbHandle = lnb.getHandle(); currentLowestPriority = priority; @@ -818,7 +816,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } for (int ownerId : cas.getOwnerClientIds()) { // Record the client id with lowest priority that is using the current Cas system. - int priority = getOwnerClientPriority(ownerId); + int priority = updateAndGetOwnerClientPriority(ownerId); if (currentLowestPriority > priority) { lowestPriorityOwnerId = ownerId; currentLowestPriority = priority; @@ -867,7 +865,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } for (int ownerId : ciCam.getOwnerClientIds()) { // Record the client id with lowest priority that is using the current Cas system. - int priority = getOwnerClientPriority(ownerId); + int priority = updateAndGetOwnerClientPriority(ownerId); if (currentLowestPriority > priority) { lowestPriorityOwnerId = ownerId; currentLowestPriority = priority; @@ -966,18 +964,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting - // This mothod is to sync up the request client's foreground/background status and update - // the client priority accordingly whenever new resource request comes in. - protected void clientPriorityUpdateOnRequest(ClientProfile requestProfile) { - int pid = requestProfile.getProcessId(); - boolean currentIsForeground = checkIsForeground(pid); - if (requestProfile.isForeground() == currentIsForeground) { + // This mothod is to sync up the request/holder client's foreground/background status and update + // the client priority accordingly whenever a new resource request comes in. + protected void clientPriorityUpdateOnRequest(ClientProfile profile) { + if (profile.isPriorityOverwritten()) { // To avoid overriding the priority set through updateClientPriority API. return; } - requestProfile.setForeground(currentIsForeground); - requestProfile.setPriority( - getClientPriority(requestProfile.getUseCase(), currentIsForeground)); + int pid = profile.getProcessId(); + boolean currentIsForeground = checkIsForeground(pid); + profile.setPriority( + getClientPriority(profile.getUseCase(), currentIsForeground)); } @VisibleForTesting @@ -1154,13 +1151,15 @@ public class TunerResourceManagerService extends SystemService implements IBinde } /** - * Get the owner client's priority. + * Update and get the owner client's priority. * * @param clientId the owner client id. * @return the priority of the owner client. */ - private int getOwnerClientPriority(int clientId) { - return getClientProfile(clientId).getPriority(); + private int updateAndGetOwnerClientPriority(int clientId) { + ClientProfile profile = getClientProfile(clientId); + clientPriorityUpdateOnRequest(profile); + return profile.getPriority(); } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 106db0b15c46..058324c4aa01 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -180,6 +180,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } else if (launchMode == WINDOWING_MODE_FULLSCREEN) { if (DEBUG) appendLog("activity-options-fullscreen=" + outParams.mBounds); } else if (layout != null && canApplyFreeformPolicy) { + mTmpBounds.set(currentParams.mBounds); getLayoutBounds(display, root, layout, mTmpBounds); if (!mTmpBounds.isEmpty()) { launchMode = WINDOWING_MODE_FREEFORM; @@ -492,11 +493,11 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } private void getLayoutBounds(@NonNull DisplayContent display, @NonNull ActivityRecord root, - @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect outBounds) { + @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect inOutBounds) { final int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK; final int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; if (!windowLayout.hasSpecifiedSize() && verticalGravity == 0 && horizontalGravity == 0) { - outBounds.setEmpty(); + inOutBounds.setEmpty(); return; } @@ -510,11 +511,17 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { int width; int height; if (!windowLayout.hasSpecifiedSize()) { - outBounds.setEmpty(); - getTaskBounds(root, display, windowLayout, WINDOWING_MODE_FREEFORM, - /* hasInitialBounds */ false, outBounds); - width = outBounds.width(); - height = outBounds.height(); + if (!inOutBounds.isEmpty()) { + // If the bounds is resolved already and WindowLayout doesn't have any opinion on + // its size, use the already resolved size and apply the gravity to it. + width = inOutBounds.width(); + height = inOutBounds.height(); + } else { + getTaskBounds(root, display, windowLayout, WINDOWING_MODE_FREEFORM, + /* hasInitialBounds */ false, inOutBounds); + width = inOutBounds.width(); + height = inOutBounds.height(); + } } else { width = defaultWidth; if (windowLayout.width > 0 && windowLayout.width < defaultWidth) { @@ -555,11 +562,11 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { fractionOfVerticalOffset = 0.5f; } - outBounds.set(0, 0, width, height); - outBounds.offset(displayStableBounds.left, displayStableBounds.top); + inOutBounds.set(0, 0, width, height); + inOutBounds.offset(displayStableBounds.left, displayStableBounds.top); final int xOffset = (int) (fractionOfHorizontalOffset * (defaultWidth - width)); final int yOffset = (int) (fractionOfVerticalOffset * (defaultHeight - height)); - outBounds.offset(xOffset, yOffset); + inOutBounds.offset(xOffset, yOffset); } /** diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index dc15b0749bb1..5b587e9859d8 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -26,14 +26,14 @@ // Log debug messages about InputDispatcherPolicy #define DEBUG_INPUT_DISPATCHER_POLICY 0 - -#include <atomic> -#include <cinttypes> -#include <limits.h> #include <android-base/parseint.h> #include <android-base/stringprintf.h> +#include <android/os/IInputConstants.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/Log.h> +#include <limits.h> +#include <atomic> +#include <cinttypes> #include <utils/Log.h> #include <utils/Looper.h> @@ -46,6 +46,7 @@ #include <input/SpriteController.h> #include <ui/Region.h> +#include <batteryservice/include/batteryservice/BatteryServiceConstants.h> #include <inputflinger/InputManager.h> #include <android_os_MessageQueue.h> @@ -1908,6 +1909,20 @@ static jintArray nativeGetVibratorIds(JNIEnv* env, jclass clazz, jlong ptr, jint return vibIdArray; } +static jint nativeGetBatteryCapacity(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + std::optional<int32_t> ret = im->getInputManager()->getReader()->getBatteryCapacity(deviceId); + return static_cast<jint>(ret.value_or(android::os::IInputConstants::INVALID_BATTERY_CAPACITY)); +} + +static jint nativeGetBatteryStatus(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + std::optional<int32_t> ret = im->getInputManager()->getReader()->getBatteryStatus(deviceId); + return static_cast<jint>(ret.value_or(BATTERY_STATUS_UNKNOWN)); +} + static void nativeReloadKeyboardLayouts(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); @@ -2163,6 +2178,8 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate}, {"nativeIsVibrating", "(JI)Z", (void*)nativeIsVibrating}, {"nativeGetVibratorIds", "(JI)[I", (void*)nativeGetVibratorIds}, + {"nativeGetBatteryCapacity", "(JI)I", (void*)nativeGetBatteryCapacity}, + {"nativeGetBatteryStatus", "(JI)I", (void*)nativeGetBatteryStatus}, {"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts}, {"nativeReloadDeviceAliases", "(J)V", (void*)nativeReloadDeviceAliases}, {"nativeDump", "(J)Ljava/lang/String;", (void*)nativeDump}, diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp index 6fabc589cf95..dfa6083691a9 100644 --- a/services/incremental/ServiceWrappers.cpp +++ b/services/incremental/ServiceWrappers.cpp @@ -210,7 +210,17 @@ public: ErrorCode setUidReadTimeouts(const Control& control, const std::vector<android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts) const final { - return -ENOTSUP; + std::vector<incfs::UidReadTimeouts> timeouts; + timeouts.resize(perUidReadTimeouts.size()); + for (int i = 0, size = perUidReadTimeouts.size(); i < size; ++i) { + auto&& timeout = timeouts[i]; + const auto& perUidTimeout = perUidReadTimeouts[i]; + timeout.uid = perUidTimeout.uid; + timeout.minTimeUs = perUidTimeout.minTimeUs; + timeout.minPendingTimeUs = perUidTimeout.minPendingTimeUs; + timeout.maxPendingTimeUs = perUidTimeout.maxPendingTimeUs; + } + return incfs::setUidReadTimeouts(control, timeouts); } }; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0768bb93839d..918cab3a0bf3 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -333,6 +333,8 @@ public final class SystemServer implements Dumpable { "com.android.server.accessibility.AccessibilityManagerService$Lifecycle"; private static final String ADB_SERVICE_CLASS = "com.android.server.adb.AdbService$Lifecycle"; + private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS = + "com.android.server.speech.SpeechRecognitionManagerService"; private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS = "com.android.server.appprediction.AppPredictionManagerService"; private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS = @@ -357,6 +359,9 @@ public final class SystemServer implements Dumpable { "com.android.server.ConnectivityServiceInitializer"; private static final String IP_CONNECTIVITY_METRICS_CLASS = "com.android.server.connectivity.IpConnectivityMetrics"; + private static final String MEDIA_COMMUNICATION_SERVICE_CLASS = + "com.android.server.media.MediaCommunicationService"; + private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; @@ -1629,12 +1634,21 @@ public final class SystemServer implements Dumpable { "MusicRecognitionManagerService not defined by OEM or disabled by flag"); } - startContentCaptureService(context, t); startAttentionService(context, t); startRotationResolverService(context, t); startSystemCaptionsManagerService(context, t); + // System Speech Recognition Service + if (deviceHasConfigString(context, + R.string.config_defaultOnDeviceSpeechRecognitionService)) { + t.traceBegin("StartSpeechRecognitionManagerService"); + mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } else { + Slog.d(TAG, "System speech recognition is not defined by OEM"); + } + // App prediction manager service if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) { t.traceBegin("StartAppPredictionService"); @@ -2511,6 +2525,10 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS); t.traceEnd(); + t.traceBegin("StartMediaCommunicationService"); + mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS); + t.traceEnd(); + ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart, START_BLOB_STORE_SERVICE); diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java index 743848c2453c..2932926b0b05 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -745,7 +745,8 @@ public class VibratorServiceTest { private InputDevice createInputDeviceWithVibrator(int id) { return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0, - null, /* hasVibrator= */ true, false, false, false /* hasSensor */); + null, /* hasVibrator= */ true, false, false, false /* hasSensor */, + false /* hasBattery */); } private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index aadab6ea4fd9..2f36c7fb9044 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -365,13 +365,13 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.registerClientProfileInternal( profiles[0], listener, clientId0); assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.getClientProfile(clientId0[0]) - .setPriority(clientPriorities[0]); + mTunerResourceManagerService.updateClientPriorityInternal( + clientId0[0], clientPriorities[0], 0/*niceValue*/); mTunerResourceManagerService.registerClientProfileInternal( profiles[1], new TestResourcesReclaimListener(), clientId1); assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.getClientProfile(clientId1[0]) - .setPriority(clientPriorities[1]); + mTunerResourceManagerService.updateClientPriorityInternal( + clientId1[0], clientPriorities[1], 0/*niceValue*/); // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; @@ -415,13 +415,13 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.registerClientProfileInternal( profiles[0], listener, clientId0); assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.getClientProfile(clientId0[0]) - .setPriority(clientPriorities[0]); + mTunerResourceManagerService.updateClientPriorityInternal( + clientId0[0], clientPriorities[0], 0/*niceValue*/); mTunerResourceManagerService.registerClientProfileInternal( profiles[1], new TestResourcesReclaimListener(), clientId1); assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.getClientProfile(clientId1[0]) - .setPriority(clientPriorities[1]); + mTunerResourceManagerService.updateClientPriorityInternal( + clientId1[0], clientPriorities[1], 0/*niceValue*/); // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; @@ -511,13 +511,13 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.registerClientProfileInternal( profiles[0], listener, clientId0); assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.getClientProfile(clientId0[0]) - .setPriority(clientPriorities[0]); + mTunerResourceManagerService.updateClientPriorityInternal( + clientId0[0], clientPriorities[0], 0/*niceValue*/); mTunerResourceManagerService.registerClientProfileInternal( profiles[1], new TestResourcesReclaimListener(), clientId1); assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.getClientProfile(clientId1[0]) - .setPriority(clientPriorities[1]); + mTunerResourceManagerService.updateClientPriorityInternal( + clientId1[0], clientPriorities[1], 0/*niceValue*/); // Init cas resources. mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); @@ -567,13 +567,13 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.registerClientProfileInternal( profiles[0], listener, clientId0); assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.getClientProfile(clientId0[0]) - .setPriority(clientPriorities[0]); + mTunerResourceManagerService.updateClientPriorityInternal( + clientId0[0], clientPriorities[0], 0/*niceValue*/); mTunerResourceManagerService.registerClientProfileInternal( profiles[1], new TestResourcesReclaimListener(), clientId1); assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.getClientProfile(clientId1[0]) - .setPriority(clientPriorities[1]); + mTunerResourceManagerService.updateClientPriorityInternal( + clientId1[0], clientPriorities[1], 0/*niceValue*/); // Init cicam/cas resources. mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); @@ -697,13 +697,13 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.registerClientProfileInternal( profiles[0], listener, clientId0); assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.getClientProfile(clientId0[0]) - .setPriority(clientPriorities[0]); + mTunerResourceManagerService.updateClientPriorityInternal( + clientId0[0], clientPriorities[0], 0/*niceValue*/); mTunerResourceManagerService.registerClientProfileInternal( profiles[1], new TestResourcesReclaimListener(), clientId1); assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); - mTunerResourceManagerService.getClientProfile(clientId1[0]) - .setPriority(clientPriorities[1]); + mTunerResourceManagerService.updateClientPriorityInternal( + clientId1[0], clientPriorities[1], 0/*niceValue*/); // Init lnb resources. int[] lnbHandles = {1}; diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java index 28d313b4d4b5..e71c2f5ba8da 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java @@ -294,6 +294,8 @@ public class InputDeviceDelegateTest { private InputDevice createInputDevice(int id, boolean hasVibrator) { return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0, - null, hasVibrator, false, false, false /* hasSensor */); + null, hasVibrator, false, false, false /* hasSensor */, false /* hasBattery */); + + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 49847992125e..ea8619fe7fa8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -521,6 +521,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { WINDOWING_MODE_FULLSCREEN); } + @Test public void testKeepsPictureInPictureLaunchModeInOptions() { final TestDisplayContent freeformDisplay = createNewDisplayContent( @@ -588,11 +589,14 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { } @Test - public void testNonEmptyLayoutInfersFreeformWithEmptySize() { + public void testLayoutWithGravityAndEmptySizeInfersFreeformAndRespectsCurrentSize() { final TestDisplayContent freeformDisplay = createNewDisplayContent( WINDOWING_MODE_FREEFORM); + final Rect expectedLaunchBounds = new Rect(0, 0, 200, 100); + mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea(); + mCurrent.mBounds.set(expectedLaunchBounds); final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() .setGravity(Gravity.LEFT).build(); @@ -600,6 +604,9 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setLayout(layout).calculate()); + assertEquals(expectedLaunchBounds.width(), mResult.mBounds.width()); + assertEquals(expectedLaunchBounds.height(), mResult.mBounds.height()); + assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode, WINDOWING_MODE_FREEFORM); } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 5b03863efc7d..472d63946ebc 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -314,20 +314,22 @@ public class TelecomManager { public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE"; /** - * A URI representing the picture that was downloaded when a call is received. + * A URI representing the picture that was downloaded when a call is received or uploaded + * when a call is placed. + * * This is a content URI within the call log provider which can be used to open a file * descriptor. This could be set a short time after a call is added to the Dialer app if the - * download is delayed for some reason. The Dialer app will receive a callback via + * download/upload is delayed for some reason. The Dialer app will receive a callback via * {@link Call.Callback#onDetailsChanged} when this value has changed. * * Reference: RCC.20 Section 2.4.3.2 */ - public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE"; + public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI"; - // TODO(hallliu), This UUID is obtained from TelephonyManager#uploadCallComposerPicture. /** * A ParcelUuid used as a token to represent a picture that was uploaded prior to the call - * being placed. + * being placed. The value of this extra should be set using the {@link android.os.ParcelUuid} + * obtained from the callback in {@link TelephonyManager#uploadCallComposerPicture}. */ public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE"; diff --git a/telephony/java/android/telephony/TelephonyLocalConnection.java b/telephony/java/android/telephony/TelephonyLocalConnection.java new file mode 100644 index 000000000000..1cab267cc817 --- /dev/null +++ b/telephony/java/android/telephony/TelephonyLocalConnection.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import java.util.UUID; + +/** + * Shim used for code in frameworks/opt/telephony to be able to call code in + * packages/services/Telephony. A singleton instance of this class is set when the phone process + * is brought up. + * @hide + */ +public class TelephonyLocalConnection { + public interface ConnectionImpl { + String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid); + } + private static ConnectionImpl sInstance; + + public static String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid) { + checkInstance(); + return sInstance.getCallComposerServerUrlForHandle(subscriptionId, uuid); + } + + private static void checkInstance() { + if (sInstance == null) { + throw new IllegalStateException("Connection impl is null!"); + } + } + + public static void setInstance(ConnectionImpl impl) { + sInstance = impl; + } +} diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 1a940c75cfa4..c6c67feeed72 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -753,6 +753,15 @@ </intent-filter> </activity> + <activity android:name="RenderEffectShaderActivity" + android:label="RenderEffect/Shader" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="com.android.test.hwui.TEST"/> + </intent-filter> + </activity> + <activity android:name="TextActivity" android:label="Text/Simple Text" android:theme="@android:style/Theme.NoTitleBar" diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java new file mode 100644 index 000000000000..661d48a84768 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.hwui; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.RenderEffect; +import android.graphics.RenderNode; +import android.graphics.Shader; +import android.os.Bundle; +import android.view.Gravity; +import android.view.View; +import android.widget.LinearLayout; + +@SuppressWarnings({"UnusedDeclaration"}) +public class RenderEffectShaderActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout layout = new LinearLayout(this); + layout.setClipChildren(false); + layout.setGravity(Gravity.CENTER); + layout.setOrientation(LinearLayout.VERTICAL); + + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500, 500); + params.bottomMargin = 100; + + layout.addView(new ShaderRenderEffectView(this), params); + + setContentView(layout); + } + + public static class ShaderRenderEffectView extends View { + + private final Paint mPaint; + private final RenderNode mRenderNode; + + public ShaderRenderEffectView(Context c) { + super(c); + + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mRenderNode = new RenderNode("blurNode"); + + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (changed) { + LinearGradient gradient = new LinearGradient( + 0f, 0f, + 0f, bottom - top, + new int[]{Color.CYAN, Color.MAGENTA}, + null, + Shader.TileMode.CLAMP + ); + mRenderNode.setRenderEffect( + RenderEffect.createShaderEffect(gradient) + ); + + int width = right - left; + int height = bottom - top; + mRenderNode.setPosition(0, 0, width, height); + Canvas canvas = mRenderNode.beginRecording(width, height); + mPaint.setColor(Color.BLUE); + + canvas.drawRect( + 0, + 0, + width, + height, + mPaint + ); + + mPaint.setColor(Color.RED); + canvas.drawCircle((right - left) / 2f, (bottom - top) / 2f, 50f, mPaint); + + mRenderNode.endRecording(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.drawRenderNode(mRenderNode); + } + } +} diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 401d87af5a33..8b34755f73b8 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -125,21 +125,11 @@ public class StagedRollbackTest { } /** - * Test rollbacks of staged installs involving only apks with bad update. - * Trigger rollback phase. - */ - @Test - public void testBadApkOnly_Phase3_Crash() throws Exception { - // One more crash to trigger rollback - RollbackUtils.sendCrashBroadcast(TestApp.A, 1); - } - - /** * Test rollbacks of staged installs involving only apks. * Confirm rollback phase. */ @Test - public void testBadApkOnly_Phase4_VerifyRollback() throws Exception { + public void testBadApkOnly_Phase3_VerifyRollback() throws Exception { assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); InstallUtils.processUserData(TestApp.A); @@ -447,8 +437,10 @@ public class StagedRollbackTest { Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1), Rollback.from(TestApp.A, 0).to(TestApp.A1)); - // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback - RollbackUtils.sendCrashBroadcast(TestApp.A, 5); + // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT-1 times + RollbackUtils.sendCrashBroadcast(TestApp.A, 4); + // Sleep for a while to make sure we don't trigger rollback + Thread.sleep(TimeUnit.SECONDS.toMillis(30)); } @Test diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 1d5730fb4427..67417bd39864 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -153,13 +153,14 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { getDevice().reboot(); runPhase("testBadApkOnly_Phase2_VerifyInstall"); - // Trigger rollback and wait for reboot to happen - runPhase("testBadApkOnly_Phase3_Crash"); + // Launch the app to crash to trigger rollback + startActivity(TESTAPP_A); + // Wait for reboot to happen waitForDeviceNotAvailable(2, TimeUnit.MINUTES); getDevice().waitForDeviceAvailable(); - runPhase("testBadApkOnly_Phase4_VerifyRollback"); + runPhase("testBadApkOnly_Phase3_VerifyRollback"); assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_APP_CRASH, TESTAPP_A); assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null); @@ -304,8 +305,10 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { getDevice().reboot(); // Verify apex was installed and then crash the apk runPhase("testRollbackApexWithApkCrashing_Phase2_Crash"); - // Wait for crash to trigger rollback - waitForDeviceNotAvailable(5, TimeUnit.MINUTES); + // Launch the app to crash to trigger rollback + startActivity(TESTAPP_A); + // Wait for reboot to happen + waitForDeviceNotAvailable(2, TimeUnit.MINUTES); getDevice().waitForDeviceAvailable(); // Verify rollback occurred due to crash of apk-in-apex runPhase("testRollbackApexWithApkCrashing_Phase3_VerifyRollback"); @@ -631,6 +634,12 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { } } + private void startActivity(String packageName) throws Exception { + String cmd = "am start -S -a android.intent.action.MAIN " + + "-c android.intent.category.LAUNCHER " + packageName; + getDevice().executeShellCommand(cmd); + } + private void crashProcess(String processName, int numberOfCrashes) throws Exception { String pid = ""; String lastPid = "invalid"; |