diff options
307 files changed, 5875 insertions, 2094 deletions
diff --git a/Android.bp b/Android.bp index 2ccadd318df6..9655daf649c7 100644 --- a/Android.bp +++ b/Android.bp @@ -536,7 +536,7 @@ java_library { "android.hardware.vibrator-V1.3-java", "android.security.apc-java", "android.security.authorization-java", - "android.system.keystore2-java", + "android.system.keystore2-V1-java", "android.system.suspend.control.internal-java", "cameraprotosnano", "devicepolicyprotosnano", diff --git a/apct-tests/perftests/core/src/android/app/OWNERS b/apct-tests/perftests/core/src/android/app/OWNERS new file mode 100644 index 000000000000..4f168ceb3c55 --- /dev/null +++ b/apct-tests/perftests/core/src/android/app/OWNERS @@ -0,0 +1,2 @@ +per-file Overlay* = file:/core/java/android/app/RESOURCES_OWNERS +per-file Resources* = file:/core/java/android/app/RESOURCES_OWNERS diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java index e1a859648ee6..aefeab621778 100644 --- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java +++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java @@ -36,30 +36,46 @@ import java.util.Map; import java.util.Set; /** - * ApplicationMediaCapabilities is an immutable class that encapsulates an application's - * capabilities for handling newer video codec format and media features. - * - * The ApplicationMediaCapabilities class is used by the platform to represent an application's - * media capabilities as defined in their manifest(TODO: Add link) in order to determine - * whether modern media files need to be transcoded for that application (TODO: Add link). - * - * ApplicationMediaCapabilities objects can also be built by applications at runtime for use with - * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more - * control over the transcoding that is built into the platform. ApplicationMediaCapabilities - * provided by applications at runtime like this override the default manifest capabilities for that - * media access. - * - * <h3> Video Codec Support</h3> - * Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support - * for newer format with this class as they are assumed to support older format like h.264. - * - * <h4>Capability of handling HDR(high dynamic range) video</h4> - * There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform, - * application will only need to specify individual types they supported. + ApplicationMediaCapabilities is an immutable class that encapsulates an application's capabilities + for handling newer video codec format and media features. + + <p> + Android 12 introduces seamless media transcoding feature. By default, Android assumes apps can + support playback of all media formats. Apps that would like to request that media be transcoded + into a more compatible format should declare their media capabilities in a media_capabilities + .xml resource file and add it as a property tag in the AndroidManifest.xml file. Here is a example: + <pre> + {@code + <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android"> + <format android:name="HEVC" supported="true"/> + <format android:name="HDR10" supported="false"/> + <format android:name="HDR10Plus" supported="false"/> + </media-capabilities> + } + </pre> + The ApplicationMediaCapabilities class is generated from this xml and used by the platform to + represent an application's media capabilities in order to determine whether modern media files need + to be transcoded for that application. + </p> + + <p> + ApplicationMediaCapabilities objects can also be built by applications at runtime for use with + {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more + control over the transcoding that is built into the platform. ApplicationMediaCapabilities + provided by applications at runtime like this override the default manifest capabilities for that + media access.The object could be build either through {@link #createFromXml(XmlPullParser)} or + through the builder class {@link ApplicationMediaCapabilities.Builder} + + <h3> Video Codec Support</h3> + <p> + Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support + for newer format with this class as they are assumed to support older format like h.264. + + <h3>Capability of handling HDR(high dynamic range) video</h3> + <p> + There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform, + application will only need to specify individual types they supported. */ -// TODO(huang): Correct openTypedAssetFileDescriptor with the new API after it is added. -// TODO(hkuang): Add a link to seamless transcoding detail when it is published -// TODO(hkuang): Add code sample on how to build a capability object with MediaCodecList public final class ApplicationMediaCapabilities implements Parcelable { private static final String TAG = "ApplicationMediaCapabilities"; @@ -105,9 +121,9 @@ public final class ApplicationMediaCapabilities implements Parcelable { */ public boolean isVideoMimeTypeSupported( @NonNull String videoMime) throws FormatNotFoundException { - if (mUnsupportedVideoMimeTypes.contains(videoMime)) { + if (mUnsupportedVideoMimeTypes.contains(videoMime.toLowerCase())) { return false; - } else if (mSupportedVideoMimeTypes.contains(videoMime)) { + } else if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) { return true; } else { throw new FormatNotFoundException(videoMime); @@ -262,11 +278,27 @@ public final class ApplicationMediaCapabilities implements Parcelable { /** * Creates {@link ApplicationMediaCapabilities} from an xml. + * + * The xml's syntax is the same as the media_capabilities.xml used by the AndroidManifest.xml. + * <p> Here is an example: + * + * <pre> + * {@code + * <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android"> + * <format android:name="HEVC" supported="true"/> + * <format android:name="HDR10" supported="false"/> + * <format android:name="HDR10Plus" supported="false"/> + * </media-capabilities> + * } + * </pre> + * <p> + * * @param xmlParser The underlying {@link XmlPullParser} that will read the xml. * @return An ApplicationMediaCapabilities object. * @throws UnsupportedOperationException if the capabilities in xml config are invalid or * incompatible. */ + // TODO: Add developer.android.com link for the format of the xml. @NonNull public static ApplicationMediaCapabilities createFromXml(@NonNull XmlPullParser xmlParser) { ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder(); @@ -430,7 +462,7 @@ public final class ApplicationMediaCapabilities implements Parcelable { mIsSlowMotionSupported = isSupported; break; default: - throw new UnsupportedOperationException("Invalid format name " + name); + Log.w(TAG, "Invalid format name " + name); } // Save the name and isSupported into the map for validate later. mFormatSupportedMap.put(name, isSupported); diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist index 43a8a878a624..da4b25519e80 100644 --- a/config/preloaded-classes-denylist +++ b/config/preloaded-classes-denylist @@ -4,7 +4,6 @@ android.os.FileObserver android.os.NullVibrator android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask android.widget.Magnifier -com.android.server.BootReceiver$2 gov.nist.core.net.DefaultNetworkLayer android.net.rtp.AudioGroup android.net.rtp.AudioStream diff --git a/core/api/current.txt b/core/api/current.txt index bfdfb4060078..f5c67c170931 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6930,6 +6930,7 @@ package android.app.admin { method public void addPersistentPreferredActivity(@NonNull android.content.ComponentName, android.content.IntentFilter, @NonNull android.content.ComponentName); method public void addUserRestriction(@NonNull android.content.ComponentName, String); method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle); + method public boolean canAdminGrantSensorsPermissions(); method public void clearApplicationUserData(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener); method public void clearCrossProfileIntentFilters(@NonNull android.content.ComponentName); method @Deprecated public void clearDeviceOwnerApp(String); @@ -7227,6 +7228,7 @@ package android.app.admin { field public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI"; field public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR"; field public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE"; + field public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT = "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT"; field public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER"; field public static final String EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS = "android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS"; field public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION"; @@ -51073,7 +51075,7 @@ package android.view.inputmethod { method public boolean sendKeyEvent(android.view.KeyEvent); method public boolean setComposingRegion(int, int); method public boolean setComposingText(CharSequence, int); - method public default boolean setImeTemporarilyConsumesInput(boolean); + method public default boolean setImeConsumesInput(boolean); method public boolean setSelection(int, int); field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1 field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 3111b7c00630..4d24cad699b6 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -870,6 +870,7 @@ package android.app.admin { } public class DevicePolicyManager { + method public boolean canAdminGrantSensorsPermissionsForUser(int); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser(); @@ -4971,6 +4972,7 @@ package android.media { method public android.media.PlayerProxy getPlayerProxy(); method public int getPlayerState(); method public int getPlayerType(); + method @IntRange(from=0) public int getSessionId(); method public boolean isActive(); field public static final int PLAYER_STATE_IDLE = 1; // 0x1 field public static final int PLAYER_STATE_PAUSED = 3; // 0x3 @@ -8643,6 +8645,7 @@ package android.os { method @NonNull public static String formatUid(int); method public static int getAppId(int); method public int getIdentifier(); + method public static int getUid(@NonNull android.os.UserHandle, int); method @Deprecated public boolean isOwner(); method public boolean isSystem(); method public static int myUserId(); @@ -9510,6 +9513,11 @@ package android.security.keystore { method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int); } + public abstract class KeyProperties { + field public static final int NAMESPACE_APPLICATION = -1; // 0xffffffff + field public static final int NAMESPACE_WIFI = 102; // 0x66 + } + } package android.security.keystore.recovery { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 79917d020198..e39b2b856661 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -12,6 +12,7 @@ package android { field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS"; + field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; field public static final String MANAGE_ACTIVITY_TASKS = "android.permission.MANAGE_ACTIVITY_TASKS"; @@ -463,6 +464,7 @@ package android.app.admin { } public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable { + method public boolean canDeviceOwnerGrantSensorsPermissions(); method public int describeContents(); method @NonNull public android.content.ComponentName getDeviceAdminComponentName(); method public long getLocalTime(); @@ -477,6 +479,7 @@ package android.app.admin { public static final class FullyManagedDeviceProvisioningParams.Builder { ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build(); + method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setDeviceOwnerCanGrantSensorsPermissions(boolean); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long); method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale); @@ -900,6 +903,40 @@ package android.hardware.camera2 { } +package android.hardware.devicestate { + + public final class DeviceStateManager { + method public void addDeviceStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener); + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest); + method @NonNull public int[] getSupportedStates(); + method public void removeDeviceStateListener(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener); + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback); + } + + public static interface DeviceStateManager.DeviceStateListener { + method public void onDeviceStateChanged(int); + } + + public final class DeviceStateRequest { + method public int getFlags(); + method public int getState(); + method @NonNull public static android.hardware.devicestate.DeviceStateRequest.Builder newBuilder(int); + field public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1; // 0x1 + } + + public static final class DeviceStateRequest.Builder { + method @NonNull public android.hardware.devicestate.DeviceStateRequest build(); + method @NonNull public android.hardware.devicestate.DeviceStateRequest.Builder setFlags(int); + } + + public static interface DeviceStateRequest.Callback { + method public default void onRequestActivated(@NonNull android.hardware.devicestate.DeviceStateRequest); + method public default void onRequestCanceled(@NonNull android.hardware.devicestate.DeviceStateRequest); + method public default void onRequestSuspended(@NonNull android.hardware.devicestate.DeviceStateRequest); + } + +} + package android.hardware.display { public class AmbientDisplayConfiguration { diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 9b6f4b43581b..c31c22cca329 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -538,4 +538,10 @@ public abstract class ActivityManagerInternal { * @return mBootTimeTempAllowlistDuration of ActivityManagerConstants. */ public abstract long getBootTimeTempAllowListDuration(); + + /** Register an {@link AnrController} to control the ANR dialog behavior */ + public abstract void registerAnrController(AnrController controller); + + /** Unregister an {@link AnrController} */ + public abstract void unregisterAnrController(AnrController controller); } diff --git a/core/java/android/app/AnrController.java b/core/java/android/app/AnrController.java new file mode 100644 index 000000000000..cfc9d2715720 --- /dev/null +++ b/core/java/android/app/AnrController.java @@ -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.app; + +/** + * Interface to control the ANR dialog within the activity manager + * {@hide} + */ +public interface AnrController { + /** + * Returns the delay in milliseconds for an ANR dialog that is about to be shown for + * {@code packageName}. + */ + long getAnrDelayMillis(String packageName, int uid); +} diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index e6aa7a77357c..1ff64dbe6d2e 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -59,7 +59,7 @@ per-file IInstantAppResolver.aidl = file:/services/core/java/com/android/server/ per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS # ResourcesManager -per-file ResourcesManager.java = rtmitchell@google.com, toddke@google.com +per-file ResourcesManager.java = file:RESOURCES_OWNERS # VoiceInteraction per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS diff --git a/core/java/android/app/RESOURCES_OWNERS b/core/java/android/app/RESOURCES_OWNERS new file mode 100644 index 000000000000..21c39a8828ad --- /dev/null +++ b/core/java/android/app/RESOURCES_OWNERS @@ -0,0 +1,2 @@ +rtmitchell@google.com +toddke@google.com diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java index 9092ef36deb6..29792ac47a36 100644 --- a/core/java/android/app/WindowTokenClient.java +++ b/core/java/android/app/WindowTokenClient.java @@ -44,8 +44,8 @@ public class WindowTokenClient extends IWindowToken.Stub { * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient} * can only attach one {@link Context}. * <p>This method must be called before invoking - * {@link android.view.IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle, - * String)}.<p/> + * {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, + * Bundle, boolean)}.<p/> * * @param context context to be attached * @throws IllegalStateException if attached context has already existed. diff --git a/core/java/android/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java index 8b0c7061925f..9c07f85a6390 100644 --- a/core/java/android/app/admin/DevicePolicyCache.java +++ b/core/java/android/app/admin/DevicePolicyCache.java @@ -57,6 +57,13 @@ public abstract class DevicePolicyCache { public abstract int getPermissionPolicy(@UserIdInt int userHandle); /** + * Caches {@link DevicePolicyManager#canAdminGrantSensorsPermissionsForUser(int)} for the + * given user. + */ + public abstract boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle); + + + /** * Empty implementation. */ private static class EmptyDevicePolicyCache extends DevicePolicyCache { @@ -77,5 +84,10 @@ public abstract class DevicePolicyCache { public int getPermissionPolicy(int userHandle) { return DevicePolicyManager.PERMISSION_POLICY_PROMPT; } + + @Override + public boolean canAdminGrantSensorsPermissionsForUser(int userHandle) { + return false; + } } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 06fe9d764f25..82255c87c971 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -980,6 +980,19 @@ public class DevicePolicyManager { = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM"; /** + * A boolean extra indicating the admin of a fully-managed device opts out of controlling + * permission grants for sensor-related permissions, + * see {@link #setPermissionGrantState(ComponentName, String, String, int)}. + * + * The default for this extra is {@code false} - by default, the admin of a fully-managed + * device has the ability to grant sensors-related permissions. + * + * <p>Use with {@link #ACTION_PROVISION_MANAGED_DEVICE} only. + */ + public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT = + "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT"; + + /** * A String extra holding the URL-safe base64 encoded SHA-256 checksum of any signature of the * android package archive at the download location specified in {@link * #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}. @@ -10520,6 +10533,13 @@ public class DevicePolicyManager { * As this policy only acts on runtime permission requests, it only applies to applications * built with a {@code targetSdkVersion} of {@link android.os.Build.VERSION_CODES#M} or later. * + * <p> + * NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, an auto-grant + * policy will not apply to certain sensors-related permissions on some configurations. + * See {@link #setPermissionGrantState(ComponentName, String, String, int)} for the list of + * permissions affected, and the behavior change for managed profiles and fully-managed + * devices. + * * @param admin Which profile or device owner this request is associated with. * @param policy One of the policy constants {@link #PERMISSION_POLICY_PROMPT}, * {@link #PERMISSION_POLICY_AUTO_GRANT} and {@link #PERMISSION_POLICY_AUTO_DENY}. @@ -10578,6 +10598,31 @@ public class DevicePolicyManager { * application built with a {@code targetSdkVersion} < * {@link android.os.Build.VERSION_CODES#M} the app-op matching the permission is set to * {@link android.app.AppOpsManager#MODE_IGNORED}, but the permission stays granted. + * <p> + * NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, control over + * the following, sensors-related, permissions is restricted: + * <ul> + * <li>Manifest.permission.ACCESS_FINE_LOCATION</li> + * <li>Manifest.permission.ACCESS_BACKGROUND_LOCATION</li> + * <li>Manifest.permission.ACCESS_COARSE_LOCATION</li> + * <li>Manifest.permission.CAMERA</li> + * <li>Manifest.permission.RECORD_AUDIO</li> + * <li>Manifest.permission.RECORD_BACKGROUND_AUDIO</li> + * <li>Manifest.permission.ACTIVITY_RECOGNITION</li> + * <li>Manifest.permission.BODY_SENSORS</li> + * </ul> + * <p> + * A profile owner may not grant these permissions (i.e. call this method with any of the + * permissions listed above and {@code grantState} of {@code #PERMISSION_GRANT_STATE_GRANTED}), + * but may deny them. + * <p> + * A device owner, by default, may continue granting these permissions. However, for increased + * user control, the admin may opt out of controlling grants for these permissions by including + * {@link #EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT} in the provisioning parameters. In that + * case the device owner's control will be limited do denying these permissions. + * <p> + * Attempts by the admin to grant these permissions, when the admin is restricted from doing + * so, will be silently ignored (no exception will be thrown). * * @param admin Which profile or device owner this request is associated with. * @param packageName The application to grant or revoke a permission to. @@ -13271,4 +13316,38 @@ public class DevicePolicyManager { } } } + /** + * Returns true if the caller is running on a device where the admin can grant + * permissions related to device sensors. + * This is a signal that the device is a fully-managed device where personal usage is + * discouraged. + * The list of permissions is listed in + * {@link #setPermissionGrantState(ComponentName, String, String, int)}. + * + * May be called by any app. + * @return true if the app can grant device sensors-related permissions, false otherwise. + */ + public boolean canAdminGrantSensorsPermissions() { + return canAdminGrantSensorsPermissionsForUser(myUserId()); + } + + /** + * Returns true if the admin can control grants of sensors-related permissions, for + * a given user. + * + * @hide + * @param userId The ID of the user to check. + * @return if the admin may grant these permissions, false otherwise. + */ + @SystemApi + public boolean canAdminGrantSensorsPermissionsForUser(int userId) { + if (mService == null) { + return false; + } + try { + return mService.canAdminGrantSensorsPermissionsForUser(userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java index 83af0195ddba..5e1cbadb458e 100644 --- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java +++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java @@ -42,6 +42,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { private final long mLocalTime; @SuppressLint("UseIcu") @Nullable private final Locale mLocale; + private final boolean mDeviceOwnerCanGrantSensorsPermissions; private FullyManagedDeviceProvisioningParams( @NonNull ComponentName deviceAdminComponentName, @@ -49,13 +50,16 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { boolean leaveAllSystemAppsEnabled, @Nullable String timeZone, long localTime, - @Nullable @SuppressLint("UseIcu") Locale locale) { + @Nullable @SuppressLint("UseIcu") Locale locale, + boolean deviceOwnerCanGrantSensorsPermissions) { this.mDeviceAdminComponentName = requireNonNull(deviceAdminComponentName); this.mOwnerName = requireNonNull(ownerName); this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled; this.mTimeZone = timeZone; this.mLocalTime = localTime; this.mLocale = locale; + this.mDeviceOwnerCanGrantSensorsPermissions = + deviceOwnerCanGrantSensorsPermissions; } private FullyManagedDeviceProvisioningParams( @@ -64,13 +68,15 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { boolean leaveAllSystemAppsEnabled, @Nullable String timeZone, long localTime, - @Nullable String localeStr) { + @Nullable String localeStr, + boolean deviceOwnerCanGrantSensorsPermissions) { this(deviceAdminComponentName, ownerName, leaveAllSystemAppsEnabled, timeZone, localTime, - getLocale(localeStr)); + getLocale(localeStr), + deviceOwnerCanGrantSensorsPermissions); } @Nullable @@ -107,6 +113,14 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { } /** + * @return true if the device owner can control sensor-related permission grants, false + * if the device owner has opted out of it. + */ + public boolean canDeviceOwnerGrantSensorsPermissions() { + return mDeviceOwnerCanGrantSensorsPermissions; + } + + /** * Builder class for {@link FullyManagedDeviceProvisioningParams} objects. */ public static final class Builder { @@ -117,6 +131,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { private long mLocalTime; @SuppressLint("UseIcu") @Nullable private Locale mLocale; + // Default to allowing control over sensor permission grants. + boolean mDeviceOwnerCanGrantSensorsPermissions = true; /** * Initialize a new {@link Builder} to construct a @@ -181,6 +197,17 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { } /** + * Marks that the Device Owner may grant permissions related to device sensors. + * See {@link DevicePolicyManager#EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT}. + */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setDeviceOwnerCanGrantSensorsPermissions(boolean mayGrant) { + mDeviceOwnerCanGrantSensorsPermissions = mayGrant; + return this; + } + + /** * Combines all of the attributes that have been set on this {@code Builder} * * @return a new {@link FullyManagedDeviceProvisioningParams} object. @@ -193,7 +220,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { mLeaveAllSystemAppsEnabled, mTimeZone, mLocalTime, - mLocale); + mLocale, + mDeviceOwnerCanGrantSensorsPermissions); } } @@ -211,6 +239,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { + ", mTimeZone=" + (mTimeZone == null ? "null" : mTimeZone) + ", mLocalTime=" + mLocalTime + ", mLocale=" + (mLocale == null ? "null" : mLocale) + + ", mDeviceOwnerCanGrantSensorsPermissions=" + + mDeviceOwnerCanGrantSensorsPermissions + '}'; } @@ -222,6 +252,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { dest.writeString(mTimeZone); dest.writeLong(mLocalTime); dest.writeString(mLocale == null ? null : mLocale.toLanguageTag()); + dest.writeBoolean(mDeviceOwnerCanGrantSensorsPermissions); } @NonNull @@ -235,6 +266,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { String timeZone = in.readString(); long localtime = in.readLong(); String locale = in.readString(); + boolean deviceOwnerCanGrantSensorsPermissions = in.readBoolean(); return new FullyManagedDeviceProvisioningParams( componentName, @@ -242,7 +274,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable { leaveAllSystemAppsEnabled, timeZone, localtime, - locale); + locale, + deviceOwnerCanGrantSensorsPermissions); } @Override diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index cf0b31ea8cb2..89f30cc821ab 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -500,4 +500,5 @@ interface IDevicePolicyManager { void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage); void resetDefaultCrossProfileIntentFilters(int userId); + boolean canAdminGrantSensorsPermissionsForUser(int userId); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 5d28216756ae..2a402b204cb7 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3552,6 +3552,7 @@ public abstract class Context { //@hide: NETWORK_SCORE_SERVICE, USAGE_STATS_SERVICE, MEDIA_SESSION_SERVICE, + MEDIA_COMMUNICATION_SERVICE, BATTERY_SERVICE, JOB_SCHEDULER_SERVICE, //@hide: PERSISTENT_DATA_BLOCK_SERVICE, diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 62925757cde1..0c0e4020d04a 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -450,6 +450,7 @@ public class LauncherApps { FLAG_MATCH_PINNED, FLAG_MATCH_MANIFEST, FLAG_MATCH_CACHED, + FLAG_MATCH_PINNED_BY_ANY_LAUNCHER, FLAG_GET_KEY_FIELDS_ONLY, FLAG_GET_PERSONS_DATA, }) diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java index cbd2c550a797..9012b5ce2b1e 100644 --- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java @@ -108,17 +108,14 @@ public class ParsedPermissionUtils { permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel); - if (permission.getProtectionFlags() != 0) { - if ((permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0 - && (permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) - == 0 - && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) - != PermissionInfo.PROTECTION_SIGNATURE - && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) - != PermissionInfo.PROTECTION_INTERNAL) { - return input.error("<permission> protectionLevel specifies a non-instant flag " - + "but is not based on signature or internal type"); - } + final int otherProtectionFlags = permission.getProtectionFlags() + & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT + | PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY); + if (otherProtectionFlags != 0 + && permission.getProtection() != PermissionInfo.PROTECTION_SIGNATURE + && permission.getProtection() != PermissionInfo.PROTECTION_INTERNAL) { + return input.error("<permission> protectionLevel specifies a non-instant, non-appop," + + " non-runtimeOnly flag but is not based on signature or internal type"); } return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input); 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/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java index 29a6ee278d9c..f175e7b00b7e 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManager.java +++ b/core/java/android/hardware/devicestate/DeviceStateManager.java @@ -16,8 +16,12 @@ package android.hardware.devicestate; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemService; +import android.annotation.TestApi; import android.content.Context; import java.util.concurrent.Executor; @@ -28,13 +32,19 @@ import java.util.concurrent.Executor; * * @hide */ +@TestApi @SystemService(Context.DEVICE_STATE_SERVICE) public final class DeviceStateManager { - /** Invalid device state. */ + /** + * Invalid device state. + * + * @hide + */ public static final int INVALID_DEVICE_STATE = -1; - private DeviceStateManagerGlobal mGlobal; + private final DeviceStateManagerGlobal mGlobal; + /** @hide */ public DeviceStateManager() { DeviceStateManagerGlobal global = DeviceStateManagerGlobal.getInstance(); if (global == null) { @@ -45,23 +55,73 @@ public final class DeviceStateManager { } /** + * Returns the list of device states that are supported and can be requested with + * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. + */ + @NonNull + public int[] getSupportedStates() { + return mGlobal.getSupportedStates(); + } + + /** + * Submits a {@link DeviceStateRequest request} to modify the device state. + * <p> + * By default, the request is kept active until a call to + * {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs: + * <ul> + * <li>Another processes submits a request succeeding this request in which case the request + * will be suspended until the interrupting request is canceled. + * <li>The requested state has become unsupported. + * <li>The process submitting the request dies. + * </ul> + * However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}. + * + * @throws IllegalArgumentException if the requested state is unsupported. + * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} + * permission is not held. + * + * @see DeviceStateRequest + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) + public void requestState(@NonNull DeviceStateRequest request, + @Nullable @CallbackExecutor Executor executor, + @Nullable DeviceStateRequest.Callback callback) { + mGlobal.requestState(request, callback, executor); + } + + /** + * Cancels a {@link DeviceStateRequest request} previously submitted with a call to + * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. + * <p> + * This method is noop if the {@code request} has not been submitted with a call to + * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. + * + * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} + * permission is not held. + */ + @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) + public void cancelRequest(@NonNull DeviceStateRequest request) { + mGlobal.cancelRequest(request); + } + + /** * Registers a listener to receive notifications about changes in device state. * - * @param listener the listener to register. * @param executor the executor to process notifications. + * @param listener the listener to register. * * @see DeviceStateListener */ - public void registerDeviceStateListener(@NonNull DeviceStateListener listener, - @NonNull Executor executor) { + public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor, + @NonNull DeviceStateListener listener) { mGlobal.registerDeviceStateListener(listener, executor); } /** * Unregisters a listener previously registered with - * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}. + * {@link #addDeviceStateListener(Executor, DeviceStateListener)}. */ - public void unregisterDeviceStateListener(@NonNull DeviceStateListener listener) { + public void removeDeviceStateListener(@NonNull DeviceStateListener listener) { mGlobal.unregisterDeviceStateListener(listener); } diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java index c8905038d056..b9ae88ea840f 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java @@ -20,9 +20,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.devicestate.DeviceStateManager.DeviceStateListener; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -67,6 +69,9 @@ public final class DeviceStateManagerGlobal { @GuardedBy("mLock") private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>(); + @GuardedBy("mLock") + private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>(); + @Nullable @GuardedBy("mLock") private Integer mLastReceivedState; @@ -77,9 +82,84 @@ public final class DeviceStateManagerGlobal { } /** + * Returns the set of supported device states. + * + * @see DeviceStateManager#getSupportedStates() + */ + public int[] getSupportedStates() { + try { + return mDeviceStateManager.getSupportedDeviceStates(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Submits a {@link DeviceStateRequest request} to modify the device state. + * + * @see DeviceStateManager#requestState(DeviceStateRequest, + * Executor, DeviceStateRequest.Callback) + * @see DeviceStateRequest + */ + public void requestState(@NonNull DeviceStateRequest request, + @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) { + if (callback == null && executor != null) { + throw new IllegalArgumentException("Callback must be supplied with executor."); + } else if (executor == null && callback != null) { + throw new IllegalArgumentException("Executor must be supplied with callback."); + } + + synchronized (mLock) { + registerCallbackIfNeededLocked(); + + if (findRequestTokenLocked(request) != null) { + // This request has already been submitted. + return; + } + + // Add the request wrapper to the mRequests array before requesting the state as the + // callback could be triggered immediately if the mDeviceStateManager IBinder is in the + // same process as this instance. + IBinder token = new Binder(); + mRequests.put(token, new DeviceStateRequestWrapper(request, callback, executor)); + + try { + mDeviceStateManager.requestState(token, request.getState(), request.getFlags()); + } catch (RemoteException ex) { + mRequests.remove(token); + throw ex.rethrowFromSystemServer(); + } + } + } + + /** + * Cancels a {@link DeviceStateRequest request} previously submitted with a call to + * {@link #requestState(DeviceStateRequest, DeviceStateRequest.Callback, Executor)}. + * + * @see DeviceStateManager#cancelRequest(DeviceStateRequest) + */ + public void cancelRequest(@NonNull DeviceStateRequest request) { + synchronized (mLock) { + registerCallbackIfNeededLocked(); + + final IBinder token = findRequestTokenLocked(request); + if (token == null) { + // This request has not been submitted. + return; + } + + try { + mDeviceStateManager.cancelRequest(token); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + } + + /** * Registers a listener to receive notifications about changes in device state. * - * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor) + * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener) */ @VisibleForTesting(visibility = Visibility.PACKAGE) public void registerDeviceStateListener(@NonNull DeviceStateListener listener, @@ -112,7 +192,7 @@ public final class DeviceStateManagerGlobal { * Unregisters a listener previously registered with * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}. * - * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor) + * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener) */ @VisibleForTesting(visibility = Visibility.PACKAGE) public void unregisterDeviceStateListener(DeviceStateListener listener) { @@ -144,6 +224,17 @@ public final class DeviceStateManagerGlobal { return -1; } + @Nullable + private IBinder findRequestTokenLocked(@NonNull DeviceStateRequest request) { + for (int i = 0; i < mRequests.size(); i++) { + if (mRequests.valueAt(i).mRequest.equals(request)) { + return mRequests.keyAt(i); + } + } + return null; + } + + /** Handles a call from the server that the device state has changed. */ private void handleDeviceStateChanged(int newDeviceState) { ArrayList<DeviceStateListenerWrapper> listeners; synchronized (mLock) { @@ -156,11 +247,68 @@ public final class DeviceStateManagerGlobal { } } + /** + * Handles a call from the server that a request for the supplied {@code token} has become + * active. + */ + private void handleRequestActive(IBinder token) { + DeviceStateRequestWrapper request; + synchronized (mLock) { + request = mRequests.get(token); + } + if (request != null) { + request.notifyRequestActive(); + } + } + + /** + * Handles a call from the server that a request for the supplied {@code token} has become + * suspended. + */ + private void handleRequestSuspended(IBinder token) { + DeviceStateRequestWrapper request; + synchronized (mLock) { + request = mRequests.get(token); + } + if (request != null) { + request.notifyRequestSuspended(); + } + } + + /** + * Handles a call from the server that a request for the supplied {@code token} has become + * canceled. + */ + private void handleRequestCanceled(IBinder token) { + DeviceStateRequestWrapper request; + synchronized (mLock) { + request = mRequests.remove(token); + } + if (request != null) { + request.notifyRequestCanceled(); + } + } + private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub { @Override public void onDeviceStateChanged(int deviceState) { handleDeviceStateChanged(deviceState); } + + @Override + public void onRequestActive(IBinder token) { + handleRequestActive(token); + } + + @Override + public void onRequestSuspended(IBinder token) { + handleRequestSuspended(token); + } + + @Override + public void onRequestCanceled(IBinder token) { + handleRequestCanceled(token); + } } private static final class DeviceStateListenerWrapper { @@ -176,4 +324,43 @@ public final class DeviceStateManagerGlobal { mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState)); } } + + private static final class DeviceStateRequestWrapper { + private final DeviceStateRequest mRequest; + @Nullable + private final DeviceStateRequest.Callback mCallback; + @Nullable + private final Executor mExecutor; + + DeviceStateRequestWrapper(@NonNull DeviceStateRequest request, + @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) { + mRequest = request; + mCallback = callback; + mExecutor = executor; + } + + void notifyRequestActive() { + if (mCallback == null) { + return; + } + + mExecutor.execute(() -> mCallback.onRequestActivated(mRequest)); + } + + void notifyRequestSuspended() { + if (mCallback == null) { + return; + } + + mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest)); + } + + void notifyRequestCanceled() { + if (mCallback == null) { + return; + } + + mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest)); + } + } } diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java new file mode 100644 index 000000000000..70f7002597ed --- /dev/null +++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java @@ -0,0 +1,163 @@ +/* + * 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.devicestate; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.TestApi; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; + +/** + * A request to alter the state of the device managed by {@link DeviceStateManager}. + * <p> + * Once constructed, a {@link DeviceStateRequest request} can be submitted with a call to + * {@link DeviceStateManager#requestState(DeviceStateRequest, Executor, + * DeviceStateRequest.Callback)}. + * <p> + * By default, the request is kept active until a call to + * {@link DeviceStateManager#cancelRequest(DeviceStateRequest)} or until one of the following + * occurs: + * <ul> + * <li>Another processes submits a request succeeding this request in which case the request + * will be suspended until the interrupting request is canceled. + * <li>The requested state has become unsupported. + * <li>The process submitting the request dies. + * </ul> + * However, this behavior can be changed by setting flags on the request. For example, the + * {@link #FLAG_CANCEL_WHEN_BASE_CHANGES} flag will extend this behavior to also cancel the + * request whenever the base (non-override) device state changes. + * + * @see DeviceStateManager + * + * @hide + */ +@TestApi +public final class DeviceStateRequest { + /** + * Flag that indicates the request should be canceled automatically when the base + * (non-override) device state changes. Useful when the requestor only wants the request to + * remain active while the base state remains constant and automatically cancel when the user + * manipulates the device into a different state. + */ + public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1 << 0; + + /** @hide */ + @IntDef(prefix = {"FLAG_"}, flag = true, value = { + FLAG_CANCEL_WHEN_BASE_CHANGES, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RequestFlags {} + + /** + * Creates a new {@link Builder} for a {@link DeviceStateRequest}. Must be one of the supported + * states for the device which can be queried with a call to + * {@link DeviceStateManager#getSupportedStates()}. + * + * @param requestedState the device state being requested. + */ + @NonNull + public static Builder newBuilder(int requestedState) { + return new Builder(requestedState); + } + + /** + * Builder for {@link DeviceStateRequest}. An instance can be obtained through + * {@link #newBuilder(int)}. + */ + public static final class Builder { + private final int mRequestedState; + private int mFlags; + + private Builder(int requestedState) { + mRequestedState = requestedState; + } + + /** + * Sets the flag bits provided within {@code flags} with all other bits remaining + * unchanged. + */ + @NonNull + public Builder setFlags(@RequestFlags int flags) { + mFlags |= flags; + return this; + } + + /** + * Returns a new {@link DeviceStateRequest} object whose state matches the state set on the + * builder. + */ + @NonNull + public DeviceStateRequest build() { + return new DeviceStateRequest(mRequestedState, mFlags); + } + } + + /** Callback to track the status of a request. */ + public interface Callback { + /** + * Called to indicate the request has become active and the device state will match the + * requested state. + * <p> + * Guaranteed to be called after a call to + * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)} with a state + * matching the requested state. + */ + default void onRequestActivated(@NonNull DeviceStateRequest request) {} + + /** + * Called to indicate the request has been temporarily suspended. + * <p> + * Guaranteed to be called before a call to + * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}. + */ + default void onRequestSuspended(@NonNull DeviceStateRequest request) {} + + /** + * Called to indicate the request has been canceled. The request can be resubmitted with + * another call to {@link DeviceStateManager#requestState(DeviceStateRequest, Executor, + * DeviceStateRequest.Callback)}. + * <p> + * Guaranteed to be called before a call to + * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}. + * <p> + * Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to + * occur before this method. + */ + default void onRequestCanceled(@NonNull DeviceStateRequest request) {} + } + + private final int mRequestedState; + @RequestFlags + private final int mFlags; + + private DeviceStateRequest(int requestedState, @RequestFlags int flags) { + mRequestedState = requestedState; + mFlags = flags; + } + + public int getState() { + return mRequestedState; + } + + @RequestFlags + public int getFlags() { + return mFlags; + } +} diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl index a157b3311ca5..323ad21e4884 100644 --- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl +++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl @@ -20,5 +20,45 @@ import android.hardware.devicestate.IDeviceStateManagerCallback; /** @hide */ interface IDeviceStateManager { + /** + * Registers a callback to receive notifications from the device state manager. Only one + * callback can be registered per-process. + * <p> + * As the callback mechanism is used to alert the caller of changes to request status a callback + * <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or + * {@link #cancelRequest(IBinder)}. Otherwise an exception will be thrown. + * + * @throws SecurityException if a callback is already registered for the calling process. + */ void registerCallback(in IDeviceStateManagerCallback callback); + + /** Returns the array of supported device state identifiers. */ + int[] getSupportedDeviceStates(); + + /** + * Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been + * previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a + * call to this method. + * + * @param token the request token previously registered with + * {@link #requestState(IBinder, int, int)} + * + * @throws IllegalStateException if a callback has not yet been registered for the calling + * process. + * @throws IllegalStateException if the supplied {@code token} has already been registered. + * @throws IllegalArgumentException if the supplied {@code state} is not supported. + */ + void requestState(IBinder token, int state, int flags); + + /** + * Cancels a request previously submitted with a call to + * {@link #requestState(IBinder, int, int)}. + * + * @param token the request token previously registered with + * {@link #requestState(IBinder, int, int)} + * + * @throws IllegalStateException if the supplied {@code token} has not been previously + * requested. + */ + void cancelRequest(IBinder token); } diff --git a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl index d1c581361b62..ee2a071741ef 100644 --- a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl +++ b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl @@ -18,5 +18,42 @@ package android.hardware.devicestate; /** @hide */ interface IDeviceStateManagerCallback { + /** + * Called in response to a change in device state. Guaranteed to be called once with the initial + * value on registration of the callback. + * + * @param deviceState the new state of the device. + */ oneway void onDeviceStateChanged(int deviceState); + + /** + * Called to notify the callback that a request has become active. Guaranteed to be called + * after a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming active + * resulted in a device state change. + * + * @param token the request token previously registered with + * {@link IDeviceStateManager#requestState(IBinder, int, int)} + */ + oneway void onRequestActive(IBinder token); + + /** + * Called to notify the callback that a request has become suspended. Guaranteed to be called + * before a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming + * suspended resulted in a device state change. + * + * @param token the request token previously registered with + * {@link IDeviceStateManager#requestState(IBinder, int, int)} + */ + oneway void onRequestSuspended(IBinder token); + + /** + * Called to notify the callback that a request has become canceled. No further callbacks will + * be triggered for this request. Guaranteed to be called before a subsequent call to + * {@link #onDeviceStateChanged(int)} if the request becoming canceled resulted in a device + * state change. + * + * @param token the request token previously registered with + * {@link IDeviceStateManager#requestState(IBinder, int, int)} + */ + oneway void onRequestCanceled(IBinder token); } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index d93286531465..188a2a47fca0 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -93,17 +93,26 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing private static final int MSG_UDFPS_POINTER_UP = 109; /** - * Request authentication with any single sensor. * @hide */ - public static final int SENSOR_ID_ANY = -1; + public static final int ENROLL_FIND_SENSOR = 1; + /** + * @hide + */ + public static final int ENROLL_ENROLL = 2; /** * @hide */ - @IntDef({SENSOR_ID_ANY}) + @IntDef({ENROLL_FIND_SENSOR, ENROLL_ENROLL}) @Retention(RetentionPolicy.SOURCE) - public @interface SensorId {} + public @interface EnrollReason {} + + /** + * Request authentication with any single sensor. + * @hide + */ + public static final int SENSOR_ID_ANY = -1; private IFingerprintService mService; private Context mContext; @@ -508,8 +517,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"); } @@ -590,7 +599,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(MANAGE_FINGERPRINT) public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId, - EnrollmentCallback callback, boolean shouldLogMetrics) { + EnrollmentCallback callback, @EnrollReason int enrollReason) { if (userId == UserHandle.USER_CURRENT) { userId = getCurrentUserId(); } @@ -611,7 +620,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing try { mEnrollmentCallback = callback; mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver, - mContext.getOpPackageName(), shouldLogMetrics); + mContext.getOpPackageName(), enrollReason); } catch (RemoteException e) { Slog.w(TAG, "Remote exception in enroll: ", e); // Though this may not be a hardware issue, it will cause apps to give up or try @@ -653,15 +662,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 +687,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 +1167,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/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java index f097651e1894..663a70452b24 100644 --- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java +++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java @@ -109,9 +109,10 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna this.sensorLocationY = props[1]; this.sensorRadius = props[2]; } else { - this.sensorLocationX = 0; - this.sensorLocationY = 0; - this.sensorRadius = 0; + // Fake coordinates that could be used for the fake UDFPS mode. + this.sensorLocationX = 540; + this.sensorLocationY = 1636; + this.sensorRadius = 130; } } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 3657a83039ad..8888247e2823 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -78,7 +78,7 @@ interface IFingerprintService { // Start fingerprint enrollment void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver, - String opPackageName, boolean shouldLogMetrics); + String opPackageName, int enrollReason); // Cancel enrollment in progress void cancelEnrollment(IBinder token); diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl index c093489d4494..81c7d894ee09 100644 --- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl +++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl @@ -21,10 +21,11 @@ package android.hardware.fingerprint; */ oneway interface IUdfpsOverlayController { const int REASON_UNKNOWN = 0; - const int REASON_ENROLL = 1; - const int REASON_AUTH_BP = 2; // BiometricPrompt - const int REASON_AUTH_FPM_KEYGUARD = 3; // FingerprintManager usage from Keyguard - const int REASON_AUTH_FPM_OTHER = 4; // Other FingerprintManager usage + const int REASON_ENROLL_FIND_SENSOR = 1; + const int REASON_ENROLL_ENROLLING = 2; + const int REASON_AUTH_BP = 3; // BiometricPrompt + const int REASON_AUTH_FPM_KEYGUARD = 4; // FingerprintManager usage from Keyguard + const int REASON_AUTH_FPM_OTHER = 5; // Other FingerprintManager usage // Shows the overlay. void showUdfpsOverlay(int sensorId, int reason); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 44a2e97e6f04..7e2be01feb01 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1732,7 +1732,7 @@ public class InputMethodService extends AbstractInputMethodService { // If app window has portrait orientation, regardless of what display orientation // is, IME shouldn't use fullscreen-mode. || (mInputEditorInfo.internalImeOptions - & EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT) != 0) { + & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0) { return false; } return true; diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java index 6a8e3f9c01f2..5e56164cc82c 100644 --- a/core/java/android/net/OemNetworkPreferences.java +++ b/core/java/android/net/OemNetworkPreferences.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -18,14 +18,14 @@ package android.net; import android.annotation.IntDef; import android.annotation.NonNull; +import android.os.Bundle; import android.os.Parcelable; -import android.util.SparseArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; /** @hide */ @@ -60,16 +60,16 @@ public final class OemNetworkPreferences implements Parcelable { public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4; @NonNull - private final SparseArray<List<String>> mNetworkMappings; + private final Bundle mNetworkMappings; @NonNull - public SparseArray<List<String>> getNetworkPreferences() { - return mNetworkMappings.clone(); + public Map<String, Integer> getNetworkPreferences() { + return convertToUnmodifiableMap(mNetworkMappings); } - private OemNetworkPreferences(@NonNull SparseArray<List<String>> networkMappings) { + private OemNetworkPreferences(@NonNull final Bundle networkMappings) { Objects.requireNonNull(networkMappings); - mNetworkMappings = networkMappings.clone(); + mNetworkMappings = (Bundle) networkMappings.clone(); } @Override @@ -99,26 +99,45 @@ public final class OemNetworkPreferences implements Parcelable { * @hide */ public static final class Builder { - private final SparseArray<List<String>> mNetworkMappings; + private final Bundle mNetworkMappings; public Builder() { - mNetworkMappings = new SparseArray<>(); + mNetworkMappings = new Bundle(); + } + + public Builder(@NonNull final OemNetworkPreferences preferences) { + Objects.requireNonNull(preferences); + mNetworkMappings = (Bundle) preferences.mNetworkMappings.clone(); } /** - * Add a network preference for a list of packages. + * Add a network preference for a given package. Previously stored values for the given + * package will be overwritten. * - * @param preference the desired network preference to use - * @param packages full package names (e.g.: "com.google.apps.contacts") for apps to use - * the given preference + * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app + * to use the given preference + * @param preference the desired network preference to use * @return The builder to facilitate chaining. */ @NonNull - public Builder addNetworkPreference(@OemNetworkPreference final int preference, - @NonNull List<String> packages) { - Objects.requireNonNull(packages); - mNetworkMappings.put(preference, - Collections.unmodifiableList(new ArrayList<>(packages))); + public Builder addNetworkPreference(@NonNull final String packageName, + @OemNetworkPreference final int preference) { + Objects.requireNonNull(packageName); + mNetworkMappings.putInt(packageName, preference); + return this; + } + + /** + * Remove a network preference for a given package. + * + * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app to + * remove a preference for. + * @return The builder to facilitate chaining. + */ + @NonNull + public Builder removeNetworkPreference(@NonNull final String packageName) { + Objects.requireNonNull(packageName); + mNetworkMappings.remove(packageName); return this; } @@ -131,6 +150,14 @@ public final class OemNetworkPreferences implements Parcelable { } } + private static Map<String, Integer> convertToUnmodifiableMap(@NonNull final Bundle bundle) { + final Map<String, Integer> networkPreferences = new HashMap<>(); + for (final String key : bundle.keySet()) { + networkPreferences.put(key, bundle.getInt(key)); + } + return Collections.unmodifiableMap(networkPreferences); + } + /** @hide */ @IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = { OEM_NETWORK_PREFERENCE_DEFAULT, @@ -168,7 +195,7 @@ public final class OemNetworkPreferences implements Parcelable { @Override public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { - dest.writeSparseArray(mNetworkMappings); + dest.writeBundle(mNetworkMappings); } @Override @@ -187,7 +214,7 @@ public final class OemNetworkPreferences implements Parcelable { @Override public OemNetworkPreferences createFromParcel(@NonNull android.os.Parcel in) { return new OemNetworkPreferences( - in.readSparseArray(getClass().getClassLoader())); + in.readBundle(getClass().getClassLoader())); } }; } diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index 7e50ebc419dd..2119e7b951cd 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -326,6 +326,19 @@ public final class UserHandle implements Parcelable { } /** + * Returns the uid that is composed from the userHandle and the appId. + * + * @param userHandle the UserHandle to compose the uid + * @param appId the AppId to compose the uid + * @return the uid that is composed from the userHandle and the appId + * @hide + */ + @SystemApi + public static int getUid(@NonNull UserHandle userHandle, @AppIdInt int appId) { + return getUid(userHandle.getIdentifier(), appId); + } + + /** * Returns the app id (or base uid) for a given uid, stripping out the user id from it. * @hide */ diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 7e7057fe56cb..0ff68fc582d8 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -304,20 +304,16 @@ public final class IncrementalManager { } /** - * Called when a callback wants to stop listen to the loading progress of an installed package. - * Decrease the count of the callbacks on the associated to the corresponding storage. - * If the count becomes zero, unregister the storage listener. + * Called to stop all listeners from listening to loading progress of an installed package. * @param codePath Path of the installed package - * @return True if the package name and associated storage id are valid. False otherwise. */ - public boolean unregisterLoadingProgressCallback(@NonNull String codePath, - @NonNull IPackageLoadingProgressCallback callback) { + public void unregisterLoadingProgressCallbacks(@NonNull String codePath) { final IncrementalStorage storage = openStorage(codePath); if (storage == null) { // storage does not exist, package not installed - return false; + return; } - return mLoadingProgressCallbacks.unregisterCallback(storage, callback); + mLoadingProgressCallbacks.cleanUpCallbacks(storage); } private static class LoadingProgressCallbacks extends IStorageLoadingProgressListener.Stub { @@ -325,7 +321,6 @@ public final class IncrementalManager { private final SparseArray<RemoteCallbackList<IPackageLoadingProgressCallback>> mCallbacks = new SparseArray<>(); - // TODO(b/165841827): disable callbacks when app state changes to fully loaded public void cleanUpCallbacks(@NonNull IncrementalStorage storage) { final int storageId = storage.getId(); final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage; diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index c39b8c5eb6d1..a79b197d3faa 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -130,6 +130,15 @@ public final class KeymasterDefs { public static final int KM_TAG_ASSOCIATED_DATA = Tag.ASSOCIATED_DATA; // KM_BYTES | 1000; public static final int KM_TAG_NONCE = Tag.NONCE; // KM_BYTES | 1001; public static final int KM_TAG_MAC_LENGTH = Tag.MAC_LENGTH; // KM_UINT | 1003; + public static final int KM_TAG_RESET_SINCE_ID_ROTATION = + Tag.RESET_SINCE_ID_ROTATION; // KM_BOOL | 1004 + public static final int KM_TAG_CONFIRMATION_TOKEN = Tag.CONFIRMATION_TOKEN; // KM_BYTES | 1005; + public static final int KM_TAG_CERTIFICATE_SERIAL = Tag.CERTIFICATE_SERIAL; // KM_UINT | 1006; + public static final int KM_TAG_CERTIFICATE_SUBJECT = Tag.CERTIFICATE_SUBJECT; // KM_UINT | 1007; + public static final int KM_TAG_CERTIFICATE_NOT_BEFORE = + Tag.CERTIFICATE_NOT_BEFORE; // KM_DATE | 1008; + public static final int KM_TAG_CERTIFICATE_NOT_AFTER = + Tag.CERTIFICATE_NOT_AFTER; // KM_DATE | 1009; // Algorithm values. public static final int KM_ALGORITHM_RSA = Algorithm.RSA; @@ -317,6 +326,10 @@ public final class KeymasterDefs { ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68; public static final int KM_ERROR_DEVICE_LOCKED = ErrorCode.DEVICE_LOCKED; // -72; + public static final int KM_ERROR_MISSING_NOT_BEFORE = + ErrorCode.MISSING_NOT_BEFORE; // -80; + public static final int KM_ERROR_MISSING_NOT_AFTER = + ErrorCode.MISSING_NOT_AFTER; // -80; public static final int KM_ERROR_UNIMPLEMENTED = ErrorCode.UNIMPLEMENTED; // -100; public static final int KM_ERROR_VERSION_MISMATCH = diff --git a/core/java/android/service/rotationresolver/RotationResolutionRequest.java b/core/java/android/service/rotationresolver/RotationResolutionRequest.java index 94a6052c246d..8e76e2fc9202 100644 --- a/core/java/android/service/rotationresolver/RotationResolutionRequest.java +++ b/core/java/android/service/rotationresolver/RotationResolutionRequest.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import android.view.Surface; /** * This class represents a request to an {@link RotationResolverService}. The request contains @@ -54,7 +55,7 @@ public final class RotationResolutionRequest implements Parcelable { mTimeoutMillis = timeoutMillis; } - public int getProposedRotation() { + @Surface.Rotation public int getProposedRotation() { return mProposedRotation; } diff --git a/core/java/android/service/rotationresolver/RotationResolverService.java b/core/java/android/service/rotationresolver/RotationResolverService.java index 593a642b4a38..604dd0ac8298 100644 --- a/core/java/android/service/rotationresolver/RotationResolverService.java +++ b/core/java/android/service/rotationresolver/RotationResolverService.java @@ -146,11 +146,8 @@ public abstract class RotationResolverService extends Service { } mPendingCallback = new RotationResolverCallbackWrapper(callback, this); mCancellationSignal = CancellationSignal.fromTransport(transport); - try { - onResolveRotation(request, mCancellationSignal, mPendingCallback); - } catch (UnsupportedOperationException e) { - reportFailures(callback, ROTATION_RESULT_FAILURE_CANCELLED); - } + + onResolveRotation(request, mCancellationSignal, mPendingCallback); } @MainThread diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 16b45c30b69f..7f45b384ca5f 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -475,9 +475,8 @@ public class DynamicLayout extends Layout { mObjects.insertAt(0, dirs); - final int baseLength = mBase.length(); - // Update from 0 characters to whatever the real text is - reflow(mBase, 0, 0, baseLength); + // Update from 0 characters to whatever the displayed text is + reflow(mBase, 0, 0, mDisplay.length()); if (mBase instanceof Spannable) { if (mWatcher == null) @@ -485,6 +484,7 @@ public class DynamicLayout extends Layout { // Strip out any watchers for other DynamicLayouts. final Spannable sp = (Spannable) mBase; + final int baseLength = mBase.length(); final ChangeWatcher[] spans = sp.getSpans(0, baseLength, ChangeWatcher.class); for (int i = 0; i < spans.length; i++) { sp.removeSpan(spans[i]); diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java index 5ac95d49c1bb..c0d818774ba0 100644 --- a/core/java/android/uwb/RangingManager.java +++ b/core/java/android/uwb/RangingManager.java @@ -94,7 +94,8 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { synchronized (this) { if (!hasSession(sessionHandle)) { Log.w(TAG, - "onRangingOpened - received unexpected SessionHandle: " + sessionHandle); + "onRangingOpenedFailed - received unexpected SessionHandle: " + + sessionHandle); return; } @@ -124,7 +125,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { @RangingChangeReason int reason, PersistableBundle params) { synchronized (this) { if (!hasSession(sessionHandle)) { - Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: " + Log.w(TAG, "onRangingReconfigureFailed - received unexpected SessionHandle: " + sessionHandle); return; } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index ae8afca9b5c5..62f4b864c7a8 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -124,16 +124,10 @@ interface IWindowManager * * @param token Token to be registered. * @param type Window type to be used with this token. - * @param options A bundle used to pass window-related options. * @param displayId The ID of the display where this token should be added. - * @param packageName The name of package to request to add window token. Could be {@code null} - * if callers holds the MANAGE_APP_TOKENS permission. - * @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code - * otherwise. + * @param options A bundle used to pass window-related options. */ - int addWindowTokenWithOptions(IBinder token, int type, int displayId, in Bundle options, - String packageName); - void addWindowToken(IBinder token, int type, int displayId); + void addWindowToken(IBinder token, int type, int displayId, in Bundle options); /** * Remove window token on a specific display. * @@ -784,6 +778,10 @@ interface IWindowManager /** * Registers a listener for a {@link android.app.WindowContext} to handle configuration changes * from the server side. + * <p> + * Note that this API should be invoked after calling + * {@link android.app.WindowTokenClient#attachContext(WindowContext)} + * </p> * * @param clientToken the window context's token * @param type Window type of the window context diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 749c0dfbaaaa..e1ccc51c71e1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8740,14 +8740,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Populates a {@link ViewStructure} for content capture. * - * <p>This method is called after a view is that is eligible for content capture - * (for example, if it {@link #isImportantForAutofill()}, an intelligence service is enabled for - * the user, and the activity rendering the view is enabled for content capture) is laid out and - * is visible. - * - * <p>The populated structure is then passed to the service through + * <p>This method is called after a view that is eligible for content capture + * (for example, if it {@link #isImportantForContentCapture()}, an intelligence service is + * enabled for the user, and the activity rendering the view is enabled for content capture) + * is laid out and is visible. The populated structure is then passed to the service through * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}. * + * <p>The default implementation of this method sets the most relevant properties based on + * related {@link View} methods, and views in the standard Android widgets library also + * override it to set their relevant properties. Therefore, if overriding this method, it + * is recommended to call {@code super.onProvideContentCaptureStructure()}. + * * <p><b>Note: </b>views that manage a virtual structure under this view must populate just * the node representing this view and return right away, then asynchronously report (not * necessarily in the UI thread) when the children nodes appear, disappear or have their text @@ -8755,7 +8758,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}, * {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and * {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)} - * respectively. The structure for the a child must be created using + * respectively. The structure for a child must be created using * {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the * {@code autofillId} for a child can be obtained either through * {@code childStructure.getAutofillId()} or @@ -8900,7 +8903,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Called when assist structure is being retrieved from a view as part of * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} to - * generate additional virtual structure under this view. The defaullt implementation + * generate additional virtual structure under this view. The default implementation * uses {@link #getAccessibilityNodeProvider()} to try to generate this from the * view's virtual accessibility nodes, if any. You can override this for a more * optimal implementation providing this data. diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 170124e348c9..47ac1ee5b339 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -139,7 +139,6 @@ public final class WindowManagerGlobal { public static final int ADD_INVALID_DISPLAY = -9; public static final int ADD_INVALID_TYPE = -10; public static final int ADD_INVALID_USER = -11; - public static final int ADD_TOO_MANY_TOKENS = -12; @UnsupportedAppUsage private static WindowManagerGlobal sDefaultWindowManager; diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 415b3a766d16..37220fe6870b 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -164,12 +164,12 @@ public class BaseInputConnection implements InputConnection { /** * Default implementation calls {@link #finishComposingText()} and - * {@code setImeTemporarilyConsumesInput(false)}. + * {@code setImeConsumesInput(false)}. */ @CallSuper public void closeConnection() { finishComposingText(); - setImeTemporarilyConsumesInput(false); + setImeConsumesInput(false); } /** diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index 2df75f6570db..bde4cb7c4fc3 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -299,7 +299,7 @@ public class EditorInfo implements InputType, Parcelable { * {@link EditorInfo} is using {@link Configuration#ORIENTATION_PORTRAIT} mode. * @hide */ - public static final int IME_FLAG_APP_WINDOW_PORTRAIT = 0x80000000; + public static final int IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT = 0x00000001; /** * Generic unspecified type for {@link #imeOptions}. @@ -321,7 +321,6 @@ public class EditorInfo implements InputType, Parcelable { * 1 1 IME_ACTION_NEXT * 11 IME_ACTION_DONE * 111 IME_ACTION_PREVIOUS - * 1 IME_FLAG_APP_WINDOW_PORTRAIT * 1 IME_FLAG_NO_PERSONALIZED_LEARNING * 1 IME_FLAG_NO_FULLSCREEN * 1 IME_FLAG_NAVIGATE_PREVIOUS @@ -356,7 +355,7 @@ public class EditorInfo implements InputType, Parcelable { * Masks for {@link internalImeOptions} * * <pre> - * 1 IME_FLAG_APP_WINDOW_PORTRAIT + * 1 IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT * |-------|-------|-------|-------|</pre> */ @@ -984,6 +983,7 @@ public class EditorInfo implements InputType, Parcelable { dest.writeInt(inputType); dest.writeInt(imeOptions); dest.writeString(privateImeOptions); + dest.writeInt(internalImeOptions); TextUtils.writeToParcel(actionLabel, dest, flags); dest.writeInt(actionId); dest.writeInt(initialSelStart); @@ -1019,6 +1019,7 @@ public class EditorInfo implements InputType, Parcelable { res.inputType = source.readInt(); res.imeOptions = source.readInt(); res.privateImeOptions = source.readString(); + res.internalImeOptions = source.readInt(); res.actionLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); res.actionId = source.readInt(); res.initialSelStart = source.readInt(); diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index f3111bdc7471..34a60bbe7642 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -1004,20 +1004,19 @@ public interface InputConnection { @Nullable Bundle opts); /** - * Called by the input method to indicate that it temporarily consumes all input for itself, - * or no longer does so. + * Called by the input method to indicate that it consumes all input for itself, or no longer + * does so. * - * <p>Editors should reflect that they are temporarily not receiving input by hiding the - * cursor if {@code imeTemporarilyConsumesInput} is {@code true}, and resume showing the - * cursor if it is {@code false}. + * <p>Editors should reflect that they are not receiving input by hiding the cursor if + * {@code imeConsumesInput} is {@code true}, and resume showing the cursor if it is + * {@code false}. * - * @param imeTemporarilyConsumesInput {@code true} when the IME is temporarily consuming input - * and the cursor should be hidden, {@code false} when input to the editor resumes and the - * cursor should be shown again. + * @param imeConsumesInput {@code true} when the IME is consuming input and the cursor should be + * hidden, {@code false} when input to the editor resumes and the cursor should be shown again. * @return {@code true} on success, {@code false} if the input connection is no longer valid, or * the protocol is not supported. */ - default boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + default boolean setImeConsumesInput(boolean imeConsumesInput) { return false; } } diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index b29149fc1fa0..b1501a4c035c 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -341,7 +341,7 @@ public class InputConnectionWrapper implements InputConnection { * @throws NullPointerException if the target is {@code null}. */ @Override - public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { - return mTarget.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + public boolean setImeConsumesInput(boolean imeConsumesInput) { + return mTarget.setImeConsumesInput(imeConsumesInput); } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 8cfbca88c596..fe37c5350511 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -497,9 +497,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private TextUtils.TruncateAt mEllipsize; // A flag to indicate the cursor was hidden by IME. - private boolean mImeTemporarilyConsumesInput; + private boolean mImeIsConsumingInput; - // Whether cursor is visible without regard to {@link mImeTemporarilyConsumesInput}. + // Whether cursor is visible without regard to {@link mImeConsumesInput}. // {code true} is the default value. private boolean mCursorVisibleFromAttr = true; @@ -8750,7 +8750,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } if (getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT) { - outAttrs.internalImeOptions |= EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT; + outAttrs.internalImeOptions |= EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT; } if (isMultilineInputType(outAttrs.inputType)) { // Multi-line text editors should always show an enter key. @@ -10506,8 +10506,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Set whether the cursor is visible. The default is true. Note that this property only - * makes sense for editable TextView. If IME is temporarily consuming the input, the cursor will - * be always invisible, visibility will be updated as the last state when IME does not consume + * makes sense for editable TextView. If IME is consuming the input, the cursor will always be + * invisible, visibility will be updated as the last state when IME does not consume * the input anymore. * * @see #isCursorVisible() @@ -10521,20 +10521,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Sets the IME is temporarily consuming the input and make the cursor invisible if - * {@code imeTemporarilyConsumesInput} is {@code true}. Otherwise, make the cursor visible. + * Sets the IME is consuming the input and make the cursor invisible if {@code imeConsumesInput} + * is {@code true}. Otherwise, make the cursor visible. * - * @param imeTemporarilyConsumesInput {@code true} if IME is temporarily consuming the input + * @param imeConsumesInput {@code true} if IME is consuming the input * * @hide */ - public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { - mImeTemporarilyConsumesInput = imeTemporarilyConsumesInput; + public void setImeConsumesInput(boolean imeConsumesInput) { + mImeIsConsumingInput = imeConsumesInput; updateCursorVisibleInternal(); } private void updateCursorVisibleInternal() { - boolean visible = mCursorVisibleFromAttr && !mImeTemporarilyConsumesInput; + boolean visible = mCursorVisibleFromAttr && !mImeIsConsumingInput; if (visible && mEditor == null) return; // visible is the default value with no edit data createEditorIfNeeded(); if (mEditor.mCursorVisible != visible) { @@ -10550,7 +10550,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * @return whether or not the cursor is visible (assuming this TextView is editable). This - * method may return {@code false} when the IME is temporarily consuming the input even if the + * method may return {@code false} when the IME is consuming the input even if the * {@code mEditor.mCursorVisible} attribute is {@code true} or {@code #setCursorVisible(true)} * is called. * diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index a5eb5f607c12..7aca36af919d 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -179,9 +179,10 @@ interface IPlatformCompat { * * @param changeId the ID of the change that was overridden * @param packageName the app package name that was overridden + * @return {@code true} if an override existed * @throws SecurityException if overriding changes is not permitted */ - void clearOverrideForTest(long changeId, String packageName); + boolean clearOverrideForTest(long changeId, String packageName); /** * Enables all compatibility changes that have enabledSinceTargetSdk == diff --git a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java index fa552e3603c6..50331e3338dc 100644 --- a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java +++ b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java @@ -24,10 +24,7 @@ public final class KernelCpuTotalBpfMapReader { } /** Returns whether total CPU time is measured. */ - public static boolean isSupported() { - // TODO(b/174245730): Implement this check. - return true; - } + public static native boolean isSupported(); /** Reads total CPU time from bpf map. */ public static native boolean read(Callback callback); diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index a4ce027501b8..585ddf6ddf98 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -492,10 +492,12 @@ class ZygoteServer { long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp; if (elapsedTimeMs >= mUsapPoolRefillDelayMs) { - // Normalize the poll timeout value when the time between one poll event and the - // next pushes us over the delay value. This prevents poll receiving a 0 - // timeout value, which would result in it returning immediately. - pollTimeoutMs = -1; + // The refill delay has elapsed during the period between poll invocations. + // We will now check for any currently ready file descriptors before refilling + // the USAP pool. + pollTimeoutMs = 0; + mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; + mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED; } else if (elapsedTimeMs <= 0) { // This can occur if the clock used by currentTimeMillis is reset, which is @@ -517,9 +519,11 @@ class ZygoteServer { } if (pollReturnValue == 0) { - // The poll timeout has been exceeded. This only occurs when we have finished the - // USAP pool refill delay period. - + // The poll returned zero results either when the timeout value has been exceeded + // or when a non-blocking poll is issued and no FDs are ready. In either case it + // is time to refill the pool. This will result in a duplicate assignment when + // the non-blocking poll returns zero results, but it avoids an additional + // conditional in the else branch. mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP; mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED; diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index e7b7bf4a5b52..19506a325d9a 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -79,7 +79,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_CLOSE_CONNECTION = 150; private static final int DO_COMMIT_CONTENT = 160; private static final int DO_GET_SURROUNDING_TEXT = 41; - private static final int DO_SET_IME_TEMPORARILY_CONSUMES_INPUT = 170; + private static final int DO_SET_IME_CONSUMES_INPUT = 170; @GuardedBy("mLock") @@ -268,13 +268,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } /** - * Dispatches the request for setting ime temporarily consumes input. + * Dispatches the request for setting ime consumes input. * - * <p>See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}. + * <p>See {@link InputConnection#setImeConsumesInput(boolean)}. */ - public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { - dispatchMessage(obtainMessageB(DO_SET_IME_TEMPORARILY_CONSUMES_INPUT, - imeTemporarilyConsumesInput)); + public void setImeConsumesInput(boolean imeConsumesInput) { + dispatchMessage(obtainMessageB(DO_SET_IME_CONSUMES_INPUT, imeConsumesInput)); } void dispatchMessage(Message msg) { @@ -822,17 +821,17 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } return; } - case DO_SET_IME_TEMPORARILY_CONSUMES_INPUT: { + case DO_SET_IME_CONSUMES_INPUT: { Trace.traceBegin(Trace.TRACE_TAG_INPUT, - "InputConnection#setImeTemporarilyConsumesInput"); + "InputConnection#setImeConsumesInput"); try { InputConnection ic = getInputConnection(); if (ic == null || !isActive()) { Log.w(TAG, - "setImeTemporarilyConsumesInput on inactive InputConnection"); + "setImeConsumesInput on inactive InputConnection"); return; } - ic.setImeTemporarilyConsumesInput(msg.arg1 == 1); + ic.setImeConsumesInput(msg.arg1 == 1); } finally { Trace.traceEnd(Trace.TRACE_TAG_INPUT); } diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index 586404c53f18..b06b4e5351c2 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -86,5 +86,5 @@ import com.android.internal.inputmethod.ISurroundingTextResultCallback; void getSurroundingText(int beforeLength, int afterLength, int flags, ISurroundingTextResultCallback callback); - void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput); + void setImeConsumesInput(boolean imeConsumesInput); } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 84c92ca83f36..0e9d13595124 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -525,12 +525,12 @@ public class InputConnectionWrapper implements InputConnection { } /** - * See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}. + * See {@link InputConnection#setImeConsumesInput(boolean)}. */ @AnyThread - public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + public boolean setImeConsumesInput(boolean imeConsumesInput) { try { - mIInputContext.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + mIInputContext.setImeConsumesInput(imeConsumesInput); return true; } catch (RemoteException e) { return false; diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index 4ccf9ce91f27..3d054a5e773c 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -245,11 +245,11 @@ public class EditableInputConnection extends BaseInputConnection } @Override - public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) { + public boolean setImeConsumesInput(boolean imeConsumesInput) { if (mTextView == null) { - return super.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + return super.setImeConsumesInput(imeConsumesInput); } - mTextView.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput); + mTextView.setImeConsumesInput(imeConsumesInput); return true; } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 0b48e72a4acd..8edc8a186c59 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -207,9 +207,9 @@ cc_library_shared { ], shared_libs: [ - "audioclient-types-aidl-unstable-cpp", - "audioflinger-aidl-unstable-cpp", - "av-types-aidl-unstable-cpp", + "audioclient-types-aidl-cpp", + "audioflinger-aidl-cpp", + "av-types-aidl-cpp", "libandroidicu", "libbpf_android", "libnetdbpf", diff --git a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp index 72492381e31a..d8446ca2881d 100644 --- a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp +++ b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp @@ -20,6 +20,10 @@ namespace android { +static jboolean KernelCpuTotalBpfMapReader_isSupported(JNIEnv *, jobject) { + return android::bpf::isTrackingUidTimesSupported() ? JNI_TRUE : JNI_FALSE; +} + static jboolean KernelCpuTotalBpfMapReader_read(JNIEnv *env, jobject, jobject callback) { jclass callbackClass = env->GetObjectClass(callback); jmethodID callbackMethod = env->GetMethodID(callbackClass, "accept", "(IIJ)V"); @@ -47,6 +51,7 @@ static jboolean KernelCpuTotalBpfMapReader_read(JNIEnv *env, jobject, jobject ca static const JNINativeMethod methods[] = { {"read", "(Lcom/android/internal/os/KernelCpuTotalBpfMapReader$Callback;)Z", (void *)KernelCpuTotalBpfMapReader_read}, + {"isSupported", "()Z", (void *)KernelCpuTotalBpfMapReader_isSupported}, }; int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv *env) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index dc81bc9d449d..827bf7b70cbc 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5427,7 +5427,7 @@ <permission android:name="android.permission.INPUT_CONSUMER" android:protectionLevel="signature" /> - <!-- @hide Allows an application to control the system's device state managed by the + <!-- @hide @TestApi Allows an application to control the system's device state managed by the {@link android.service.devicestate.DeviceStateManagerService}. For example, on foldable devices this would grant access to toggle between the folded and unfolded states. --> <permission android:name="android.permission.CONTROL_DEVICE_STATE" 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 80163b16ada0..ae5e2f597952 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2488,6 +2488,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" /> diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java index da7304efbd3d..48b58c6c0a1c 100644 --- a/core/tests/coretests/src/android/app/WindowContextTest.java +++ b/core/tests/coretests/src/android/app/WindowContextTest.java @@ -184,7 +184,8 @@ public class WindowContextTest { final IBinder token = windowContext.getWindowContextToken(); final IBinder existingToken = new Binder(); - mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId()); + mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId(), + null /* options */); final WindowManager.LayoutParams params = new WindowManager.LayoutParams(TYPE_INPUT_METHOD); diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index a4284a04310e..47ce2d87e69f 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -278,29 +278,29 @@ public class TextViewTest { @Test @UiThreadTest - public void setSetImeTemporarilyConsumesInput_recoveryToVisible() { + public void setSetImeConsumesInput_recoveryToVisible() { mTextView = new TextView(mActivity); mTextView.setCursorVisible(true); assertTrue(mTextView.isCursorVisible()); - mTextView.setImeTemporarilyConsumesInput(true); + mTextView.setImeConsumesInput(true); assertFalse(mTextView.isCursorVisible()); - mTextView.setImeTemporarilyConsumesInput(false); + mTextView.setImeConsumesInput(false); assertTrue(mTextView.isCursorVisible()); } @Test @UiThreadTest - public void setSetImeTemporarilyConsumesInput_recoveryToInvisible() { + public void setSetImeConsumesInput_recoveryToInvisible() { mTextView = new TextView(mActivity); mTextView.setCursorVisible(false); assertFalse(mTextView.isCursorVisible()); - mTextView.setImeTemporarilyConsumesInput(true); + mTextView.setImeConsumesInput(true); assertFalse(mTextView.isCursorVisible()); - mTextView.setImeTemporarilyConsumesInput(false); + mTextView.setImeConsumesInput(false); assertFalse(mTextView.isCursorVisible()); } diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java index 36f01f9a951d..e7fdfb8c19e9 100644 --- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java +++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java @@ -17,8 +17,11 @@ package android.hardware.devicestate; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; import android.annotation.Nullable; +import android.os.IBinder; import android.os.RemoteException; import androidx.test.filters.SmallTest; @@ -30,6 +33,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -41,6 +45,9 @@ import java.util.Set; @RunWith(JUnit4.class) @SmallTest public final class DeviceStateManagerGlobalTest { + private static final int DEFAULT_DEVICE_STATE = 0; + private static final int OTHER_DEVICE_STATE = 1; + private TestDeviceStateManagerService mService; private DeviceStateManagerGlobal mDeviceStateManagerGlobal; @@ -52,7 +59,7 @@ public final class DeviceStateManagerGlobalTest { @Test public void registerListener() { - mService.setDeviceState(0); + mService.setBaseState(DEFAULT_DEVICE_STATE); TestDeviceStateListener listener1 = new TestDeviceStateListener(); TestDeviceStateListener listener2 = new TestDeviceStateListener(); @@ -61,28 +68,58 @@ public final class DeviceStateManagerGlobalTest { ConcurrentUtils.DIRECT_EXECUTOR); mDeviceStateManagerGlobal.registerDeviceStateListener(listener2, ConcurrentUtils.DIRECT_EXECUTOR); - assertEquals(0, listener1.getLastReportedState().intValue()); - assertEquals(0, listener2.getLastReportedState().intValue()); + assertEquals(DEFAULT_DEVICE_STATE, listener1.getLastReportedState().intValue()); + assertEquals(DEFAULT_DEVICE_STATE, listener2.getLastReportedState().intValue()); - mService.setDeviceState(1); - assertEquals(1, listener1.getLastReportedState().intValue()); - assertEquals(1, listener2.getLastReportedState().intValue()); + mService.setBaseState(OTHER_DEVICE_STATE); + assertEquals(OTHER_DEVICE_STATE, listener1.getLastReportedState().intValue()); + assertEquals(OTHER_DEVICE_STATE, listener2.getLastReportedState().intValue()); } @Test public void unregisterListener() { - mService.setDeviceState(0); + mService.setBaseState(DEFAULT_DEVICE_STATE); TestDeviceStateListener listener = new TestDeviceStateListener(); mDeviceStateManagerGlobal.registerDeviceStateListener(listener, ConcurrentUtils.DIRECT_EXECUTOR); - assertEquals(0, listener.getLastReportedState().intValue()); + assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); mDeviceStateManagerGlobal.unregisterDeviceStateListener(listener); - mService.setDeviceState(1); - assertEquals(0, listener.getLastReportedState().intValue()); + mService.setBaseState(OTHER_DEVICE_STATE); + assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); + } + + @Test + public void submittingRequestRegisteredCallback() { + assertTrue(mService.mCallbacks.isEmpty()); + + DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build(); + mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); + + assertFalse(mService.mCallbacks.isEmpty()); + } + + @Test + public void submitRequest() { + mService.setBaseState(DEFAULT_DEVICE_STATE); + + TestDeviceStateListener listener = new TestDeviceStateListener(); + mDeviceStateManagerGlobal.registerDeviceStateListener(listener, + ConcurrentUtils.DIRECT_EXECUTOR); + + assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); + + DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build(); + mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); + + assertEquals(OTHER_DEVICE_STATE, listener.getLastReportedState().intValue()); + + mDeviceStateManagerGlobal.cancelRequest(request); + + assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); } private final class TestDeviceStateListener implements DeviceStateManager.DeviceStateListener { @@ -100,8 +137,23 @@ public final class DeviceStateManagerGlobalTest { } } - private final class TestDeviceStateManagerService extends IDeviceStateManager.Stub { - private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE; + private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub { + public static final class Request { + public final IBinder token; + public final int state; + public final int flags; + + private Request(IBinder token, int state, int flags) { + this.token = token; + this.state = state; + this.flags = flags; + } + } + + private int mBaseState = DEFAULT_DEVICE_STATE; + private int mMergedState = DEFAULT_DEVICE_STATE; + private ArrayList<Request> mRequests = new ArrayList<>(); + private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>(); @Override @@ -112,19 +164,86 @@ public final class DeviceStateManagerGlobalTest { mCallbacks.add(callback); try { - callback.onDeviceStateChanged(mDeviceState); + callback.onDeviceStateChanged(mMergedState); } catch (RemoteException e) { // Do nothing. Should never happen. } } - public void setDeviceState(int deviceState) { - boolean stateChanged = mDeviceState != deviceState; - mDeviceState = deviceState; - if (stateChanged) { + @Override + public int[] getSupportedDeviceStates() { + return new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE }; + } + + @Override + public void requestState(IBinder token, int state, int flags) { + if (!mRequests.isEmpty()) { + final Request topRequest = mRequests.get(mRequests.size() - 1); + for (IDeviceStateManagerCallback callback : mCallbacks) { + try { + callback.onRequestSuspended(topRequest.token); + } catch (RemoteException e) { + // Do nothing. Should never happen. + } + } + } + + final Request request = new Request(token, state, flags); + mRequests.add(request); + notifyStateChangedIfNeeded(); + + for (IDeviceStateManagerCallback callback : mCallbacks) { + try { + callback.onRequestActive(token); + } catch (RemoteException e) { + // Do nothing. Should never happen. + } + } + } + + @Override + public void cancelRequest(IBinder token) { + int index = -1; + for (int i = 0; i < mRequests.size(); i++) { + if (mRequests.get(i).token.equals(token)) { + index = i; + break; + } + } + + if (index == -1) { + throw new IllegalArgumentException("Unknown request: " + token); + } + + mRequests.remove(index); + for (IDeviceStateManagerCallback callback : mCallbacks) { + try { + callback.onRequestCanceled(token); + } catch (RemoteException e) { + // Do nothing. Should never happen. + } + } + notifyStateChangedIfNeeded(); + } + + public void setBaseState(int state) { + mBaseState = state; + notifyStateChangedIfNeeded(); + } + + private void notifyStateChangedIfNeeded() { + final int originalMergedState = mMergedState; + + if (!mRequests.isEmpty()) { + mMergedState = mRequests.get(mRequests.size() - 1).state; + } else { + mMergedState = mBaseState; + } + + if (mMergedState != originalMergedState) { for (IDeviceStateManagerCallback callback : mCallbacks) { try { - callback.onDeviceStateChanged(mDeviceState); + callback.onDeviceStateChanged(mMergedState); } catch (RemoteException e) { // Do nothing. Should never happen. } diff --git a/data/etc/platform.xml b/data/etc/platform.xml index b0ae9b98e7f5..ea42246e8262 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -165,6 +165,7 @@ <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" /> <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" /> <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="audioserver" /> + <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="audioserver" /> <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" /> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index cdc61a1c5752..bab36e61393a 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -469,6 +469,9 @@ applications that come with the platform <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/> <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/> <permission name="android.permission.MODIFY_QUIET_MODE" /> + <!-- Permission required for GTS test - GtsAssistIntentTestCases --> + <permission name="android.permission.MANAGE_SOUND_TRIGGER" /> + <permission name="android.permission.CAPTURE_AUDIO_HOTWORD" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index fa4f8b1674d1..3ebca6ad302d 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -20,6 +20,8 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.SystemApi; +import android.os.Process; import android.security.KeyStore; import android.security.keymaster.KeymasterDefs; @@ -874,9 +876,18 @@ public abstract class KeyProperties { * which it must be configured in SEPolicy. * @hide */ + @SystemApi public static final int NAMESPACE_APPLICATION = -1; /** + * The namespace identifier for the WIFI Keystore namespace. + * This must be kept in sync with system/sepolicy/private/keystore2_key_contexts + * @hide + */ + @SystemApi + public static final int NAMESPACE_WIFI = 102; + + /** * For legacy support, translate namespaces into known UIDs. * @hide */ @@ -884,6 +895,8 @@ public abstract class KeyProperties { switch (namespace) { case NAMESPACE_APPLICATION: return KeyStore.UID_SELF; + case NAMESPACE_WIFI: + return Process.WIFI_UID; // TODO Translate WIFI and VPN UIDs once the namespaces are defined. // b/171305388 and b/171305607 default: @@ -900,6 +913,8 @@ public abstract class KeyProperties { switch (uid) { case KeyStore.UID_SELF: return NAMESPACE_APPLICATION; + case Process.WIFI_UID: + return NAMESPACE_WIFI; // TODO Translate WIFI and VPN UIDs once the namespaces are defined. // b/171305388 and b/171305607 default: diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index 6a92980de37c..70e30d2de5a1 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -585,6 +585,30 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mSpec.getKeyValidityForConsumptionEnd() )); } + if (mSpec.getCertificateNotAfter() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_CERTIFICATE_NOT_AFTER, + mSpec.getCertificateNotAfter() + )); + } + if (mSpec.getCertificateNotBefore() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_CERTIFICATE_NOT_BEFORE, + mSpec.getCertificateNotBefore() + )); + } + if (mSpec.getCertificateSerialNumber() != null) { + params.add(KeyStore2ParameterUtils.makeBignum( + KeymasterDefs.KM_TAG_CERTIFICATE_SERIAL, + mSpec.getCertificateSerialNumber() + )); + } + if (mSpec.getCertificateSubject() != null) { + params.add(KeyStore2ParameterUtils.makeBytes( + KeymasterDefs.KM_TAG_CERTIFICATE_SUBJECT, + mSpec.getCertificateSubject().getEncoded() + )); + } if (mSpec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) { params.add(KeyStore2ParameterUtils.makeInt( diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index 75ac61a22cab..e1011155248e 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -352,14 +352,17 @@ public class AndroidKeyStoreProvider extends Provider { try { response = keyStore.getKeyEntry(descriptor); } catch (android.security.KeyStoreException e) { - if (e.getErrorCode() == ResponseCode.KEY_PERMANENTLY_INVALIDATED) { - throw new KeyPermanentlyInvalidatedException( - "User changed or deleted their auth credentials", - e); - } else { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Failed to obtain information about key") - .initCause(e); + switch (e.getErrorCode()) { + case ResponseCode.KEY_NOT_FOUND: + return null; + case ResponseCode.KEY_PERMANENTLY_INVALIDATED: + throw new KeyPermanentlyInvalidatedException( + "User changed or deleted their auth credentials", + e); + default: + throw (UnrecoverableKeyException) + new UnrecoverableKeyException("Failed to obtain information about key") + .initCause(e); } } diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java index 4c8ab8d6c713..dcdd7defd752 100644 --- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java +++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java @@ -28,6 +28,7 @@ import android.security.keystore.KeyProperties; import android.security.keystore.UserAuthArgs; import android.system.keystore2.Authorization; +import java.math.BigInteger; import java.security.ProviderException; import java.util.ArrayList; import java.util.Date; @@ -154,6 +155,23 @@ public abstract class KeyStore2ParameterUtils { } /** + * This function constructs a {@link KeyParameter} expressing a Bignum. + * @param tag Must be KeyMint tag with the associated type BIGNUM. + * @param b A BitInteger to be stored in the new key parameter. + * @return An instance of {@link KeyParameter}. + * @hide + */ + static @NonNull KeyParameter makeBignum(int tag, @NonNull BigInteger b) { + if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BIGNUM) { + throw new IllegalArgumentException("Not a bignum tag: " + tag); + } + KeyParameter p = new KeyParameter(); + p.tag = tag; + p.value = KeyParameterValue.blob(b.toByteArray()); + return p; + } + + /** * This function constructs a {@link KeyParameter} expressing date. * @param tag Must be KeyMint tag with the associated type DATE. * @param date A date @@ -167,10 +185,6 @@ public abstract class KeyStore2ParameterUtils { KeyParameter p = new KeyParameter(); p.tag = tag; p.value = KeyParameterValue.dateTime(date.getTime()); - if (p.value.getDateTime() < 0) { - throw new IllegalArgumentException("Date tag value out of range: " - + p.value.getDateTime()); - } return p; } /** diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp index a07723f39b0c..8fe6da3183f4 100644 --- a/libs/hwui/jni/fonts/FontFamily.cpp +++ b/libs/hwui/jni/fonts/FontFamily.cpp @@ -95,7 +95,7 @@ static jstring FontFamily_getLangTags(JNIEnv* env, jobject, jlong familyPtr) { } // CriticalNative -static jint FontFamily_getVariant(jlong familyPtr) { +static jint FontFamily_getVariant(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) { FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr); return static_cast<jint>(family->family->variant()); } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 08deb156a6cf..8d090f824e71 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1,5 +1,4 @@ /* -/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java index 812dac97bcd3..27f72687ccbe 100644 --- a/media/java/android/media/AudioPlaybackConfiguration.java +++ b/media/java/android/media/AudioPlaybackConfiguration.java @@ -20,6 +20,7 @@ import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL; import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -195,6 +196,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { private int mDeviceId; + private int mSessionId; + /** * Never use without initializing parameters afterwards */ @@ -207,7 +210,10 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @hide */ public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) { - if (DEBUG) { Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer); } + if (DEBUG) { + Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer + + " sessionId=" + pic.mSessionId); + } mPlayerIId = piid; mPlayerType = pic.mPlayerType; mClientUid = uid; @@ -220,6 +226,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { } else { mIPlayerShell = null; } + mSessionId = pic.mSessionId; } /** @@ -259,6 +266,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { anonymCopy.mClientUid = PLAYER_UPID_INVALID; anonymCopy.mClientPid = PLAYER_UPID_INVALID; anonymCopy.mIPlayerShell = null; + anonymCopy.mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE; return anonymCopy; } @@ -303,6 +311,17 @@ public final class AudioPlaybackConfiguration implements Parcelable { /** * @hide + * Return the audio session ID associated with this player. + * See {@link AudioManager#generateAudioSessionId()}. + * @return an audio session ID + */ + @SystemApi + public @IntRange(from = 0) int getSessionId() { + return mSessionId; + } + + /** + * @hide * Return the type of player linked to this configuration. * <br>Note that player types not exposed in the system API will be represented as * {@link #PLAYER_TYPE_UNKNOWN}. @@ -381,6 +400,17 @@ public final class AudioPlaybackConfiguration implements Parcelable { /** * @hide + * Handle a change of audio session Id + * @param sessionId the audio session ID + */ + public boolean handleSessionIdEvent(int sessionId) { + final boolean changed = sessionId != mSessionId; + mSessionId = sessionId; + return changed; + } + + /** + * @hide * Handle a player state change * @param event * @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID} @@ -476,7 +506,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public int hashCode() { - return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid); + return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid, + mSessionId); } @Override @@ -498,6 +529,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { ips = mIPlayerShell; } dest.writeStrongInterface(ips == null ? null : ips.getIPlayer()); + dest.writeInt(mSessionId); } private AudioPlaybackConfiguration(Parcel in) { @@ -510,6 +542,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in); final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder()); mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p); + mSessionId = in.readInt(); } @Override @@ -523,7 +556,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { && (mDeviceId == that.mDeviceId) && (mPlayerType == that.mPlayerType) && (mClientUid == that.mClientUid) - && (mClientPid == that.mClientPid)); + && (mClientPid == that.mClientPid)) + && (mSessionId == that.mSessionId); } @Override @@ -533,7 +567,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { + " type:" + toLogFriendlyPlayerType(mPlayerType) + " u/pid:" + mClientUid + "/" + mClientPid + " state:" + toLogFriendlyPlayerState(mPlayerState) - + " attr:" + mPlayerAttr; + + " attr:" + mPlayerAttr + + " sessionId:" + mSessionId; } //===================================================================== diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 175d36fedb1f..e056d435198a 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -836,7 +836,7 @@ public class AudioTrack extends PlayerBase mState = STATE_INITIALIZED; } - baseRegisterPlayer(); + baseRegisterPlayer(mSessionId); } /** @@ -866,7 +866,7 @@ public class AudioTrack extends PlayerBase // other initialization... if (nativeTrackInJavaObj != 0) { - baseRegisterPlayer(); + baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE); deferred_connect(nativeTrackInJavaObj); } else { mState = STATE_UNINITIALIZED; diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java index bbf632a406ec..e339ae8ef706 100644 --- a/media/java/android/media/HwAudioSource.java +++ b/media/java/android/media/HwAudioSource.java @@ -54,7 +54,7 @@ public class HwAudioSource extends PlayerBase { Preconditions.checkArgument(device.isSource(), "Requires a source device"); mAudioDeviceInfo = device; mAudioAttributes = attributes; - baseRegisterPlayer(); + baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE); } /** diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index dd42aab67c65..71ee57e3d471 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -73,6 +73,8 @@ interface IAudioService { oneway void releaseRecorder(in int riid); + oneway void playerSessionId(in int piid, in int sessionId); + // Java-only methods below. oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index dd0bc61c4225..ca0d29f2f47f 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -663,6 +663,10 @@ public class MediaPlayer extends PlayerBase * result in an exception.</p> */ public MediaPlayer() { + this(AudioSystem.AUDIO_SESSION_ALLOCATE); + } + + private MediaPlayer(int sessionId) { super(new AudioAttributes.Builder().build(), AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER); @@ -684,7 +688,7 @@ public class MediaPlayer extends PlayerBase native_setup(new WeakReference<MediaPlayer>(this), getCurrentOpPackageName()); - baseRegisterPlayer(); + baseRegisterPlayer(sessionId); } /* @@ -913,7 +917,7 @@ public class MediaPlayer extends PlayerBase AudioAttributes audioAttributes, int audioSessionId) { try { - MediaPlayer mp = new MediaPlayer(); + MediaPlayer mp = new MediaPlayer(audioSessionId); final AudioAttributes aa = audioAttributes != null ? audioAttributes : new AudioAttributes.Builder().build(); mp.setAudioAttributes(aa); @@ -978,7 +982,7 @@ public class MediaPlayer extends PlayerBase AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid); if (afd == null) return null; - MediaPlayer mp = new MediaPlayer(); + MediaPlayer mp = new MediaPlayer(audioSessionId); final AudioAttributes aa = audioAttributes != null ? audioAttributes : new AudioAttributes.Builder().build(); @@ -2365,7 +2369,13 @@ public class MediaPlayer extends PlayerBase * This method must be called before one of the overloaded <code> setDataSource </code> methods. * @throws IllegalStateException if it is called in an invalid state */ - public native void setAudioSessionId(int sessionId) throws IllegalArgumentException, IllegalStateException; + public void setAudioSessionId(int sessionId) + throws IllegalArgumentException, IllegalStateException { + native_setAudioSessionId(sessionId); + baseUpdateSessionId(sessionId); + } + + private native void native_setAudioSessionId(int sessionId); /** * Returns the audio session ID. diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java index 58ae279d4df1..4407efad6052 100644 --- a/media/java/android/media/PlayerBase.java +++ b/media/java/android/media/PlayerBase.java @@ -97,6 +97,7 @@ public abstract class PlayerBase { * Constructor. Must be given audio attributes, as they are required for AppOps. * @param attr non-null audio attributes * @param class non-null class of the implementation of this abstract class + * @param sessionId the audio session Id */ PlayerBase(@NonNull AudioAttributes attr, int implType) { if (attr == null) { @@ -110,7 +111,7 @@ public abstract class PlayerBase { /** * Call from derived class when instantiation / initialization is successful */ - protected void baseRegisterPlayer() { + protected void baseRegisterPlayer(int sessionId) { if (!USE_AUDIOFLINGER_MUTING_FOR_OP) { IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE); mAppOps = IAppOpsService.Stub.asInterface(b); @@ -128,7 +129,8 @@ public abstract class PlayerBase { } try { mPlayerIId = getService().trackPlayer( - new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this))); + new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this), + sessionId)); } catch (RemoteException e) { Log.e(TAG, "Error talking to audio service, player will not be tracked", e); } @@ -145,7 +147,7 @@ public abstract class PlayerBase { try { getService().playerAttributes(mPlayerIId, attr); } catch (RemoteException e) { - Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e); + Log.e(TAG, "Error talking to audio service, audio attributes will not be updated", e); } synchronized (mLock) { boolean attributesChanged = (mAttributes != attr); @@ -154,6 +156,18 @@ public abstract class PlayerBase { } } + /** + * To be called whenever the session ID of the player changes + * @param sessionId, the new session Id + */ + void baseUpdateSessionId(int sessionId) { + try { + getService().playerSessionId(mPlayerIId, sessionId); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to audio service, the session ID will not be updated", e); + } + } + void baseUpdateDeviceId(@Nullable AudioDeviceInfo deviceInfo) { int deviceId = 0; if (deviceInfo != null) { @@ -566,16 +580,19 @@ public abstract class PlayerBase { public static final int AUDIO_ATTRIBUTES_DEFINED = 1; public final AudioAttributes mAttributes; public final IPlayer mIPlayer; + public final int mSessionId; - PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer) { + PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer, + int sessionId) { mPlayerType = type; mAttributes = attr; mIPlayer = iplayer; + mSessionId = sessionId; } @Override public int hashCode() { - return Objects.hash(mPlayerType); + return Objects.hash(mPlayerType, mSessionId); } @Override @@ -588,6 +605,7 @@ public abstract class PlayerBase { dest.writeInt(mPlayerType); mAttributes.writeToParcel(dest, 0); dest.writeStrongBinder(mIPlayer == null ? null : mIPlayer.asBinder()); + dest.writeInt(mSessionId); } public static final @android.annotation.NonNull Parcelable.Creator<PlayerIdCard> CREATOR @@ -611,6 +629,7 @@ public abstract class PlayerBase { // IPlayer can be null if unmarshalling a Parcel coming from who knows where final IBinder b = in.readStrongBinder(); mIPlayer = (b == null ? null : IPlayer.Stub.asInterface(b)); + mSessionId = in.readInt(); } @Override @@ -621,7 +640,8 @@ public abstract class PlayerBase { PlayerIdCard that = (PlayerIdCard) o; // FIXME change to the binder player interface once supported as a member - return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes)); + return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes) + && (mSessionId == that.mSessionId)); } } diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java index 797caf36203b..32413dc6e841 100644 --- a/media/java/android/media/SoundPool.java +++ b/media/java/android/media/SoundPool.java @@ -155,7 +155,8 @@ public class SoundPool extends PlayerBase { } mAttributes = attributes; - baseRegisterPlayer(); + // FIXME: b/174876164 implement session id for soundpool + baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE); } /** diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 8525e9979aef..ee0be010c233 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -65,6 +65,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -245,6 +246,8 @@ public class Tuner implements AutoCloseable { private static final int MSG_ON_FILTER_STATUS = 3; private static final int MSG_ON_LNB_EVENT = 4; + private static final int FILTER_CLEANUP_THRESHOLD = 256; + /** @hide */ @IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK}) @Retention(RetentionPolicy.SOURCE) @@ -1208,6 +1211,15 @@ public class Tuner implements AutoCloseable { synchronized (mFilters) { WeakReference<Filter> weakFilter = new WeakReference<Filter>(filter); mFilters.add(weakFilter); + if (mFilters.size() > FILTER_CLEANUP_THRESHOLD) { + Iterator<WeakReference<Filter>> iterator = mFilters.iterator(); + while (iterator.hasNext()) { + WeakReference<Filter> wFilter = iterator.next(); + if (wFilter.get() == null) { + iterator.remove(); + } + } + } } } return filter; diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 67a2c49e5746..65b64d7e8df3 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -31,8 +31,8 @@ cc_library_shared { ], shared_libs: [ - "audioclient-types-aidl-unstable-cpp", - "av-types-aidl-unstable-cpp", + "audioclient-types-aidl-cpp", + "av-types-aidl-cpp", "libandroid_runtime", "libaudioclient", "libnativehelper", diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index bd8d2e9f77a4..98ac5b983098 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -1409,7 +1409,7 @@ static const JNINativeMethod gMethods[] = { {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup}, {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id}, - {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id}, + {"native_setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id}, {"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel}, {"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect}, {"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData}, diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp index 45f42f1b5dc6..48d738039696 100644 --- a/native/android/system_fonts.cpp +++ b/native/android/system_fonts.cpp @@ -264,7 +264,7 @@ AFont* _Nonnull AFontMatcher_match( static_cast<minikin::FamilyVariant>(matcher->mFamilyVariant), 1 /* maxRun */); - const minikin::Font* font = runs[0].fakedFont.font; + const std::shared_ptr<minikin::Font>& font = runs[0].fakedFont.font; std::unique_ptr<AFont> result = std::make_unique<AFont>(); const android::MinikinFontSkia* minikinFontSkia = reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get()); diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index fa3213d54281..3b054e942178 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -4845,9 +4845,13 @@ public class ConnectivityManager { } } - private void setOemNetworkPreference(@NonNull OemNetworkPreferences preference) { - Log.d(TAG, "setOemNetworkPreference called with preference: " - + preference.toString()); + private void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) { + try { + mService.setOemNetworkPreference(preference); + } catch (RemoteException e) { + Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString()); + throw e.rethrowFromSystemServer(); + } } @NonNull diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index db8b7ed3b177..e2672c480c10 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -29,6 +29,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.NetworkState; +import android.net.OemNetworkPreferences; import android.net.ProxyInfo; import android.net.UidRange; import android.net.QosSocketInfo; @@ -240,4 +241,6 @@ interface IConnectivityManager void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback); void unregisterQosCallback(in IQosCallback callback); + + void setOemNetworkPreference(in OemNetworkPreferences preference); } diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index c4d1b09a5c20..b9ef4c21ef6f 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -435,25 +435,7 @@ public class NetworkRequest implements Parcelable { * @hide */ public boolean isRequest() { - return isForegroundRequest() || isBackgroundRequest(); - } - - /** - * Returns true iff. the contained NetworkRequest is one that: - * - * - should be associated with at most one satisfying network - * at a time; - * - * - should cause a network to be kept up and in the foreground if - * it is the best network which can satisfy the NetworkRequest. - * - * For full detail of how isRequest() is used for pairing Networks with - * NetworkRequests read rematchNetworkAndRequests(). - * - * @hide - */ - public boolean isForegroundRequest() { - return type == Type.TRACK_DEFAULT || type == Type.REQUEST; + return type == Type.REQUEST || type == Type.BACKGROUND_REQUEST; } /** diff --git a/packages/Connectivity/framework/src/android/net/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java index 03cfbbb4a22d..77c8a4f4579b 100644 --- a/packages/Connectivity/framework/src/android/net/Proxy.java +++ b/packages/Connectivity/framework/src/android/net/Proxy.java @@ -32,8 +32,6 @@ import java.net.InetSocketAddress; import java.net.ProxySelector; import java.net.URI; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * A convenience class for accessing the user and default proxy @@ -66,40 +64,9 @@ public final class Proxy { @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO"; - /** @hide */ - public static final int PROXY_VALID = 0; - /** @hide */ - public static final int PROXY_HOSTNAME_EMPTY = 1; - /** @hide */ - public static final int PROXY_HOSTNAME_INVALID = 2; - /** @hide */ - public static final int PROXY_PORT_EMPTY = 3; - /** @hide */ - public static final int PROXY_PORT_INVALID = 4; - /** @hide */ - public static final int PROXY_EXCLLIST_INVALID = 5; - private static ConnectivityManager sConnectivityManager = null; - // Hostname / IP REGEX validation - // Matches blank input, ips, and domain names - private static final String NAME_IP_REGEX = - "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*"; - - private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$"; - - private static final Pattern HOSTNAME_PATTERN; - - private static final String EXCL_REGEX = - "[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*(\\.[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*)*"; - - private static final String EXCLLIST_REGEXP = "^$|^" + EXCL_REGEX + "(," + EXCL_REGEX + ")*$"; - - private static final Pattern EXCLLIST_PATTERN; - static { - HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP); - EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP); sDefaultProxySelector = ProxySelector.getDefault(); } @@ -218,33 +185,6 @@ public final class Proxy { return false; } - /** - * Validate syntax of hostname, port and exclusion list entries - * {@hide} - */ - public static int validate(String hostname, String port, String exclList) { - Matcher match = HOSTNAME_PATTERN.matcher(hostname); - Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList); - - if (!match.matches()) return PROXY_HOSTNAME_INVALID; - - if (!listMatch.matches()) return PROXY_EXCLLIST_INVALID; - - if (hostname.length() > 0 && port.length() == 0) return PROXY_PORT_EMPTY; - - if (port.length() > 0) { - if (hostname.length() == 0) return PROXY_HOSTNAME_EMPTY; - int portVal = -1; - try { - portVal = Integer.parseInt(port); - } catch (NumberFormatException ex) { - return PROXY_PORT_INVALID; - } - if (portVal <= 0 || portVal > 0xFFFF) return PROXY_PORT_INVALID; - } - return PROXY_VALID; - } - /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Deprecated diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java index 9c9fed102828..229db0d717cd 100644 --- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java +++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java @@ -23,6 +23,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import com.android.net.module.util.ProxyUtils; + import java.net.InetSocketAddress; import java.net.URLConnection; import java.util.List; @@ -233,7 +235,7 @@ public class ProxyInfo implements Parcelable { */ public boolean isValid() { if (!Uri.EMPTY.equals(mPacFileUrl)) return true; - return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost, + return ProxyUtils.PROXY_VALID == ProxyUtils.validate(mHost == null ? "" : mHost, mPort == 0 ? "" : Integer.toString(mPort), mExclusionList == null ? "" : mExclusionList); } diff --git a/packages/Connectivity/framework/src/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java index c87b8279c4d6..1812509ba6d2 100644 --- a/packages/Connectivity/framework/src/android/net/VpnManager.java +++ b/packages/Connectivity/framework/src/android/net/VpnManager.java @@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.Activity; import android.content.ComponentName; import android.content.Context; @@ -28,6 +29,8 @@ import android.content.Intent; import android.content.res.Resources; import android.os.RemoteException; +import com.android.internal.net.LegacyVpnInfo; +import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; import java.io.IOException; @@ -161,4 +164,104 @@ public class VpnManager { throw e.rethrowFromSystemServer(); } } -} + + /** + * Return the VPN configuration for the given user ID. + * @hide + */ + @Nullable + public VpnConfig getVpnConfig(@UserIdInt int userId) { + try { + return mService.getVpnConfig(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Prepare for a VPN application. + * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId}, + * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required. + * + * @param oldPackage Package name of the application which currently controls VPN, which will + * be replaced. If there is no such application, this should should either be + * {@code null} or {@link VpnConfig.LEGACY_VPN}. + * @param newPackage Package name of the application which should gain control of VPN, or + * {@code null} to disable. + * @param userId User for whom to prepare the new VPN. + * + * @hide + */ + public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage, + int userId) { + try { + return mService.prepareVpn(oldPackage, newPackage, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set whether the VPN package has the ability to launch VPNs without user intervention. This + * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn} + * class. If the caller is not {@code userId}, {@link + * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required. + * + * @param packageName The package for which authorization state should change. + * @param userId User for whom {@code packageName} is installed. + * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN + * permissions should be granted. When unauthorizing an app, {@link + * VpnManager.TYPE_VPN_NONE} should be used. + * @hide + */ + public void setVpnPackageAuthorization( + String packageName, int userId, @VpnManager.VpnType int vpnType) { + try { + mService.setVpnPackageAuthorization(packageName, userId, vpnType); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return the legacy VPN information for the specified user ID. + * @hide + */ + public LegacyVpnInfo getLegacyVpnInfo(@UserIdInt int userId) { + try { + return mService.getLegacyVpnInfo(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Starts a legacy VPN. + * @hide + */ + public void startLegacyVpn(VpnProfile profile) { + try { + mService.startLegacyVpn(profile); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Informs the service that legacy lockdown VPN state should be updated (e.g., if its keystore + * entry has been updated). If the LockdownVpn mechanism is enabled, updates the vpn + * with a reload of its profile. + * + * <p>This method can only be called by the system UID + * @return a boolean indicating success + * + * @hide + */ + public boolean updateLockdownVpn() { + try { + return mService.updateLockdownVpn(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 3837743a0ff8..889980a05bfa 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sal nie outomaties koppel nie"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang nie"</string> <string name="saved_network" msgid="7143698034077223645">"Gestoor deur <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Gekoppel aan beperkte netwerk"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Outomaties deur %1$s gekoppel"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Outomaties deur netwerkgraderingverskaffer gekoppel"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Gekoppel via %1$s"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 4ce01d68a5ae..c41e4d5d5869 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"በራስ-ሰር አይገናኝም"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ምንም የበይነመረብ መዳረሻ ያለም"</string> <string name="saved_network" msgid="7143698034077223645">"የተቀመጠው በ<xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ከሚለካ አውታረ መረብ ጋር ተገናኝቷል"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"በ%1$s በኩል በራስ-ሰር ተገናኝቷል"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"በአውታረ መረብ ደረጃ ሰጪ አቅራቢ በኩል በራስ-ሰር ተገናኝቷል"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"በ%1$s በኩል መገናኘት"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 2580d0fc82cc..f8d1d576c759 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"لن يتم الاتصال تلقائيًا"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"لا يتوفّر اتصال بالإنترنت"</string> <string name="saved_network" msgid="7143698034077223645">"تم الحفظ بواسطة <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"تم الاتصال بشبكة تفرض تكلفة استخدام."</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"تم الاتصال تلقائيًا عبر %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"تم الاتصال تلقائيًا عبر مقدم خدمة تقييم الشبكة"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"تم الاتصال عبر %1$s"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index b61ff508cfe7..c6078f898c3e 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"স্বয়ংক্ৰিয়ভাৱে সংযোগ নহ’ব"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ইণ্টাৰনেট সংযোগ নাই"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>এ ছেভ কৰিছে"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"নিৰিখ-নিৰ্দিষ্ট নেটৱৰ্কৰ সৈতে সংযোগ কৰা হৈছে"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s মাধ্যমেদি স্বয়ংক্ৰিয়ভাৱে সংযোগ কৰা হৈছে"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটৱৰ্ক ৰেটিং প্ৰদানকাৰীৰ জৰিয়তে স্বয়ং সংয়োগ কৰা হ’ল"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ৰ মাধ্যমেদি সংযোগ কৰা হৈছে"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index d06377677a50..d3e0a25593e4 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik qoşulmayacaq"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"İnternet girişi yoxdur"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tərəfindən saxlandı"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ölçülən şəbəkəyə qoşulub"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzərindən avtomatik qoşuldu"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Avtomatik olaraq şəbəkə reytinq provayderi ilə qoşuludur"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s vasitəsilə qoşuludur"</string> @@ -313,7 +312,7 @@ <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Adsız Bluetooth cihazları (yalnız MAC ünvanları) göstəriləcək"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Uzaqdan idarə olunan cihazlarda dözülməz yüksək səs həcmi və ya nəzarət çatışmazlığı kimi səs problemləri olduqda Bluetooth mütləq səs həcmi xüsusiyyətini deaktiv edir."</string> <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche funksiyasını aktiv edir."</string> - <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Təkmilləşdirilmiş Bağlantı funksiyasını aktiv edir."</string> + <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Qabaqcıl məlumat mübadiləsini aktiv edir."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Yerli terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Yerli örtük girişini təklif edən terminal tətbiqi aktiv edin"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP yoxlanılır"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 2976bb5893b6..85412224e995 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automatsko povezivanje nije uspelo"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Sačuvao/la je <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezani ste na mrežu sa ograničenjem"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano preko %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano preko dobavljača ocene mreže"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Veza je uspostavljena preko pristupne tačke %1$s"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 7af9e9323e05..aab600f3330e 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не будзе аўтаматычна падключацца"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Няма доступу да інтэрнэту"</string> <string name="saved_network" msgid="7143698034077223645">"Захавана праз: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Падключана да сеткі з падлікам трафіка"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Аўтаматычна падключана праз %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аўтаматычна падключана праз пастаўшчыка паслугі ацэнкі сеткі"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Падключана праз %1$s"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 77b6493931f7..92f293599f97 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Няма да се свърже автоматично"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Няма достъп до интернет"</string> <string name="saved_network" msgid="7143698034077223645">"Запазено от <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Установена е връзка с мрежа с отчитане"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично е установена връзка чрез %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично е установена връзка чрез доставчик на услуги за оценяване на мрежите"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Установена е връзка през „%1$s“"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 819625b67725..b40e24e3a672 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"অটোমেটিক কানেক্ট করবে না"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ইন্টারনেট অ্যাক্সেস নেই"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> দ্বারা সেভ করা"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"মিটার্ড নেটওয়ার্কের সঙ্গে কানেক্ট করা"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"স্বয়ংক্রিয়ভাবে %1$s এর মাধ্যমে কানেক্ট হয়েছে"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটওয়ার্কের রেটিং প্রদানকারীর মাধ্যমে অটোমেটিক কানেক্ট"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s মাধ্যমে কানেক্ট হয়েছে"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 193ac6066e40..cec2e4533878 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se automatski povezati"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Sačuvano: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezani ste s mrežom s naplatom"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano koristeći %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano putem ocjenjivača mreže"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Povezani preko %1$s"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index fef9bfb5c8c2..6134da873a0e 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No es connectarà automàticament"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No hi ha accés a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Desada per <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connectat a una xarxa d\'ús mesurat"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Connectada automàticament a través de: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connectada automàticament a través d\'un proveïdor de valoració de xarxes"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connectada mitjançant %1$s"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 281a78850e00..f29a3dd54ff8 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Připojení nebude automaticky navázáno"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nebyl zjištěn žádný přístup k internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Uloženo uživatelem <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Připojeno k měřené síti"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky připojeno přes poskytovatele %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky připojeno přes poskytovatele hodnocení sítí"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Připojeno prostřednictvím %1$s"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 69cc8d805091..d92d41d536bc 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Der oprettes ikke automatisk forbindelse"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetadgang"</string> <string name="saved_network" msgid="7143698034077223645">"Gemt af <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Der er oprettet forbindelse til det forbrugsbaserede netværk"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilsluttet via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk forbundet via udbyder af netværksvurdering"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Tilsluttet via %1$s"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 737ea167a8c9..ac05b620539c 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kein automatischer Verbindungsaufbau"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Kein Internetzugriff"</string> <string name="saved_network" msgid="7143698034077223645">"Gespeichert von <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Mit kostenpflichtigem Netzwerk verbunden"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch über %1$s verbunden"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch über Anbieter von Netzwerkbewertungen verbunden"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Über %1$s verbunden"</string> @@ -287,7 +286,7 @@ <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Verringert den Akkuverbrauch und verbessert die Netzwerkleistung"</string> <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Wenn dieser Modus aktiviert ist, kann sich die MAC-Adresse dieses Geräts bei jeder Verbindung mit einem Netzwerk ändern, bei dem die MAC-Adressen randomisiert werden."</string> <string name="wifi_metered_label" msgid="8737187690304098638">"Kostenpflichtig"</string> - <string name="wifi_unmetered_label" msgid="6174142840934095093">"Kostenlos"</string> + <string name="wifi_unmetered_label" msgid="6174142840934095093">"Ohne Datenlimit"</string> <string name="select_logd_size_title" msgid="1604578195914595173">"Logger-Puffergrößen"</string> <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Größe pro Protokollpuffer wählen"</string> <string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Speicher der dauerhaften Protokollierung löschen?"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 8e1d5e3d1b18..0b11d69132c5 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Δεν θα συνδεθεί αυτόματα"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Δεν υπάρχει πρόσβαση στο διαδίκτυο"</string> <string name="saved_network" msgid="7143698034077223645">"Αποθηκεύτηκε από <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Σύνδεση σε δίκτυο με ογκοχρέωση"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Συνδέθηκε αυτόματα μέσω %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Συνδέθηκε αυτόματα μέσω παρόχου αξιολόγησης δικτύου"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Συνδέθηκε μέσω %1$s"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index f79072fff1e9..1da8e37186c6 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se conectará automáticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No hay acceso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a la red de uso medido"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conexión automática mediante %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente mediante proveedor de calificación de red"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conexión a través de %1$s"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index f99a3f12307b..54226ce0fe5c 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se establecerá conexión automáticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No se ha detectado ningún acceso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a una red de uso medido"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectada automáticamente a través de %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente a través de un proveedor de valoración de redes"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index fa2aa467d0c5..4c747e36ef52 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automaatselt ei ühendata"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Juurdepääs Internetile puudub"</string> <string name="saved_network" msgid="7143698034077223645">"Salvestas: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ühendatud mahupõhise võrguga"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ühendus loodi automaatselt teenusega %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ühendus loodi automaatselt võrgukvaliteedi hinnangute pakkuja kaudu"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Ühendatud üksuse %1$s kaudu"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 2c4a8ee09f5e..6016cd20ff37 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ez da konektatuko automatikoki"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ezin da konektatu Internetera"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioak gorde du"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Sare neurtu batera konektatuta"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s bidez automatikoki konektatuta"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikoki konektatuta sare-balorazioen hornitzailearen bidez"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s bidez konektatuta"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 1c6ca763197a..e85557000a61 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -36,10 +36,9 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"اتصال بهصورت خودکار انجام نمیشود"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"دسترسی به اینترنت ندارد"</string> <string name="saved_network" msgid="7143698034077223645">"ذخیرهشده توسط <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"اتصال به شبکه محدود برقرار شد"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"اتصال خودکار ازطریق %1$s"</string> - <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائهدهنده رتبهبندی شبکه"</string> + <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائهدهنده ردهبندی شبکه"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"متصل از طریق %1$s"</string> <string name="connected_via_app" msgid="3532267661404276584">"متصل شده ازطریق <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="available_via_passpoint" msgid="1716000261192603682">"در دسترس از طریق %1$s"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 04c9130ec31f..724e52a95927 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Yhteyttä ei muodosteta automaattisesti"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ei internetyhteyttä"</string> <string name="saved_network" msgid="7143698034077223645">"Tallentaja: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Yhdistetty maksulliseen verkkoon"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaattinen yhteys muodostettu palvelun %1$s kautta"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Yhdistetty automaattisesti verkon arviointipalvelun kautta"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Yhdistetty seuraavan kautta: %1$s"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index aa0cd2a684c7..74c3005b0228 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Enregistrés par <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Appareil connecté à un réseau mesuré"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiquement connecté par %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement par le fournisseur d\'avis sur le réseau"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté par %1$s"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index dbdc160030fa..c9651ce570c5 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Enregistré lors de : <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connecté au réseau facturé à l\'usage"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Connecté automatiquement via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement via un fournisseur d\'évaluation de l\'état du réseau"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté via %1$s"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 90c130301a8f..f499f587a850 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non se conectará automaticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Estableceuse conexión coa rede de pago por consumo"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 4caeda279849..23ee1de46484 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ઑટોમૅટિક રીતે કનેક્ટ કરશે નહીં"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"કોઈ ઇન્ટરનેટ ઍક્સેસ નથી"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા સચવાયું"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"મીટર્ડ (ડેટા નિયંત્રણ) નેટવર્ક સાથે કનેક્ટેડ છે"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s દ્વારા સ્વત: કનેક્ટ થયેલ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"નેટવર્ક રેટિંગ પ્રદાતા દ્વારા ઑટોમૅટિક રીતે કનેક્ટ થયું"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s દ્વારા કનેક્ટ થયેલ"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 468808be918a..a2fc43c250cc 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"अपने आप कनेक्ट नहीं होगा"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट नहीं है"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> के द्वारा सहेजा गया"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"सीमित डेटा वाले नेटवर्क से कनेक्ट किया गया"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s के ज़रिए ऑटोमैटिक रूप से कनेक्ट है"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग कंपनी के ज़रिए अपने आप कनेक्ट है"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s के द्वारा उपलब्ध"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 932c2560f2d9..396051d51a09 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se povezati automatski"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Spremila aplik. <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezano s mrežom s ograničenim prometom"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezan putem %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezan putem ocjenjivača mreže"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Povezano putem %1$s"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index fbaffac6b083..0c9bc54c0861 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nem csatlakozik automatikusan"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nincs internet-hozzáférés"</string> <string name="saved_network" msgid="7143698034077223645">"Mentette: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Forgalomkorlátos hálózathoz csatlakozva"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatikusan csatlakozott a következőn keresztül: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikusan csatlakozott a hálózatértékelés szolgáltatóján keresztül"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Csatlakozva a következőn keresztül: %1$s"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 224d6418b5d5..56c93b635e20 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Չի միանա ավտոմատ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ինտերնետ կապ չկա"</string> <string name="saved_network" msgid="7143698034077223645">"Ով է պահել՝ <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Միացած է վճարովի թրաֆիկով ցանցի"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ավտոմատ կերպով կապակցվել է %1$s-ի միջոցով"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ավտոմատ միացել է ցանցերի վարկանիշի մատակարարի միջոցով"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Միացված է %1$s-ի միջոցով"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 8cf13cde6a42..0440f4796143 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan tersambung otomatis"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Tidak ada akses internet"</string> <string name="saved_network" msgid="7143698034077223645">"Disimpan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Terhubung ke jaringan berbayar"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Tersambung otomatis melalui %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Otomatis tersambung melalui penyedia rating jaringan"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Terhubung melalui %1$s"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index aa8893e4783a..0bb294f5869b 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Mun ekki tengjast sjálfkrafa"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Enginn netaðgangur"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> vistaði"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Tengdist neti með mældri notkun"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Sjálfkrafa tengt um %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Sjálfkrafa tengt um netgæðaveitu"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Tengt í gegnum %1$s"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 0199f549d099..54049d79a76b 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non verrà eseguita la connessione automatica"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nessun accesso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Salvata da <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connessione a rete a consumo effettuata"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Collegato automaticamente tramite %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Collegato automaticamente tramite fornitore di servizi di valutazione rete"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Collegato tramite %1$s"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index a50a22d2d08d..6cc4347946f4 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"לא יתבצע חיבור באופן אוטומטי"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"אין גישה לאינטרנט"</string> <string name="saved_network" msgid="7143698034077223645">"נשמר על ידי <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"יש חיבור לרשת המבוססת על חיוב לפי שימוש בנתונים"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"מחובר אוטומטית דרך %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"מחובר אוטומטית דרך ספק של דירוג רשת"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"מחובר דרך %1$s"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index a1d1b70d607e..ec674adb75d0 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"自動的に接続されません"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"インターネット接続なし"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>で保存"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"従量制ネットワークに接続しました"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s 経由で自動的に接続しています"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ネットワーク評価プロバイダ経由で自動的に接続しています"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s経由で接続"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 77fd4b156024..30ab3b49afdd 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ავტომატურად დაკავშირება ვერ მოხერხდება"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ინტერნეტ-კავშირი არ არის"</string> <string name="saved_network" msgid="7143698034077223645">"შენახული <xliff:g id="NAME">%1$s</xliff:g>-ის მიერ"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"დაკავშირებულია ფასიან ქსელთან"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"ავტომატურად დაკავშირდა %1$s-ის მეშვეობით"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ავტომატურად დაკავშირდა ქსელის ხარისხის შეფასების პროვაიდერის მეშვეობით"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ით დაკავშირებული"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index eb5cd54e4f19..ef7852710af4 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматты қосылмайды"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Интернетпен байланыс жоқ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> сақтаған"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Трафик саналатын желіге қосылды."</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s арқылы автоматты қосылды"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Желі рейтингі провайдері арқылы автоматты түрде қосылған"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s арқылы қосылған"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 38abb805cb10..ed59c747f8fd 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string> <string name="saved_network" msgid="7143698034077223645">"បានរក្សាទុកដោយ <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"បានភ្ជាប់ទៅបណ្ដាញដែលផ្អែកតាមទិន្នន័យដែលប្រើ"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈ %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈក្រុមហ៊ុនផ្តល់ការវាយតម្លៃលើបណ្តាញ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"បានភ្ជាប់តាមរយៈ %1$s"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 560fba15bbad..995dc8632e5c 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವಿಲ್ಲ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ನಿಂದ ಉಳಿಸಲಾಗಿದೆ"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ಮಾಪನ ಮಾಡಲಾದ ನೆಟ್ವರ್ಕ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ನೆಟ್ವರ್ಕ್ ರೇಟಿಂಗ್ ಒದಗಿಸುವವರ ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index f43ce16368cb..7863d481295e 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"자동으로 연결되지 않습니다."</string> <string name="wifi_no_internet" msgid="1774198889176926299">"인터넷에 연결되어 있지 않음"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>(으)로 저장됨"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"데이터 전송량 제한이 있는 네트워크에 연결됨"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s을(를) 통해 자동으로 연결됨"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"네트워크 평가 제공업체를 통해 자동으로 연결됨"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s을(를) 통해 연결됨"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 35b1ecacd505..45d05c6372ce 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматтык түрдө туташпайт"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Интернетке туташпай турат"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> тарабынан сакталды"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Чектелген трафикке туташтырылды"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s аркылуу автоматтык түрдө туташты"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Тармактар рейтингинин булагы аркылуу автоматтык түрдө туташты"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s аркылуу жеткиликтүү"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index f60fe7f8affc..ca6e06c13892 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string> <string name="saved_network" msgid="7143698034077223645">"ບັນທຶກໂດຍ <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍທີ່ມີການວັດແທກແລ້ວ"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"ເຊື່ອມຕໍ່ຜ່ານທາງ %1$s ໂດຍອັດຕະໂນມັດ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ເຊື່ອມຕໍ່ກັບອັດຕະໂນມັດແລ້ວຜ່ານຜູ້ໃຫ້ບໍລິການຄະແນນເຄືອຂ່າຍ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"ເຊື່ອມຕໍ່ຜ່ານ %1$s ແລ້ວ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index e66e3c5a9944..173b57eaba57 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nebus automatiškai prisijungiama"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nėra interneto ryšio"</string> <string name="saved_network" msgid="7143698034077223645">"Išsaugojo <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Prisijungta prie matuojamo tinklo"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiškai prisijungta naudojant „%1$s“"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiškai prisijungta naudojant tinklo įvertinimo paslaugos teikėjo paslaugomis"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Prisijungta naudojant „%1$s“"</string> @@ -208,7 +207,7 @@ <string name="enable_adb" msgid="8072776357237289039">"USB perkrova"</string> <string name="enable_adb_summary" msgid="3711526030096574316">"Derinimo režimas, kai prijungtas USB"</string> <string name="clear_adb_keys" msgid="3010148733140369917">"Panaikinti USB derinimo prieigos teises"</string> - <string name="enable_adb_wireless" msgid="6973226350963971018">"Belaidžio ryšio derinimas"</string> + <string name="enable_adb_wireless" msgid="6973226350963971018">"Belaidžio ryšio derin."</string> <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Derinimo režimas, kai prisijungta prie „Wi‑Fi“"</string> <string name="adb_wireless_error" msgid="721958772149779856">"Klaida"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"Belaidžio ryšio derinimas"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 90bcc04d1212..0ef3f0eee6b5 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не може да се поврзе автоматски"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Нема пристап до интернет"</string> <string name="saved_network" msgid="7143698034077223645">"Зачувано од <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Поврзано на мрежа со ограничен интернет"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматски поврзано преку %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматски поврзано преку оценувач на мрежа"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Поврзано преку %1$s"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index ba1987bc1d88..e6289ae3591e 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"സ്വയമേവ കണക്റ്റുചെയ്യില്ല"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> സംരക്ഷിച്ചത്"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"മീറ്റർ ചെയ്ത നെറ്റ്വർക്കിലേക്ക് കണക്റ്റ് ചെയ്തു"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s വഴി സ്വയമേവ ബന്ധിപ്പിച്ചു"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"നെറ്റ്വർക്ക് റേറ്റിംഗ് ദാതാവുമായി സ്വയം കണക്റ്റുചെയ്തു"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s വഴി ബന്ധിപ്പിച്ചു"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 45a831daee14..ead712c30b89 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматаар холбогдохгүй"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Интернет хандалт алга"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> хадгалсан"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Хязгаартай сүлжээнд холбогдсон"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s-р автоматаар холбогдсон"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Сүлжээний үнэлгээ үзүүлэгчээр автоматаар холбогдох"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-р холбогдсон"</string> @@ -398,8 +397,8 @@ <item msgid="1282170165150762976">"Дижитал агуулгад зориулан тааруулсан өнгө"</item> </string-array> <string name="inactive_apps_title" msgid="5372523625297212320">"Зогсолтын горимын апп"</string> - <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Унтраах/асаахын тулд дарна уу."</string> - <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Унтраах/асаахын тулд дарна уу."</string> + <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Асаах/унтраахын тулд дарна уу."</string> + <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Асаах/унтраахын тулд дарна уу."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Апп зогсолтын горимын төлөв:<xliff:g id="BUCKET"> %s</xliff:g>"</string> <string name="transcode_settings_title" msgid="2581975870429850549">"Медиа хөрвүүлгийн тохиргоо"</string> <string name="transcode_user_control" msgid="6176368544817731314">"Хөрвүүлгийн өгөгдмөлийг дарах"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 1e69b28fa4de..edcc71b17e8a 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वयंचलितपणे कनेक्ट करणार नाही"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट अॅक्सेस नाही"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे सेव्ह केले"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"मर्यादित नेटवर्कशी कनेक्ट केले आहे"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s द्वारे स्वयंचलितपणे कनेक्ट केले"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग प्रदात्याद्वारे स्वयंचलितपणे कनेक्ट केले"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s द्वारे कनेक्ट केले"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 71c9eeafc594..8a14437e3647 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan menyambung secara automatik"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Tiada akses Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Diselamatkan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Disambungkan kepada rangkaian bermeter"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Disambungkan secara automatik melalui %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Disambungkan secara automatik melalui pembekal penilaian rangkaian"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Disambungkan melalui %1$s"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 3b3983e5164e..377c8b241144 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"အလိုအလျောက်ချိတ်ဆက်မည်မဟုတ်ပါ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"အင်တာနက် ချိတ်ဆက်မှု မရှိပါ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> က သိမ်းဆည်းခဲ့သည်"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"အခမဲ့မဟုတ်သော ကွန်ရက်သို့ ချိတ်ဆက်ထားသည်"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ကွန်ရက်အဆင့်သတ်မှတ်ပေးသူ မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index a8c01b3db73c..f0e773b1833d 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kobler ikke til automatisk"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internettilgang"</string> <string name="saved_network" msgid="7143698034077223645">"Lagret av <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Koble til et nettverk med datamåling"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilkoblet via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk tilkoblet via leverandør av nettverksvurdering"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Tilkoblet via %1$s"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index c28563764a2d..0bde70fe209d 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वतः जडान हुने छैन"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"इन्टरनेटमाथिको पहुँच छैन"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा सुरक्षित गरियो"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"प्रयोगसम्बन्धी सीमा तोकिएको नेटवर्कमा कनेक्ट गरियो"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s मार्फत् स्वतः जडान गरिएको"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्कको दर्जा प्रदायक मार्फत स्वत: जडान गरिएको"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s मार्फत जडित"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 9f4ea68f4c64..2bc1e8c0f7f0 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Er wordt niet automatisch verbinding gemaakt"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string> <string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Verbonden met netwerk met datalimiet"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 31bd7af804e0..787c87182cd7 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ସ୍ୱଚାଳିତ ଭାବେ ସଂଯୁକ୍ତ ହେବନାହିଁ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ଇଣ୍ଟରନେଟ୍ର କୌଣସି ଆକ୍ସେସ୍ ନାହିଁ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ୱାରା ସେଭ କରାଯାଇଛି"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ମିଟରଯୁକ୍ତ ନେଟୱାର୍କ ସହ ସଂଯୋଗ କରାଯାଇଛି"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲୀ ସଂଯୁକ୍ତ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ନେଟୱର୍କ ମୂଲ୍ୟାୟନ ପ୍ରଦାତାଙ୍କ ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲ୍ୟ ସଂଯୁକ୍ତ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index f21c4cedfb71..9483e060081c 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ਵੱਲੋਂ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ਮੀਟਰਬੱਧ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਹੈ"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ਰਾਹੀਂ ਆਪਣੇ-ਆਪ ਕਨੈਕਟ ਹੋਇਆ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ਨੈੱਟਵਰਕ ਰੇਟਿੰਗ ਪ੍ਰਦਾਨਕ ਰਾਹੀਂ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਹੋਇਆ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index c9c4a6c31f49..c7f1595c86d0 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nie można połączyć automatycznie"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Brak dostępu do internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Zapisane przez: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Połączono z siecią z pomiarem użycia danych"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatycznie połączono przez: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatycznie połączono przez dostawcę ocen jakości sieci"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Połączono przez %1$s"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 4b710853f329..63d8b8f53f7f 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nu se va conecta automat"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nu există acces la internet"</string> <string name="saved_network" msgid="7143698034077223645">"Salvată de <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"S-a conectat la o rețea contorizată"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectată automat prin %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectată automat prin furnizor de evaluări ale rețelei"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectată prin %1$s"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 4aeb9855ff2b..ac43e493d73b 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Подключение не будет выполняться автоматически"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Без доступа к Интернету"</string> <string name="saved_network" msgid="7143698034077223645">"Кто сохранил: <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Подключено к сети с ограниченным трафиком"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматически подключено к %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматически подключено через автора рейтинга сетей"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Подключено к %1$s"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 525d4231eb4c..08fd9a03d7c4 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ස්වයංක්රිය නැවත සම්බන්ධ නොවනු ඇත"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"අන්තර්ජාල ප්රවේශය නැත"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> විසින් සුරකින ලදී"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"මනුගත ජාලයට සම්බන්ධ කර ඇත"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s හරහා ස්වයංක්රියව සම්බන්ධ විය"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ජාල ශ්රේණිගත සපයන්නා හරහා ස්වයංක්රියව සම්බන්ධ විය"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s හරහා සම්බන්ධ විය"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index ee9ae6a47d2f..8f05684e6de9 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nedôjde k automatickému pripojeniu"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Žiadny prístup k internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Uložila aplikácia <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Pripojené k meranej sieti"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky pripojené prostredníctvom %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky pripojené prostredníctvom poskytovateľa hodnotenia siete"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Pripojené prostredníctvom %1$s"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index ff60a322dc1d..3dccf3b53f9c 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Samodejna vnovična vzpostavitev povezave se ne bo izvedla"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ni dostopa do interneta"</string> <string name="saved_network" msgid="7143698034077223645">"Shranil(-a): <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezano z omrežjem z omejenim prenosom podatkov"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Samodejno vzpostavljena povezava prek: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Samodejno vzpostavljena povezava prek ponudnika ocenjevanja omrežij"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Vzpostavljena povezava prek: %1$s"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 78e6ed60c176..c09ad4c42fa4 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nuk do të lidhet automatikisht"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nuk ka qasje në internet"</string> <string name="saved_network" msgid="7143698034077223645">"E ruajtur nga <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Lidhur me një rrjet me matje"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Lidhur automatikisht përmes %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Lidhur automatikisht nëpërmjet ofruesit të vlerësimit të rrjetit"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"E lidhur përmes %1$s"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 8bb9277ba42b..b5a91bfed1d0 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Аутоматско повезивање није успело"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Нема приступа интернету"</string> <string name="saved_network" msgid="7143698034077223645">"Сачувао/ла је <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Повезани сте на мрежу са ограничењем"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Аутоматски повезано преко %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аутоматски повезано преко добављача оцене мреже"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Веза је успостављена преко приступне тачке %1$s"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 03fb2238989c..16a1be606707 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Det går inte att ansluta automatiskt"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetåtkomst"</string> <string name="saved_network" msgid="7143698034077223645">"Sparades av <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ansluten till nätverk med datapriser"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiskt ansluten via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiskt ansluten via leverantör av nätverksbetyg"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Anslutet via %1$s"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 631a41346282..58896c87baaa 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Haiwezi kuunganisha kiotomatiki"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Hakuna muunganisho wa intaneti"</string> <string name="saved_network" msgid="7143698034077223645">"Ilihifadhiwa na <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Imeunganishwa kwenye mtandao unaopima data"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Imeunganishwa kiotomatiki kupitia %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Imeunganishwa kiotomatiki kupitia mtoa huduma wa ukadiriaji wa mtandao"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Imeunganishwa kupitia %1$s"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 5796603a761a..71cf7d41fcc4 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"தானாக இணைக்கப்படாது"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"இண்டர்நெட் அணுகல் இல்லை"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> சேமித்தது"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"கட்டண நெட்வொர்க்குடன் இணைக்கப்பட்டுள்ளது"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s மூலம் தானாக இணைக்கப்பட்டது"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"நெட்வொர்க் மதிப்பீடு வழங்குநரால் தானாக இணைக்கப்பட்டது"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s வழியாக இணைக்கப்பட்டது"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 9027ca18fc79..c3a65e702a28 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"స్వయంచాలకంగా కనెక్ట్ కాదు"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ఇంటర్నెట్ యాక్సెస్ లేదు"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా సేవ్ చేయబడింది"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"డేటా నియంత్రణ నెట్వర్క్కు కనెక్ట్ చేయబడింది"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"నెట్వర్క్ రేటింగ్ ప్రదాత ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 8358dbbfafa5..903accbdc7ff 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"จะไม่เชื่อมต่อโดยอัตโนมัติ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"เข้าถึงอินเทอร์เน็ตไม่ได้"</string> <string name="saved_network" msgid="7143698034077223645">"บันทึกโดย<xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"เชื่อมต่อกับเครือข่ายแบบจำกัดปริมาณแล้ว"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"เชื่อมต่ออัตโนมัติผ่าน %1$s แล้ว"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"เชื่อมต่ออัตโนมัติผ่านผู้ให้บริการการจัดอันดับเครือข่าย"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"เชื่อมต่อผ่าน %1$s แล้ว"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index d5250a4581db..b259201fe029 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Hindi awtomatikong kokonekta"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Walang access sa internet"</string> <string name="saved_network" msgid="7143698034077223645">"Na-save ng <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Nakakonekta sa nakametrong network"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Awtomatikong nakakonekta sa pamamagitan ng %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Awtomatikong nakakonekta sa pamamagitan ng provider ng rating ng network"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Nakakonekta sa pamamagitan ng %1$s"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 8e0b889f5191..a0bc3626d086 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Otomatik olarak bağlanma"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"İnternet erişimi yok"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tarafından kaydedildi"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Sayaçlı ağa bağlı"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzerinden otomatik olarak bağlı"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ağ derecelendirme sağlayıcı aracılığıyla otomatik olarak bağlandı"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s üzerinden bağlı"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index c2f71c38d83e..509c8a602794 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не під’єднуватиметься автоматично"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Немає доступу до Інтернету"</string> <string name="saved_network" msgid="7143698034077223645">"Збережено додатком <xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Установлено з\'єднання з мережею з тарифікацією трафіку"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично під’єднано через %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично під’єднано через постачальника оцінки якості мережі"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Під’єднано через %1$s"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index a0c5f94a2845..e097ab2f5789 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"خودکار طور پر منسلک نہیں ہو گا"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"انٹرنیٹ تک کوئی رسائی نہیں"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> کی جانب سے محفوظ کردہ"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"میٹرڈ نیٹ ورک سے منسلک ہے"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s کے ذریعے از خود منسلک کردہ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"نیٹ ورک درجہ بندی کے فراہم کنندہ کے ذریعے از خود منسلک"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"منسلک بذریعہ %1$s"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 1fb610bad36e..8c2a95d5c316 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik ravishda ulanilmaydi"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Internet aloqasi yo‘q"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tomonidan saqlangan"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Trafik hisoblanadigan tarmoqqa ulandi"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s orqali avtomatik ulandi"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tarmoqlar reytingi muallifi orqali avtomatik ulandi"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s orqali ulangan"</string> @@ -156,7 +155,7 @@ <string name="running_process_item_user_label" msgid="3988506293099805796">"Foydalanuvchi: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> <string name="launch_defaults_some" msgid="3631650616557252926">"Ba’zi birlamchi sozlamalar o‘rnatilgan"</string> <string name="launch_defaults_none" msgid="8049374306261262709">"Birlamchi sozlamalar belgilanmagan"</string> - <string name="tts_settings" msgid="8130616705989351312">"Nutq sintezi sozlamalari"</string> + <string name="tts_settings" msgid="8130616705989351312">"Matnni nutqqa aylantirish sozlamalari"</string> <string name="tts_settings_title" msgid="7602210956640483039">"Nutq sintezi"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"Nutq tezligi"</string> <string name="tts_default_rate_summary" msgid="3781937042151716987">"Matnni o‘qish tezligi"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 1f3ea486bd08..7a4d89bd4f22 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sẽ không tự động kết nối"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Không có quyền truy cập Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Do <xliff:g id="NAME">%1$s</xliff:g> lưu"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Đã kết nối với mạng có đo lượng dữ liệu"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Tự động được kết nối qua %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tự động được kết nối qua nhà cung cấp dịch vụ xếp hạng mạng"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Được kết nối qua %1$s"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 828d25fbe71b..7cd61fc5491c 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"无法自动连接"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"无法访问互联网"</string> <string name="saved_network" msgid="7143698034077223645">"由“<xliff:g id="NAME">%1$s</xliff:g>”保存"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已连接到按流量计费的网络"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"已通过%1$s自动连接"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已自动连接(通过网络评分服务提供方)"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"已通过%1$s连接"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 54e1f41e9ed8..458071cd8cb2 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"不會自動連線"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"無法連接互聯網"</string> <string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已連結至按用量收費的網絡"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網絡評分供應商自動連線"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index b8f1f58b5691..867ed8b61c17 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"無法自動連線"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"沒有可用的網際網路連線"</string> <string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已連線到計量付費網路"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網路評分供應商自動連線"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index d680b66b56fa..e64dbd30dc82 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -36,8 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ngeke ize ixhumeke ngokuzenzakalela"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Akukho ukufinyelela kwe-inthanethi"</string> <string name="saved_network" msgid="7143698034077223645">"Kulondolozwe ngu-<xliff:g id="NAME">%1$s</xliff:g>"</string> - <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> - <skip /> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Kuxhunywe kunethiwekhi eyenziwe imitha"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ixhumeke ngokuzenzakalela nge-%1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Kuxhunywe ngokuzenzakalelayo ngomhlinzeki wesilinganiso wenethiwekhi"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Kuxhumeke nge-%1$s"</string> diff --git a/packages/SystemUI/res/drawable/controls_dialog_bg.xml b/packages/SystemUI/res/drawable/controls_dialog_bg.xml new file mode 100644 index 000000000000..cb4686dd04a7 --- /dev/null +++ b/packages/SystemUI/res/drawable/controls_dialog_bg.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="?android:attr/colorBackgroundFloating" /> + <corners android:radius="@dimen/notification_corner_radius" /> +</shape> diff --git a/packages/SystemUI/res/drawable/udfps_progress_bar.xml b/packages/SystemUI/res/drawable/udfps_progress_bar.xml new file mode 100644 index 000000000000..e5389f3b99ef --- /dev/null +++ b/packages/SystemUI/res/drawable/udfps_progress_bar.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@android:id/background"> + <shape + android:innerRadiusRatio="2.2" + android:shape="ring" + android:thickness="@dimen/udfps_enroll_progress_thickness" + android:useLevel="false" + android:tint="?android:colorControlNormal"> + <solid android:color="@*android:color/white_disabled_material" /> + </shape> + </item> + <item android:id="@android:id/progress"> + <rotate + android:fromDegrees="270" + android:pivotX="50%" + android:pivotY="50%" + android:toDegrees="270"> + <shape + android:innerRadiusRatio="2.2" + android:shape="ring" + android:thickness="@dimen/udfps_enroll_progress_thickness" + android:tint="?android:attr/colorControlActivated"> + <solid android:color="@android:color/white" /> + </shape> + </rotate> + </item> +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/controls_in_dialog.xml b/packages/SystemUI/res/layout/controls_in_dialog.xml new file mode 100644 index 000000000000..983999f9a5f8 --- /dev/null +++ b/packages/SystemUI/res/layout/controls_in_dialog.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/control_detail_root" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginVertical="@dimen/controls_activity_view_top_offset" + android:layout_marginHorizontal="@dimen/controls_activity_view_side_offset" + android:padding="8dp" + android:orientation="vertical" + android:background="@drawable/controls_dialog_bg"> + + <com.android.systemui.globalactions.MinHeightScrollView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:orientation="vertical" + android:scrollbars="none"> + + <LinearLayout + android:id="@+id/global_actions_controls" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:orientation="vertical" + android:clipToPadding="false" /> + + </com.android.systemui.globalactions.MinHeightScrollView> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 04de9784812f..862076b650b9 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -81,6 +81,17 @@ android:contentDescription="@string/accessibility_phone_button" android:tint="?attr/wallpaperTextColor" /> + <ImageView + android:id="@+id/alt_left_button" + android:layout_height="@dimen/keyguard_affordance_height" + android:layout_width="@dimen/keyguard_affordance_width" + android:layout_gravity="bottom|start" + android:scaleType="center" + android:tint="?attr/wallpaperTextColor" + android:layout_marginStart="24dp" + android:layout_marginBottom="48dp" + android:visibility="gone" /> + <FrameLayout android:id="@+id/overlay_container" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml index 8f3345f9d85c..e2f3e2a306e3 100644 --- a/packages/SystemUI/res/layout/long_screenshot.xml +++ b/packages/SystemUI/res/layout/long_screenshot.xml @@ -74,7 +74,7 @@ android:id="@+id/preview" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="24dp" + android:layout_marginBottom="42dp" android:layout_marginHorizontal="48dp" android:adjustViewBounds="true" app:layout_constrainedHeight="true" @@ -91,19 +91,33 @@ android:id="@+id/crop_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="24dp" + android:layout_marginBottom="42dp" app:layout_constrainedHeight="true" app:layout_constrainedWidth="true" app:layout_constraintTop_toBottomOf="@id/guideline" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent" - app:handleThickness="3dp" + app:handleThickness="@dimen/screenshot_crop_handle_thickness" app:handleColor="@*android:color/accent_device_default" - app:scrimColor="#9444" + app:scrimColor="@color/screenshot_crop_scrim" tools:background="?android:colorBackground" tools:minHeight="100dp" tools:minWidth="100dp" /> + <com.android.systemui.screenshot.MagnifierView + android:id="@+id/magnifier" + android:visibility="invisible" + android:layout_width="200dp" + android:layout_height="200dp" + app:layout_constraintTop_toBottomOf="@id/guideline" + app:layout_constraintLeft_toLeftOf="parent" + app:handleThickness="@dimen/screenshot_crop_handle_thickness" + app:handleColor="@*android:color/accent_device_default" + app:scrimColor="@color/screenshot_crop_scrim" + app:borderThickness="4dp" + app:borderColor="#fff" + /> + </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index c0788051efed..6ae306e17209 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -20,4 +20,17 @@ android:id="@+id/udfps_view" android:layout_width="match_parent" android:layout_height="match_parent" - systemui:sensorTouchAreaCoefficient="0.5"/> + systemui:sensorTouchAreaCoefficient="0.5"> + + <!-- Enrollment progress bar--> + <com.android.systemui.biometrics.UdfpsProgressBar + android:id="@+id/progress_bar" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:max="100" + android:padding="@dimen/udfps_enroll_progress_thickness" + android:progress="0" + android:layout_gravity="center" + android:visibility="gone"/> + +</com.android.systemui.biometrics.UdfpsView> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 6c55fb62e638..8166e35d5b6a 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -178,6 +178,14 @@ <attr name="scrimColor" format="color" /> </declare-styleable> + <declare-styleable name="MagnifierView"> + <attr name="handleThickness" format="dimension" /> + <attr name="handleColor" format="color" /> + <attr name="scrimColor" format="color" /> + <attr name="borderThickness" format="dimension" /> + <attr name="borderColor" format="color" /> + </declare-styleable> + <declare-styleable name="RoundedCornerProgressDrawable"> <attr name="android:drawable" /> </declare-styleable> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index a7cf3e91dbba..5fb6de7bb588 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -197,6 +197,9 @@ <color name="global_screenshot_dismiss_foreground">@color/GM2_grey_500</color> <color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black --> + <!-- Long screenshot UI --> + <color name="screenshot_crop_scrim">#9444</color> + <!-- GM2 colors --> <color name="GM2_grey_50">#F8F9FA</color> <color name="GM2_grey_100">#F1F3F4</color> @@ -261,6 +264,7 @@ <color name="control_enabled_cool_foreground">@color/GM2_blue_300</color> <color name="control_thumbnail_tint">#33000000</color> <color name="control_thumbnail_shadow_color">@*android:color/black</color> + <color name="controls_lockscreen_scrim">#AA000000</color> <!-- Docked misalignment message --> <color name="misalignment_text_color">#F28B82</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index d92f4ea65390..afa98b56a13c 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -345,6 +345,7 @@ <dimen name="screenshot_action_chip_padding_end">16dp</dimen> <dimen name="screenshot_action_chip_text_size">14sp</dimen> <dimen name="screenshot_dismissal_height_delta">80dp</dimen> + <dimen name="screenshot_crop_handle_thickness">3dp</dimen> <!-- The width of the view containing navigation buttons --> @@ -1116,6 +1117,9 @@ <!-- Y translation for credential contents when animating in --> <dimen name="biometric_dialog_credential_translation_offset">60dp</dimen> + <!-- UDFPS enrollment progress bar thickness --> + <dimen name="udfps_enroll_progress_thickness">12dp</dimen> + <!-- Wireless Charging Animation values --> <dimen name="wireless_charging_dots_radius_start">0dp</dimen> <dimen name="wireless_charging_dots_radius_end">4dp</dimen> @@ -1259,6 +1263,7 @@ <!-- Home Controls activity view detail panel--> <dimen name="controls_activity_view_top_offset">100dp</dimen> + <dimen name="controls_activity_view_side_offset">12dp</dimen> <dimen name="controls_activity_view_text_size">17sp</dimen> <dimen name="controls_activity_view_corner_radius">@*android:dimen/config_bottomDialogCornerRadius</dimen> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 4b04eebfddf0..7c72548a7252 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -662,6 +662,19 @@ <item name="android:windowExitAnimation">@anim/bottomsheet_out</item> </style> + <style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar"> + <item name="android:windowAnimationStyle">@style/Animation.Fade</item> + <item name="android:windowFullscreen">true</item> + <item name="android:windowIsFloating">false</item> + <item name="android:windowBackground">@color/controls_lockscreen_scrim</item> + <item name="android:backgroundDimEnabled">true</item> + </style> + + <style name="Animation.Fade"> + <item name="android:windowEnterAnimation">@android:anim/fade_in</item> + <item name="android:windowExitAnimation">@android:anim/fade_out</item> + </style> + <style name="Control" /> <style name="Control.MenuItem"> @@ -752,4 +765,12 @@ <item name="android:textSize">14sp</item> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> </style> + + <style name="UdfpsProgressBarStyle" + parent="android:style/Widget.Material.ProgressBar.Horizontal"> + <item name="android:indeterminate">false</item> + <item name="android:max">10000</item> + <item name="android:mirrorForRtl">false</item> + <item name="android:progressDrawable">@drawable/udfps_progress_bar</item> + </style> </resources> 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/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java index e07c84034b31..5290986b2a1c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java @@ -38,6 +38,7 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { private static final String TAG = "UdfpsAnimationEnroll"; private static final float SHADOW_RADIUS = 5.f; + private static final float PROGRESS_BAR_RADIUS = 140.f; @Nullable private RectF mSensorRect; @NonNull private final Paint mSensorPaint; @@ -81,12 +82,12 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { @Override public int getPaddingX() { - return (int) Math.ceil(SHADOW_RADIUS); + return (int) Math.ceil(PROGRESS_BAR_RADIUS); } @Override public int getPaddingY() { - return (int) Math.ceil(SHADOW_RADIUS); + return (int) Math.ceil(PROGRESS_BAR_RADIUS); } @Override @@ -104,12 +105,4 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { public int getOpacity() { return 0; } - - public void onEnrollmentProgress(int remaining) { - Log.d(TAG, "Remaining: " + remaining); - } - - public void onEnrollmentHelp() { - Log.d(TAG, "onEnrollmentHelp"); - } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java index 4e3419e1fab3..41ea4d66f575 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java @@ -103,18 +103,6 @@ public class UdfpsAnimationView extends View implements DozeReceiver, postInvalidate(); } - void onEnrollmentProgress(int remaining) { - if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) { - ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentProgress(remaining); - } - } - - void onEnrollmentHelp() { - if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) { - ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp(); - } - } - public int getPaddingX() { if (mUdfpsAnimation == null) { return 0; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index c088400f4057..e7b08e72877d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -84,6 +84,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private boolean mIsOverlayRequested; // Reason the overlay has been requested. See IUdfpsOverlayController for definitions. private int mRequestReason; + @Nullable UdfpsEnrollHelper mEnrollHelper; // The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when // to turn off high brightness mode. To get around this limitation, the state of the AOD @@ -95,6 +96,12 @@ public class UdfpsController implements DozeReceiver, HbmCallback { public class UdfpsOverlayController extends IUdfpsOverlayController.Stub { @Override public void showUdfpsOverlay(int sensorId, int reason) { + if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR + || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) { + mEnrollHelper = new UdfpsEnrollHelper(reason); + } else { + mEnrollHelper = null; + } UdfpsController.this.showOverlay(reason); } @@ -296,7 +303,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { try { Log.v(TAG, "showUdfpsOverlay | adding window"); final UdfpsAnimation animation = getUdfpsAnimationForReason(reason); - mView.setUdfpsAnimation(animation); + mView.setExtras(animation, mEnrollHelper); mWindowManager.addView(mView, computeLayoutParams(animation)); mView.setOnTouchListener(mOnTouchListener); mIsOverlayShowing = true; @@ -313,7 +320,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private UdfpsAnimation getUdfpsAnimationForReason(int reason) { Log.d(TAG, "getUdfpsAnimationForReason: " + reason); switch (reason) { - case IUdfpsOverlayController.REASON_ENROLL: + case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR: + case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: return new UdfpsAnimationEnroll(mContext); case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: return new UdfpsAnimationKeyguard(mView, mContext, mStatusBarStateController); @@ -329,7 +337,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { mFgExecutor.execute(() -> { if (mIsOverlayShowing) { Log.v(TAG, "hideUdfpsOverlay | removing window"); - mView.setUdfpsAnimation(null); + mView.setExtras(null, null); mView.setOnTouchListener(null); // Reset the controller back to its starting state. onFingerUp(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java new file mode 100644 index 000000000000..2442633a4a69 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java @@ -0,0 +1,60 @@ +/* + * 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.biometrics; + +import android.hardware.fingerprint.IUdfpsOverlayController; + +import androidx.annotation.NonNull; + +/** + * Helps keep track of enrollment state and animates the progress bar accordingly. + */ +public class UdfpsEnrollHelper { + private static final String TAG = "UdfpsEnrollHelper"; + + // IUdfpsOverlayController reason + private final int mEnrollReason; + + private int mTotalSteps = -1; + private int mCurrentProgress = 0; + + public UdfpsEnrollHelper(int reason) { + mEnrollReason = reason; + } + + boolean shouldShowProgressBar() { + return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING; + } + + void onEnrollmentProgress(int remaining, @NonNull UdfpsProgressBar progressBar) { + if (mTotalSteps == -1) { + mTotalSteps = remaining; + } + + mCurrentProgress = progressBar.getMax() * Math.max(0, mTotalSteps + 1 - remaining) + / (mTotalSteps + 1); + progressBar.setProgress(mCurrentProgress, true /* animate */); + } + + void updateProgress(@NonNull UdfpsProgressBar progressBar) { + progressBar.setProgress(mCurrentProgress); + } + + void onEnrollmentHelp() { + + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java new file mode 100644 index 000000000000..84e2fab7bf6b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java @@ -0,0 +1,59 @@ +/* + * 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.biometrics; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ProgressBar; + +import com.android.systemui.R; + +/** + * A (determinate) progress bar in the form of a ring. The progress bar goes clockwise starting + * from the 12 o'clock position. This view maintain equal width and height using a strategy similar + * to "centerInside" for ImageView. + */ +public class UdfpsProgressBar extends ProgressBar { + + public UdfpsProgressBar(Context context) { + this(context, null); + } + + public UdfpsProgressBar(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, R.style.UdfpsProgressBarStyle); + } + + public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + final int measuredHeight = getMeasuredHeight(); + final int measuredWidth = getMeasuredWidth(); + + final int length = Math.min(measuredHeight, measuredWidth); + setMeasuredDimension(length, length); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 7e378d3c568e..00cb28b8b8fb 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -56,6 +56,8 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin @NonNull private final RectF mSensorRect; @NonNull private final Paint mDebugTextPaint; + @Nullable private UdfpsProgressBar mProgressBar; + // Used to obtain the sensor location. @NonNull private FingerprintSensorPropertiesInternal mSensorProps; @@ -64,6 +66,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin private boolean mIlluminationRequested; private int mStatusBarState; private boolean mNotificationShadeExpanded; + @Nullable private UdfpsEnrollHelper mEnrollHelper; public UdfpsView(Context context, AttributeSet attrs) { super(context, attrs); @@ -108,8 +111,17 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin mSensorProps = properties; } - void setUdfpsAnimation(@Nullable UdfpsAnimation animation) { + void setExtras(@Nullable UdfpsAnimation animation, @Nullable UdfpsEnrollHelper enrollHelper) { mAnimationView.setAnimation(animation); + mEnrollHelper = enrollHelper; + + if (enrollHelper != null) { + mEnrollHelper.updateProgress(mProgressBar); + mProgressBar.setVisibility(enrollHelper.shouldShowProgressBar() + ? View.VISIBLE : View.GONE); + } else { + mProgressBar.setVisibility(View.GONE); + } } @Override @@ -138,6 +150,11 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin } @Override + protected void onFinishInflate() { + mProgressBar = findViewById(R.id.progress_bar); + } + + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); mSensorRect.set(0 + mAnimationView.getPaddingX(), @@ -233,10 +250,10 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin } void onEnrollmentProgress(int remaining) { - mAnimationView.onEnrollmentProgress(remaining); + mEnrollHelper.onEnrollmentProgress(remaining, mProgressBar); } void onEnrollmentHelp() { - mAnimationView.onEnrollmentHelp(); + } } 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/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt new file mode 100644 index 000000000000..8e878cf76ad9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt @@ -0,0 +1,90 @@ +/* + * 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.controls.ui + +import android.app.Dialog +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager + +import com.android.systemui.Interpolators +import com.android.systemui.R +import com.android.systemui.broadcast.BroadcastDispatcher + +/** + * Show the controls space inside a dialog, as from the lock screen. + */ +class ControlsDialog( + thisContext: Context, + val broadcastDispatcher: BroadcastDispatcher +) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) { + + private val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val action = intent.getAction() + if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { + dismiss() + } + } + } + + init { + window.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) + window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM + or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) + + setContentView(R.layout.controls_in_dialog) + + requireViewById<ViewGroup>(R.id.control_detail_root).apply { + setOnClickListener { dismiss() } + (getParent() as View).setOnClickListener { dismiss() } + } + } + + fun show( + controller: ControlsUiController + ): ControlsDialog { + super.show() + + val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls) + vg.alpha = 0f + controller.show(vg, { /* do nothing */ }) + + vg.animate() + .alpha(1f) + .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) + .setDuration(300) + + val filter = IntentFilter() + filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) + broadcastDispatcher.registerReceiver(receiver, filter) + + return this + } + + override fun dismiss() { + broadcastDispatcher.unregisterReceiver(receiver) + + if (!isShowing()) return + + super.dismiss() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java index 453e85ae25c7..517f8e7f0dac 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java @@ -51,7 +51,7 @@ public class ContextualButton extends ButtonDispatcher { * Reload the drawable from resource id, should reapply the previous dark intensity. */ public void updateIcon(int lightIconColor, int darkIconColor) { - if (getCurrentView() == null || !getCurrentView().isAttachedToWindow() || mIconResId == 0) { + if (mIconResId == 0) { return; } final KeyButtonDrawable currentDrawable = getImageDrawable(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 6e28cd89d43a..56d06eb1b474 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -315,7 +315,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener } try { if (DEBUG) Log.d(TAG, "Adding token"); - mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY); + mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY, + null /* options */); mIsTokenGranted = true; } catch (RemoteException e) { } diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 9037192e2ddc..543874325254 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -187,7 +187,7 @@ public class ScreenMediaRecorder { * @param refreshRate Desired refresh rate * @return array with supported width, height, and refresh rate */ - private int[] getSupportedSize(int screenWidth, int screenHeight, int refreshRate) { + private int[] getSupportedSize(final int screenWidth, final int screenHeight, int refreshRate) { double maxScale = 0; MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); @@ -207,25 +207,33 @@ public class ScreenMediaRecorder { int width = vc.getSupportedWidths().getUpper(); int height = vc.getSupportedHeights().getUpper(); - if (width >= screenWidth && height >= screenHeight - && vc.isSizeSupported(screenWidth, screenHeight)) { + int screenWidthAligned = screenWidth; + if (screenWidthAligned % vc.getWidthAlignment() != 0) { + screenWidthAligned -= (screenWidthAligned % vc.getWidthAlignment()); + } + int screenHeightAligned = screenHeight; + if (screenHeightAligned % vc.getHeightAlignment() != 0) { + screenHeightAligned -= (screenHeightAligned % vc.getHeightAlignment()); + } + if (width >= screenWidthAligned && height >= screenHeightAligned + && vc.isSizeSupported(screenWidthAligned, screenHeightAligned)) { // Desired size is supported, now get the rate - int maxRate = vc.getSupportedFrameRatesFor(screenWidth, screenHeight) - .getUpper().intValue(); + int maxRate = vc.getSupportedFrameRatesFor(screenWidthAligned, + screenHeightAligned).getUpper().intValue(); if (maxRate < refreshRate) { refreshRate = maxRate; } Log.d(TAG, "Screen size supported at rate " + refreshRate); - return new int[]{screenWidth, screenHeight, refreshRate}; + return new int[]{screenWidthAligned, screenHeightAligned, refreshRate}; } // Otherwise, continue searching double scale = Math.min(((double) width / screenWidth), ((double) height / screenHeight)); if (scale > maxScale) { - maxScale = scale; + maxScale = Math.min(1, scale); maxInfo = vc; } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java index 8e182b415488..c8afd0b6cfe9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java @@ -35,7 +35,7 @@ import com.android.systemui.R; * cropped out. */ public class CropView extends View { - private enum CropBoundary { + public enum CropBoundary { NONE, TOP, BOTTOM } @@ -48,8 +48,14 @@ public class CropView extends View { private float mTopCrop = 0f; private float mBottomCrop = 1f; + // When the user is dragging a handle, these variables store the distance between the top/bottom + // crop values and + private float mTopDelta = 0f; + private float mBottomDelta = 0f; + private CropBoundary mCurrentDraggingBoundary = CropBoundary.NONE; - private float mLastY; + private float mStartingY; // y coordinate of ACTION_DOWN + private CropInteractionListener mCropInteractionListener; public CropView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); @@ -73,54 +79,84 @@ public class CropView extends View { @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); - drawShade(canvas, 0, mTopCrop); - drawShade(canvas, mBottomCrop, 1f); - drawHandle(canvas, mTopCrop); - drawHandle(canvas, mBottomCrop); + float top = mTopCrop + mTopDelta; + float bottom = mBottomCrop + mBottomDelta; + drawShade(canvas, 0, top); + drawShade(canvas, bottom, 1f); + drawHandle(canvas, top); + drawHandle(canvas, bottom); } @Override public boolean onTouchEvent(MotionEvent event) { int topPx = fractionToPixels(mTopCrop); int bottomPx = fractionToPixels(mBottomCrop); - if (event.getAction() == MotionEvent.ACTION_DOWN) { - mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx); - if (mCurrentDraggingBoundary != CropBoundary.NONE) { - mLastY = event.getY(); - } - return true; - } - if (event.getAction() == MotionEvent.ACTION_MOVE - && mCurrentDraggingBoundary != CropBoundary.NONE) { - float delta = event.getY() - mLastY; - if (mCurrentDraggingBoundary == CropBoundary.TOP) { - mTopCrop = pixelsToFraction((int) MathUtils.constrain(topPx + delta, 0, - bottomPx - 2 * mCropTouchMargin)); - } else { // Bottom - mBottomCrop = pixelsToFraction((int) MathUtils.constrain(bottomPx + delta, - topPx + 2 * mCropTouchMargin, getMeasuredHeight())); - } - mLastY = event.getY(); - invalidate(); - return true; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx); + if (mCurrentDraggingBoundary != CropBoundary.NONE) { + mStartingY = event.getY(); + updateListener(event); + } + return true; + case MotionEvent.ACTION_MOVE: + if (mCurrentDraggingBoundary != CropBoundary.NONE) { + float delta = event.getY() - mStartingY; + if (mCurrentDraggingBoundary == CropBoundary.TOP) { + mTopDelta = pixelsToFraction((int) MathUtils.constrain(delta, -topPx, + bottomPx - 2 * mCropTouchMargin - topPx)); + } else { // Bottom + mBottomDelta = pixelsToFraction((int) MathUtils.constrain(delta, + topPx + 2 * mCropTouchMargin - bottomPx, + getMeasuredHeight() - bottomPx)); + } + updateListener(event); + invalidate(); + return true; + } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (mCurrentDraggingBoundary != CropBoundary.NONE) { + // Commit the delta to the stored crop values. + mTopCrop += mTopDelta; + mBottomCrop += mBottomDelta; + mTopDelta = 0; + mBottomDelta = 0; + updateListener(event); + } } return super.onTouchEvent(event); } /** - * @return value [0,1] representing the position of the top crop boundary. + * @return value [0,1] representing the position of the top crop boundary. Does not reflect + * changes from any in-progress touch input. */ public float getTopBoundary() { return mTopCrop; } /** - * @return value [0,1] representing the position of the bottom crop boundary. + * @return value [0,1] representing the position of the bottom crop boundary. Does not reflect + * changes from any in-progress touch input. */ public float getBottomBoundary() { return mBottomCrop; } + public void setCropInteractionListener(CropInteractionListener listener) { + mCropInteractionListener = listener; + } + + private void updateListener(MotionEvent event) { + if (mCropInteractionListener != null) { + float boundaryPosition = (mCurrentDraggingBoundary == CropBoundary.TOP) + ? mTopCrop + mTopDelta : mBottomCrop + mBottomDelta; + mCropInteractionListener.onCropMotionEvent(event, mCurrentDraggingBoundary, + boundaryPosition, fractionToPixels(boundaryPosition)); + } + } + private void drawShade(Canvas canvas, float fracStart, float fracEnd) { canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(), fractionToPixels(fracEnd), mShadePaint); @@ -148,4 +184,17 @@ public class CropView extends View { } return CropBoundary.NONE; } + + /** + * Listen for crop motion events and state. + */ + public interface CropInteractionListener { + /** + * Called whenever CropView has a MotionEvent that can impact the position of the crop + * boundaries. + */ + void onCropMotionEvent(MotionEvent event, CropBoundary boundary, float boundaryPosition, + int boundaryPositionPx); + + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java new file mode 100644 index 000000000000..f88715164bc7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java @@ -0,0 +1,184 @@ +/* + * 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.screenshot; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.Nullable; + +import com.android.systemui.R; + +/** + * MagnifierView shows a full-res cropped circular display of a given ImageTileSet, contents and + * positioning dereived from events from a CropView to which it listens. + * + * Not meant to be a general-purpose magnifier! + */ +public class MagnifierView extends View implements CropView.CropInteractionListener { + private Drawable mDrawable; + + private final Paint mShadePaint; + private final Paint mHandlePaint; + + private Path mOuterCircle; + private Path mInnerCircle; + + private Path mCheckerboard; + private Paint mCheckerboardPaint; + private final float mBorderPx; + private final int mBorderColor; + private float mCheckerboardBoxSize = 40; + + private float mLastCropPosition; + private CropView.CropBoundary mCropBoundary; + + public MagnifierView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public MagnifierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + TypedArray t = context.getTheme().obtainStyledAttributes( + attrs, R.styleable.MagnifierView, 0, 0); + mShadePaint = new Paint(); + mShadePaint.setColor(t.getColor(R.styleable.MagnifierView_scrimColor, Color.TRANSPARENT)); + mHandlePaint = new Paint(); + mHandlePaint.setColor(t.getColor(R.styleable.MagnifierView_handleColor, Color.BLACK)); + mHandlePaint.setStrokeWidth( + t.getDimensionPixelSize(R.styleable.MagnifierView_handleThickness, 20)); + mBorderPx = t.getDimensionPixelSize(R.styleable.MagnifierView_borderThickness, 0); + mBorderColor = t.getColor(R.styleable.MagnifierView_borderColor, Color.WHITE); + t.recycle(); + mCheckerboardPaint = new Paint(); + mCheckerboardPaint.setColor(Color.GRAY); + } + + public void setImageTileset(ImageTileSet tiles) { + if (tiles != null) { + mDrawable = tiles.getDrawable(); + mDrawable.setBounds(0, 0, tiles.getWidth(), tiles.getHeight()); + } else { + mDrawable = null; + } + invalidate(); + } + + @Override + public void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + int radius = getWidth() / 2; + mOuterCircle = new Path(); + mOuterCircle.addCircle(radius, radius, radius, Path.Direction.CW); + mInnerCircle = new Path(); + mInnerCircle.addCircle(radius, radius, radius - mBorderPx, Path.Direction.CW); + mCheckerboard = generateCheckerboard(); + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + + // TODO: just draw a circle at the end instead of clipping like this? + canvas.clipPath(mOuterCircle); + canvas.drawColor(mBorderColor); + canvas.clipPath(mInnerCircle); + + // Draw a checkerboard pattern for out of bounds. + canvas.drawPath(mCheckerboard, mCheckerboardPaint); + + if (mDrawable != null) { + canvas.save(); + // Translate such that the center of this view represents the center of the crop + // boundary. + canvas.translate(-mDrawable.getBounds().width() / 2 + getWidth() / 2, + -mDrawable.getBounds().height() * mLastCropPosition + getHeight() / 2); + mDrawable.draw(canvas); + canvas.restore(); + } + + Rect scrimRect = new Rect(0, 0, getWidth(), getHeight() / 2); + if (mCropBoundary == CropView.CropBoundary.BOTTOM) { + scrimRect.offset(0, getHeight() / 2); + } + canvas.drawRect(scrimRect, mShadePaint); + + canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mHandlePaint); + } + + @Override + public void onCropMotionEvent(MotionEvent event, CropView.CropBoundary boundary, + float cropPosition, int cropPositionPx) { + mCropBoundary = boundary; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mLastCropPosition = cropPosition; + setTranslationY(cropPositionPx - getHeight() / 2); + setPivotX(getWidth() / 2); + setPivotY(getHeight() / 2); + setScaleX(0.2f); + setScaleY(0.2f); + setAlpha(0f); + setTranslationX((getParentWidth() - getWidth()) / 2); + setVisibility(View.VISIBLE); + animate().alpha(1f).translationX(0).scaleX(1f).scaleY(1f).start(); + break; + case MotionEvent.ACTION_MOVE: + mLastCropPosition = cropPosition; + setTranslationY(cropPositionPx - getHeight() / 2); + invalidate(); + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + animate().alpha(0).translationX((getParentWidth() - getWidth()) / 2).scaleX(0.2f) + .scaleY(0.2f).withEndAction(() -> setVisibility(View.INVISIBLE)).start(); + break; + } + } + + private Path generateCheckerboard() { + Path path = new Path(); + int checkerWidth = (int) Math.ceil(getWidth() / mCheckerboardBoxSize); + int checkerHeight = (int) Math.ceil(getHeight() / mCheckerboardBoxSize); + + for (int row = 0; row < checkerHeight; row++) { + // Alternate starting on the first and second column; + int colStart = (row % 2 == 0) ? 0 : 1; + for (int col = colStart; col < checkerWidth; col += 2) { + path.addRect(col * mCheckerboardBoxSize, + row * mCheckerboardBoxSize, + (col + 1) * mCheckerboardBoxSize, + (row + 1) * mCheckerboardBoxSize, + Path.Direction.CW); + } + } + return path; + } + + private int getParentWidth() { + return ((View) getParent()).getWidth(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 18c379a4650f..25438a6f57ba 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -81,6 +81,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener private View mEdit; private View mShare; private CropView mCropView; + private MagnifierView mMagnifierView; public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor, Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) { @@ -120,13 +121,14 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener mEdit = findViewById(R.id.edit); mShare = findViewById(R.id.share); mCropView = findViewById(R.id.crop_view); + mMagnifierView = findViewById(R.id.magnifier); + mCropView.setCropInteractionListener(mMagnifierView); mSave.setOnClickListener(this::onClicked); mCancel.setOnClickListener(this::onClicked); mEdit.setOnClickListener(this::onClicked); mShare.setOnClickListener(this::onClicked); - //mPreview.setImageDrawable(mImageTileSet.getDrawable()); mConnection.start(this::startCapture); } @@ -164,6 +166,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener private void doFinish() { mPreview.setImageDrawable(null); + mMagnifierView.setImageTileset(null); mImageTileSet.clear(); mCallback.onFinish(); mWindow.getDecorView().getViewTreeObserver() @@ -273,6 +276,7 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener session.end(mCallback::onFinish); } else { mPreview.setImageDrawable(mImageTileSet.getDrawable()); + mMagnifierView.setImageTileset(mImageTileSet); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 0a366c9bb380..dd1419f4ff42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -48,6 +48,7 @@ import android.os.Messenger; import android.os.RemoteException; import android.os.UserHandle; import android.provider.MediaStore; +import android.provider.Settings; import android.service.media.CameraPrewarmService; import android.telecom.TelecomManager; import android.text.TextUtils; @@ -60,6 +61,7 @@ import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; @@ -71,6 +73,10 @@ import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.assist.AssistManager; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.controls.dagger.ControlsComponent; +import com.android.systemui.controls.ui.ControlsDialog; +import com.android.systemui.controls.ui.ControlsUiController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.IntentButtonProvider; import com.android.systemui.plugins.IntentButtonProvider.IntentButton; @@ -117,11 +123,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private static final int DOZE_ANIMATION_STAGGER_DELAY = 48; private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250; + // TODO(b/179494051): May no longer be needed private final boolean mShowLeftAffordance; private final boolean mShowCameraAffordance; private KeyguardAffordanceView mRightAffordanceView; private KeyguardAffordanceView mLeftAffordanceView; + private ImageView mAltLeftButton; private ViewGroup mIndicationArea; private TextView mEnterpriseDisclosure; private TextView mIndicationText; @@ -171,6 +179,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private int mBurnInYOffset; private ActivityIntentHelper mActivityIntentHelper; + private ControlsDialog mControlsDialog; + private ControlsComponent mControlsComponent; + private int mLockScreenMode; + private BroadcastDispatcher mBroadcastDispatcher; + public KeyguardBottomAreaView(Context context) { this(context, null); } @@ -236,6 +249,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mOverlayContainer = findViewById(R.id.overlay_container); mRightAffordanceView = findViewById(R.id.camera_button); mLeftAffordanceView = findViewById(R.id.left_button); + mAltLeftButton = findViewById(R.id.alt_left_button); mIndicationArea = findViewById(R.id.keyguard_indication_area); mEnterpriseDisclosure = findViewById( R.id.keyguard_indication_enterprise_disclosure); @@ -334,6 +348,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height); mLeftAffordanceView.setLayoutParams(lp); updateLeftAffordanceIcon(); + + lp = mAltLeftButton.getLayoutParams(); + lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width); + lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height); + mAltLeftButton.setLayoutParams(lp); } private void updateRightAffordanceIcon() { @@ -392,10 +411,17 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private void updateLeftAffordanceIcon() { + if (mDozing) { + mAltLeftButton.setVisibility(GONE); + } else if (mAltLeftButton.getDrawable() != null) { + mAltLeftButton.setVisibility(VISIBLE); + } + if (!mShowLeftAffordance || mDozing) { mLeftAffordanceView.setVisibility(GONE); return; } + IconState state = mLeftButton.getIcon(); mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE); if (state.drawable != mLeftAffordanceView.getDrawable() @@ -669,6 +695,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public void startFinishDozeAnimation() { long delay = 0; + if (mAltLeftButton.getVisibility() == View.VISIBLE) { + startFinishDozeAnimationElement(mAltLeftButton, delay); + } if (mLeftAffordanceView.getVisibility() == View.VISIBLE) { startFinishDozeAnimationElement(mLeftAffordanceView, delay); delay += DOZE_ANIMATION_STAGGER_DELAY; @@ -744,6 +773,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL if (dozing) { mOverlayContainer.setVisibility(INVISIBLE); + if (mControlsDialog != null) { + mControlsDialog.dismiss(); + mControlsDialog = null; + } } else { mOverlayContainer.setVisibility(VISIBLE); if (animate) { @@ -773,6 +806,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mLeftAffordanceView.setAlpha(alpha); mRightAffordanceView.setAlpha(alpha); mIndicationArea.setAlpha(alpha); + mAltLeftButton.setAlpha(alpha); } private class DefaultLeftButton implements IntentButton { @@ -844,4 +878,54 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } return insets; } + + /** + * Show or hide controls, depending on the lock screen mode and controls + * availability. + */ + public void setupControls(ControlsComponent component, BroadcastDispatcher dispatcher) { + mControlsComponent = component; + mBroadcastDispatcher = dispatcher; + setupControls(); + } + + private void setupControls() { + if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) { + mAltLeftButton.setVisibility(View.GONE); + mAltLeftButton.setOnClickListener(null); + return; + } + + if (Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 0) { + return; + } + + if (mControlsComponent.getControlsListingController().isPresent()) { + mControlsComponent.getControlsListingController().get() + .addCallback(list -> { + if (!list.isEmpty()) { + mAltLeftButton.setImageDrawable(list.get(0).loadIcon()); + mAltLeftButton.setVisibility(View.VISIBLE); + mAltLeftButton.setOnClickListener((v) -> { + ControlsUiController ui = mControlsComponent + .getControlsUiController().get(); + mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher) + .show(ui); + }); + + } else { + mAltLeftButton.setVisibility(View.GONE); + mAltLeftButton.setOnClickListener(null); + } + }); + } + } + + /** + * Optionally add controls when in the new lockscreen mode + */ + public void onLockScreenModeChanged(int mode) { + mLockScreenMode = mode; + setupControls(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 3b4759464891..e0ef3b6483a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -80,8 +80,10 @@ import com.android.systemui.DejankUtils; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.controls.dagger.ControlsComponent; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeLog; @@ -229,6 +231,7 @@ public class NotificationPanelViewController extends PanelViewController { public void onLockScreenModeChanged(int mode) { mLockScreenMode = mode; mClockPositionAlgorithm.onLockScreenModeChanged(mode); + mKeyguardBottomArea.onLockScreenModeChanged(mode); } @Override @@ -295,6 +298,8 @@ public class NotificationPanelViewController extends PanelViewController { private final QSDetailDisplayer mQSDetailDisplayer; private final FeatureFlags mFeatureFlags; private final ScrimController mScrimController; + private final ControlsComponent mControlsComponent; + // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card. // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications private final int mMaxKeyguardNotifications; @@ -503,6 +508,7 @@ public class NotificationPanelViewController extends PanelViewController { private NotificationShelfController mNotificationShelfController; private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; + private BroadcastDispatcher mBroadcastDispatcher; private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { @Override @@ -557,8 +563,9 @@ public class NotificationPanelViewController extends PanelViewController { ScrimController scrimController, MediaDataManager mediaDataManager, AmbientState ambientState, - FeatureFlags featureFlags - ) { + FeatureFlags featureFlags, + ControlsComponent controlsComponent, + BroadcastDispatcher broadcastDispatcher) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager, @@ -592,6 +599,7 @@ public class NotificationPanelViewController extends PanelViewController { mBiometricUnlockController = biometricUnlockController; mScrimController = scrimController; mMediaDataManager = mediaDataManager; + mControlsComponent = controlsComponent; pulseExpansionHandler.setPulseExpandAbortListener(() -> { if (mQs != null) { mQs.animateHeaderSlidingOut(); @@ -630,6 +638,7 @@ public class NotificationPanelViewController extends PanelViewController { mEntryManager = notificationEntryManager; mConversationNotificationManager = conversationNotificationManager; mAuthController = authController; + mBroadcastDispatcher = broadcastDispatcher; mView.setBackgroundColor(Color.TRANSPARENT); OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener(); @@ -838,6 +847,7 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper); mKeyguardBottomArea.setStatusBar(mStatusBar); mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete); + mKeyguardBottomArea.setupControls(mControlsComponent, mBroadcastDispatcher); } private void updateMaxDisplayedNotifications(boolean recompute) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index fa78ca6626e1..c07ba723ab43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -61,8 +61,10 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.controls.dagger.ControlsComponent; import com.android.systemui.doze.DozeLog; import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; @@ -207,6 +209,10 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private FeatureFlags mFeatureFlags; @Mock + private ControlsComponent mControlsComponent; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Mock private NotificationsQuickSettingsContainer mNotificationContainerParent; @Mock private AmbientState mAmbientState; @@ -300,7 +306,9 @@ public class NotificationPanelViewTest extends SysuiTestCase { mScrimController, mMediaDataManager, mAmbientState, - mFeatureFlags); + mFeatureFlags, + mControlsComponent, + mBroadcastDispatcher); mNotificationPanelViewController.initDependencies( mStatusBar, mNotificationShelfController); diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java index 4d95ef13a2a8..6dcad255eee4 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java @@ -20,14 +20,11 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; -import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; -import android.net.IConnectivityManager; +import android.net.ConnectivityManager; import android.os.Bundle; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; import android.text.SpannableStringBuilder; @@ -45,7 +42,7 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity private static final String TAG = "VpnDisconnected"; - private IConnectivityManager mService; + private ConnectivityManager mService; private int mUserId; private String mVpnPackage; @@ -53,10 +50,9 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mService = IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); mUserId = UserHandle.myUserId(); - mVpnPackage = getAlwaysOnVpnPackage(); + final ConnectivityManager cm = getSystemService(ConnectivityManager.class); + mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId); if (mVpnPackage == null) { finish(); return; @@ -102,15 +98,6 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity } } - private String getAlwaysOnVpnPackage() { - try { - return mService.getAlwaysOnVpnPackage(mUserId); - } catch (RemoteException e) { - Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e); - return null; - } - } - private CharSequence getVpnLabel() { try { return VpnConfig.getVpnLabel(this, mVpnPackage); diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java index e66f2cc17a7f..aab01d03b96d 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java @@ -18,15 +18,12 @@ package com.android.vpndialogs; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; -import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; -import android.net.IConnectivityManager; +import android.net.ConnectivityManager; import android.net.VpnManager; import android.os.Bundle; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.text.Html; @@ -48,7 +45,8 @@ public class ConfirmDialog extends AlertActivity private String mPackage; - private IConnectivityManager mService; + private ConnectivityManager mCm; // TODO: switch entirely to VpnManager once VPN code moves + private VpnManager mVm; public ConfirmDialog() { this(VpnManager.TYPE_VPN_SERVICE); @@ -62,10 +60,10 @@ public class ConfirmDialog extends AlertActivity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPackage = getCallingPackage(); - mService = IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); + mCm = getSystemService(ConnectivityManager.class); + mVm = getSystemService(VpnManager.class); - if (prepareVpn()) { + if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) { setResult(RESULT_OK); finish(); return; @@ -74,7 +72,7 @@ public class ConfirmDialog extends AlertActivity finish(); return; } - final String alwaysOnVpnPackage = getAlwaysOnVpnPackage(); + final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId()); // Can't prepare new vpn app when another vpn is always-on if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) { finish(); @@ -97,24 +95,6 @@ public class ConfirmDialog extends AlertActivity button.setFilterTouchesWhenObscured(true); } - private String getAlwaysOnVpnPackage() { - try { - return mService.getAlwaysOnVpnPackage(UserHandle.myUserId()); - } catch (RemoteException e) { - Log.e(TAG, "fail to call getAlwaysOnVpnPackage", e); - // Fallback to null to show the dialog - return null; - } - } - - private boolean prepareVpn() { - try { - return mService.prepareVpn(mPackage, null, UserHandle.myUserId()); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - } - private CharSequence getVpnLabel() { try { return VpnConfig.getVpnLabel(this, mPackage); @@ -146,10 +126,10 @@ public class ConfirmDialog extends AlertActivity @Override public void onClick(DialogInterface dialog, int which) { try { - if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) { + if (mVm.prepareVpn(null, mPackage, UserHandle.myUserId())) { // Authorize this app to initiate VPN connections in the future without user // intervention. - mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType); + mVm.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType); setResult(RESULT_OK); } } catch (Exception e) { diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java index 01dca7e30e64..1fc74f704f62 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java @@ -16,13 +16,11 @@ package com.android.vpndialogs; -import android.content.Context; import android.content.DialogInterface; -import android.net.IConnectivityManager; +import android.net.VpnManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; @@ -41,7 +39,7 @@ public class ManageDialog extends AlertActivity implements private VpnConfig mConfig; - private IConnectivityManager mService; + private VpnManager mVm; private TextView mDuration; private TextView mDataTransmitted; @@ -55,11 +53,9 @@ public class ManageDialog extends AlertActivity implements super.onCreate(savedInstanceState); try { + mVm = getSystemService(VpnManager.class); - mService = IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); - - mConfig = mService.getVpnConfig(UserHandle.myUserId()); + mConfig = mVm.getVpnConfig(UserHandle.myUserId()); // mConfig can be null if we are a restricted user, in that case don't show this dialog if (mConfig == null) { @@ -118,9 +114,9 @@ public class ManageDialog extends AlertActivity implements } else if (which == DialogInterface.BUTTON_NEUTRAL) { final int myUserId = UserHandle.myUserId(); if (mConfig.legacy) { - mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId); + mVm.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId); } else { - mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId); + mVm.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId); } } } catch (Exception e) { diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 8f093c7e6674..065e2bbd3eef 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -1113,7 +1113,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ try { final IBinder overlayWindowToken = new Binder(); mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY, - displayId); + displayId, null /* options */); synchronized (mLock) { mOverlayWindowTokens.put(displayId, overlayWindowToken); } diff --git a/services/core/Android.bp b/services/core/Android.bp index e01c4df42ff5..37d2cdc16926 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -100,10 +100,10 @@ java_library_static { libs: [ "services.net", "android.hardware.light-V2.0-java", - "android.hardware.gnss-java", - "android.hardware.power-java", + "android.hardware.gnss-V1-java", + "android.hardware.power-V1-java", "android.hardware.power-V1.0-java", - "android.hardware.vibrator-java", + "android.hardware.vibrator-V1-java", "android.net.ipsec.ike.stubs.module_lib", "app-compat-annotations", "framework-tethering.stubs.module_lib", @@ -128,22 +128,22 @@ java_library_static { "android.hardware.health-V1.0-java", "android.hardware.health-V2.0-java", "android.hardware.health-V2.1-java", - "android.hardware.light-java", + "android.hardware.light-V1-java", "android.hardware.tv.cec-V1.0-java", "android.hardware.weaver-V1.0-java", "android.hardware.biometrics.face-V1.1-java", - "android.hardware.biometrics.face-java", + "android.hardware.biometrics.face-V1-java", "android.hardware.biometrics.fingerprint-V2.3-java", - "android.hardware.biometrics.fingerprint-java", + "android.hardware.biometrics.fingerprint-V1-java", "android.hardware.oemlock-V1.0-java", "android.hardware.configstore-V1.0-java", "android.hardware.contexthub-V1.0-java", - "android.hardware.rebootescrow-java", + "android.hardware.rebootescrow-V1-java", "android.hardware.soundtrigger-V2.3-java", - "android.hardware.power.stats-java", + "android.hardware.power.stats-V1-java", "android.hidl.manager-V1.2-java", "capture_state_listener-aidl-java", - "dnsresolver_aidl_interface-java", + "dnsresolver_aidl_interface-V7-java", "icu4j_calendar_astronomer", "netd-client", "overlayable_policy_aidl-java", diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 9ccb0c786ebe..6886cdefc28a 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -997,28 +997,6 @@ public abstract class PackageManagerInternal { public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId); /** - * Register to listen for loading progress of an installed package. - * @param packageName The name of the installed package - * @param callback To loading reporting progress - * @param userId The user under which to check. - * @return Whether the registration was successful. It can fail if the package has not been - * installed yet. - */ - public abstract boolean registerInstalledLoadingProgressCallback(@NonNull String packageName, - @NonNull InstalledLoadingProgressCallback callback, int userId); - - /** - * Unregister to stop listening to loading progress of an installed package - * @param packageName The name of the installed package - * @param callback To unregister - * @return True if the callback is removed from registered callback list. False is the callback - * does not exist on the registered callback list, which can happen if the callback has - * already been unregistered. - */ - public abstract boolean unregisterInstalledLoadingProgressCallback(@NonNull String packageName, - @NonNull InstalledLoadingProgressCallback callback); - - /** * Returns the string representation of a known package. For example, * {@link #PACKAGE_SETUP_WIZARD} is represented by the string Setup Wizard. * diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index bf9d564e434a..3933e379e872 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -120,6 +120,7 @@ import android.net.NetworkState; import android.net.NetworkTestResultParcelable; import android.net.NetworkUtils; import android.net.NetworkWatchlistManager; +import android.net.OemNetworkPreferences; import android.net.PrivateDnsConfigParcel; import android.net.ProxyInfo; import android.net.QosCallbackException; @@ -281,15 +282,18 @@ public class ConnectivityService extends IConnectivityManager.Stub // connect anyway?" dialog after the user selects a network that doesn't validate. private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000; - // Default to 30s linger time-out. Modifiable only for testing. + // Default to 30s linger time-out, and 5s for nascent network. Modifiable only for testing. private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger"; private static final int DEFAULT_LINGER_DELAY_MS = 30_000; + private static final int DEFAULT_NASCENT_DELAY_MS = 5_000; // The maximum number of network request allowed per uid before an exception is thrown. private static final int MAX_NETWORK_REQUESTS_PER_UID = 100; @VisibleForTesting protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it. + @VisibleForTesting + protected int mNascentDelayMs; // How long to delay to removal of a pending intent based request. // See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS @@ -1063,6 +1067,8 @@ public class ConnectivityService extends IConnectivityManager.Stub Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000); mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); + // TODO: Consider making the timer customizable. + mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS; mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService"); mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService"); @@ -2030,7 +2036,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mHandler.sendMessage(mHandler.obtainMessage( EVENT_PRIVATE_DNS_VALIDATION_UPDATE, new PrivateDnsValidationUpdate(netId, - InetAddress.parseNumericAddress(ipAddress), + InetAddresses.parseNumericAddress(ipAddress), hostname, validated))); } catch (IllegalArgumentException e) { loge("Error parsing ip address in validation event"); @@ -3334,7 +3340,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 3. If this network is unneeded (which implies it is not lingering), and there is at least // one lingered request, set inactive. nai.updateInactivityTimer(); - if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) { + if (nai.isInactive() && nai.numForegroundNetworkRequests() > 0) { if (DBG) log("Unsetting inactive " + nai.toShortString()); nai.unsetInactive(); logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER); @@ -3588,10 +3594,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // As this request was not satisfied on rematch and thus never had any scores sent to the // factories, send null now for each request of type REQUEST. for (final NetworkRequest req : nri.mRequests) { - if (!req.isRequest()) { - continue; - } - sendUpdatedScoreToFactories(req, null); + if (req.isRequest()) sendUpdatedScoreToFactories(req, null); } } @@ -3631,7 +3634,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return true; } - if (!nai.everConnected || nai.isVPN() || nai.isLingering() || numRequests > 0) { + if (!nai.everConnected || nai.isVPN() || nai.isInactive() || numRequests > 0) { return false; } for (NetworkRequestInfo nri : mNetworkRequests.values()) { @@ -3768,7 +3771,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRequestInfoLogs.log("RELEASE " + nri); if (null != nri.getActiveRequest()) { - if (nri.getActiveRequest().isRequest()) { + if (!nri.getActiveRequest().isListen()) { removeSatisfiedNetworkRequestFromNetwork(nri); } else { nri.setSatisfier(null, null); @@ -5516,7 +5519,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // The network currently satisfying this NRI. Only one request in an NRI can have a - // satisfier. For non-multilayer requests, only REQUEST-type requests can have a satisfier. + // satisfier. For non-multilayer requests, only non-listen requests can have a satisfier. @Nullable private NetworkAgentInfo mSatisfier; NetworkAgentInfo getSatisfier() { @@ -7004,8 +7007,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) { for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest nr = nai.requestAt(i); - // Don't send listening requests to factories. b/17393458 - if (nr.isListen()) continue; + // Don't send listening or track default request to factories. b/17393458 + if (!nr.isRequest()) continue; sendUpdatedScoreToFactories(nr, nai); } } @@ -7067,10 +7070,10 @@ public class ConnectivityService extends IConnectivityManager.Stub ensureRunningOnConnectivityServiceThread(); for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) { for (final NetworkRequest req : nri.mRequests) { - if (req.isListen() && nri.getActiveRequest() == req) { + if (!req.isRequest() && nri.getActiveRequest() == req) { break; } - if (req.isListen()) { + if (!req.isRequest()) { continue; } // Only set the nai for the request it is satisfying. @@ -7220,8 +7223,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nai.numRequestNetworkRequests() != 0) { for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest nr = nai.requestAt(i); - // Ignore listening requests. - if (nr.isListen()) continue; + // Ignore listening and track default requests. + if (!nr.isRequest()) continue; loge("Dead network still had at least " + nr); break; } @@ -7244,7 +7247,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Tear the network down. teardownUnneededNetwork(oldNetwork); } else { - // Put the network in the background. + // Put the network in the background if it doesn't satisfy any foreground request. updateCapabilitiesForNetwork(oldNetwork); } } @@ -7498,6 +7501,14 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { if (VDBG || DDBG) log(" accepting network in place of null"); } + + // To prevent constantly CPU wake up for nascent timer, if a network comes up + // and immediately satisfies a request then remove the timer. This will happen for + // all networks except in the case of an underlying network for a VCN. + if (newSatisfier.isNascent()) { + newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE); + } + newSatisfier.unlingerRequest(newRequest.requestId); if (!newSatisfier.addRequest(newRequest)) { Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has " @@ -7640,19 +7651,19 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - // Update the linger state before processing listen callbacks, because the background - // computation depends on whether the network is lingering. Don't send the LOSING callbacks + // Update the inactivity state before processing listen callbacks, because the background + // computation depends on whether the network is inactive. Don't send the LOSING callbacks // just yet though, because they have to be sent after the listens are processed to keep // backward compatibility. - final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>(); + final ArrayList<NetworkAgentInfo> inactiveNetworks = new ArrayList<>(); for (final NetworkAgentInfo nai : nais) { - // Rematching may have altered the linger state of some networks, so update all linger - // timers. updateLingerState reads the state from the network agent and does nothing - // if the state has not changed : the source of truth is controlled with - // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been - // called while rematching the individual networks above. + // Rematching may have altered the inactivity state of some networks, so update all + // inactivity timers. updateInactivityState reads the state from the network agent + // and does nothing if the state has not changed : the source of truth is controlled + // with NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which + // have been called while rematching the individual networks above. if (updateInactivityState(nai, now)) { - lingeredNetworks.add(nai); + inactiveNetworks.add(nai); } } @@ -7669,7 +7680,11 @@ public class ConnectivityService extends IConnectivityManager.Stub processNewlySatisfiedListenRequests(nai); } - for (final NetworkAgentInfo nai : lingeredNetworks) { + for (final NetworkAgentInfo nai : inactiveNetworks) { + // For nascent networks, if connecting with no foreground request, skip broadcasting + // LOSING for backward compatibility. This is typical when mobile data connected while + // wifi connected with mobile data always-on enabled. + if (nai.isNascent()) continue; notifyNetworkLosing(nai, now); } @@ -7910,6 +7925,15 @@ public class ConnectivityService extends IConnectivityManager.Stub // doing. updateSignalStrengthThresholds(networkAgent, "CONNECT", null); + // Before first rematching networks, put an inactivity timer without any request, this + // allows {@code updateInactivityState} to update the state accordingly and prevent + // tearing down for any {@code unneeded} evaluation in this period. + // Note that the timer will not be rescheduled since the expiry time is + // fixed after connection regardless of the network satisfying other requests or not. + // But it will be removed as soon as the network satisfies a request for the first time. + networkAgent.lingerRequest(NetworkRequest.REQUEST_ID_NONE, + SystemClock.elapsedRealtime(), mNascentDelayMs); + // Consider network even though it is not yet validated. rematchAllNetworksAndRequests(); @@ -9100,6 +9124,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } } + /** * Registers {@link QosSocketFilter} with {@link IQosCallback}. * @@ -9149,4 +9174,10 @@ public class ConnectivityService extends IConnectivityManager.Stub public void unregisterQosCallback(@NonNull final IQosCallback callback) { mQosCallbackTracker.unregisterCallback(callback); } + + @Override + public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) { + // TODO http://b/176495594 track multiple default networks with networkPreferences + if (DBG) log("setOemNetworkPreference() called with: " + preference.toString()); + } } diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java index e96fd390f15a..96f832d26816 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/services/core/java/com/android/server/TestNetworkService.java @@ -50,6 +50,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.net.module.util.NetdUtils; +import com.android.net.module.util.NetworkStackConstants; import java.io.UncheckedIOException; import java.net.Inet4Address; @@ -280,10 +281,12 @@ class TestNetworkService extends ITestNetworkManager.Stub { // Add global routes (but as non-default, non-internet providing network) if (allowIPv4) { - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null, iface)); + lp.addRoute(new RouteInfo(new IpPrefix( + NetworkStackConstants.IPV4_ADDR_ANY, 0), null, iface)); } if (allowIPv6) { - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface)); + lp.addRoute(new RouteInfo(new IpPrefix( + NetworkStackConstants.IPV6_ADDR_ANY, 0), null, iface)); } final TestNetworkAgent agent = new TestNetworkAgent(context, looper, nc, lp, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6ae064435da3..9382e1aa2a9e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -145,6 +145,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManagerInternal; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.ActivityThread; +import android.app.AnrController; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.AppOpsManagerInternal.CheckOpsDelegate; @@ -15861,6 +15862,16 @@ public class ActivityManagerService extends IActivityManager.Stub // PackageManagerService. return mConstants.mBootTimeTempAllowlistDuration; } + + @Override + public void registerAnrController(AnrController controller) { + mActivityTaskManager.registerAnrController(controller); + } + + @Override + public void unregisterAnrController(AnrController controller) { + mActivityTaskManager.unregisterAnrController(controller); + } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index fe71fbf79157..c971bd2ab6d5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2984,7 +2984,13 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println("Reset all changes for " + packageName + " to default value."); return 0; } - if (platformCompat.clearOverride(changeId, packageName)) { + boolean existed; + if (killPackage) { + existed = platformCompat.clearOverride(changeId, packageName); + } else { + existed = platformCompat.clearOverrideForTest(changeId, packageName); + } + if (existed) { pw.println("Reset change " + changeId + " for " + packageName + " to default value."); } else { diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java index 64e307a5f182..165312352990 100644 --- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java @@ -416,6 +416,14 @@ class ProcessErrorStateRecord { return; } + // Retrieve max ANR delay from AnrControllers without the mService lock since the + // controllers might in turn call into apps + long anrDialogDelayMs = mService.mActivityTaskManager.getMaxAnrDelayMillis(aInfo); + if (aInfo != null && aInfo.packageName != null && anrDialogDelayMs > 0) { + Slog.i(TAG, "Delaying ANR dialog for " + aInfo.packageName + " for " + anrDialogDelayMs + + "ms"); + } + synchronized (mService) { // mBatteryStatsService can be null if the AMS is constructed with injector only. This // will only happen in tests. @@ -447,7 +455,7 @@ class ProcessErrorStateRecord { msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG; msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem); - mService.mUiHandler.sendMessage(msg); + mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs); } } } diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 7299e814b020..e022e977e02f 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -81,6 +81,7 @@ public class SettingsToPropertiesMapper { static final String[] sDeviceConfigScopes = new String[] { DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, DeviceConfig.NAMESPACE_CONFIGURATION, + DeviceConfig.NAMESPACE_CONNECTIVITY, DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT, DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS, DeviceConfig.NAMESPACE_MEDIA_NATIVE, diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 9aea7c4c6dad..f72fb1f74fa8 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -765,10 +765,6 @@ import java.util.concurrent.atomic.AtomicBoolean; return mAudioService.getVssVolumeForDevice(streamType, device); } - /*package*/ int getModeOwnerPid() { - return mModeOwnerPid; - } - /*package*/ int getDeviceForStream(int streamType) { return mAudioService.getDeviceForStream(streamType); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index e7e98324605a..6f625a745ef6 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -95,6 +95,7 @@ import android.media.IVolumeController; import android.media.MediaExtractor; import android.media.MediaFormat; import android.media.MediaMetrics; +import android.media.MediaRecorder.AudioSource; import android.media.PlayerBase; import android.media.VolumePolicy; import android.media.audiofx.AudioEffect; @@ -161,9 +162,11 @@ import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -301,6 +304,9 @@ public class AudioService extends IAudioService.Stub private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33; private static final int MSG_REINIT_VOLUMES = 34; private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35; + private static final int MSG_UPDATE_AUDIO_MODE = 36; + private static final int MSG_RECORDING_CONFIG_CHANGE = 37; + // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -941,6 +947,7 @@ public class AudioService extends IAudioService.Stub mDeviceBroker = new AudioDeviceBroker(mContext, this); mRecordMonitor = new RecordingActivityMonitor(mContext); + mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true); // must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[] // array initialized by updateStreamVolumeAlias() @@ -950,6 +957,8 @@ public class AudioService extends IAudioService.Stub mPlaybackMonitor = new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]); + mPlaybackMonitor.registerPlaybackCallback(mVoicePlaybackActivityMonitor, true); + mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor); readAndSetLowRamDevice(); @@ -1198,12 +1207,8 @@ public class AudioService extends IAudioService.Stub // Restore call state synchronized (mDeviceBroker.mSetModeLock) { - if (mAudioSystem.setPhoneState(mMode, getModeOwnerUid()) - == AudioSystem.AUDIO_STATUS_OK) { - mModeLogger.log(new AudioEventLogger.StringEvent( - "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode) - + ", uid=" + getModeOwnerUid() + ")")); - } + onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(), + mContext.getPackageName()); } final int forSys; synchronized (mSettingsLock) { @@ -2991,7 +2996,7 @@ public class AudioService extends IAudioService.Stub } /*package*/ int getHearingAidStreamType() { - return getHearingAidStreamType(mMode); + return getHearingAidStreamType(getMode()); } private int getHearingAidStreamType(int mode) { @@ -3004,15 +3009,15 @@ public class AudioService extends IAudioService.Stub // other conditions will influence the stream type choice, read on... break; } - if (mVoiceActive.get()) { + if (mVoicePlaybackActive.get()) { return AudioSystem.STREAM_VOICE_CALL; } return AudioSystem.STREAM_MUSIC; } - private AtomicBoolean mVoiceActive = new AtomicBoolean(false); + private AtomicBoolean mVoicePlaybackActive = new AtomicBoolean(false); - private final IPlaybackConfigDispatcher mVoiceActivityMonitor = + private final IPlaybackConfigDispatcher mVoicePlaybackActivityMonitor = new IPlaybackConfigDispatcher.Stub() { @Override public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs, @@ -3034,16 +3039,126 @@ public class AudioService extends IAudioService.Stub break; } } - if (mVoiceActive.getAndSet(voiceActive) != voiceActive) { + if (mVoicePlaybackActive.getAndSet(voiceActive) != voiceActive) { updateHearingAidVolumeOnVoiceActivityUpdate(); } + + // Update playback active state for all apps in audio mode stack. + // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE + // and request an audio mode update immediately. Upon any other change, queue the message + // and request an audio mode update after a grace period. + synchronized (mDeviceBroker.mSetModeLock) { + boolean updateAudioMode = false; + int existingMsgPolicy = SENDMSG_QUEUE; + int delay = CHECK_MODE_FOR_UID_PERIOD_MS; + for (SetModeDeathHandler h : mSetModeDeathHandlers) { + boolean wasActive = h.isActive(); + h.setPlaybackActive(false); + for (AudioPlaybackConfiguration config : configs) { + final int usage = config.getAudioAttributes().getUsage(); + if (config.getClientUid() == h.getUid() + && (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION + || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING) + && config.getPlayerState() + == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + h.setPlaybackActive(true); + break; + } + } + if (wasActive != h.isActive()) { + updateAudioMode = true; + if (h.isActive() && h == getAudioModeOwnerHandler()) { + existingMsgPolicy = SENDMSG_REPLACE; + delay = 0; + } + } + } + if (updateAudioMode) { + sendMsg(mAudioHandler, + MSG_UPDATE_AUDIO_MODE, + existingMsgPolicy, + AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), + mContext.getPackageName(), + delay); + } + } + } + + private final IRecordingConfigDispatcher mVoiceRecordingActivityMonitor = + new IRecordingConfigDispatcher.Stub() { + @Override + public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) { + sendMsg(mAudioHandler, MSG_RECORDING_CONFIG_CHANGE, SENDMSG_REPLACE, + 0 /*arg1 ignored*/, 0 /*arg2 ignored*/, + configs /*obj*/, 0 /*delay*/); + } + }; + + private void onRecordingConfigChange(List<AudioRecordingConfiguration> configs) { + // Update recording active state for all apps in audio mode stack. + // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE + // and request an audio mode update immediately. Upon any other change, queue the message + // and request an audio mode update after a grace period. + synchronized (mDeviceBroker.mSetModeLock) { + boolean updateAudioMode = false; + int existingMsgPolicy = SENDMSG_QUEUE; + int delay = CHECK_MODE_FOR_UID_PERIOD_MS; + for (SetModeDeathHandler h : mSetModeDeathHandlers) { + boolean wasActive = h.isActive(); + h.setRecordingActive(false); + for (AudioRecordingConfiguration config : configs) { + if (config.getClientUid() == h.getUid() + && config.getAudioSource() == AudioSource.VOICE_COMMUNICATION) { + h.setRecordingActive(true); + break; + } + } + if (wasActive != h.isActive()) { + updateAudioMode = true; + if (h.isActive() && h == getAudioModeOwnerHandler()) { + existingMsgPolicy = SENDMSG_REPLACE; + delay = 0; + } + } + } + if (updateAudioMode) { + sendMsg(mAudioHandler, + MSG_UPDATE_AUDIO_MODE, + existingMsgPolicy, + AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), + mContext.getPackageName(), + delay); + } + } + } + + private void dumpAudioMode(PrintWriter pw) { + pw.println("\nAudio mode: "); + pw.println("- Current mode = " + AudioSystem.modeToString(getMode())); + pw.println("- Mode owner: "); + SetModeDeathHandler hdlr = getAudioModeOwnerHandler(); + if (hdlr != null) { + hdlr.dump(pw, -1); + } else { + pw.println(" None"); + } + pw.println("- Mode owner stack: "); + if (mSetModeDeathHandlers.isEmpty()) { + pw.println(" Empty"); + } else { + for (int i = 0; i < mSetModeDeathHandlers.size(); i++) { + mSetModeDeathHandlers.get(i).dump(pw, i); + } + } } private void updateHearingAidVolumeOnVoiceActivityUpdate() { final int streamType = getHearingAidStreamType(); final int index = getStreamVolume(streamType); sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID, - mVoiceActive.get(), streamType, index)); + mVoicePlaybackActive.get(), streamType, index)); mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType); } @@ -4060,65 +4175,46 @@ public class AudioService extends IAudioService.Stub } - /** - * Return the pid of the current audio mode owner - * @return 0 if nobody owns the mode - */ - /*package*/ int getModeOwnerPid() { - int modeOwnerPid = 0; - try { - modeOwnerPid = mSetModeDeathHandlers.get(0).getPid(); - } catch (Exception e) { - // nothing to do, modeOwnerPid is not modified - } - return modeOwnerPid; - } - - /** - * Return the uid of the current audio mode owner - * @return 0 if nobody owns the mode - */ - /*package*/ int getModeOwnerUid() { - int modeOwnerUid = 0; - try { - modeOwnerUid = mSetModeDeathHandlers.get(0).getUid(); - } catch (Exception e) { - // nothing to do, modeOwnerUid is not modified - } - return modeOwnerUid; - } - private class SetModeDeathHandler implements IBinder.DeathRecipient { private final IBinder mCb; // To be notified of client's death private final int mPid; private final int mUid; private final boolean mIsPrivileged; private final String mPackage; - private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client + private int mMode; + private long mUpdateTime; + private boolean mPlaybackActive = false; + private boolean mRecordingActive = false; - SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { + SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, + String caller, int mode) { + mMode = mode; mCb = cb; mPid = pid; mUid = uid; - mIsPrivileged = isPrivileged; mPackage = caller; + mIsPrivileged = isPrivileged; + mUpdateTime = java.lang.System.currentTimeMillis(); } public void binderDied() { - int newModeOwnerPid = 0; synchronized (mDeviceBroker.mSetModeLock) { - Log.w(TAG, "setMode() client died"); + Log.w(TAG, "SetModeDeathHandler client died"); int index = mSetModeDeathHandlers.indexOf(this); if (index < 0) { - Log.w(TAG, "unregistered setMode() client died"); + Log.w(TAG, "unregistered SetModeDeathHandler client died"); } else { - newModeOwnerPid = setModeInt( - AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG); + SetModeDeathHandler h = mSetModeDeathHandlers.get(index); + mSetModeDeathHandlers.remove(index); + sendMsg(mAudioHandler, + MSG_UPDATE_AUDIO_MODE, + SENDMSG_QUEUE, + AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), + mContext.getPackageName(), + 0); } } - // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all - // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode()); } public int getPid() { @@ -4127,6 +4223,7 @@ public class AudioService extends IAudioService.Stub public void setMode(int mode) { mMode = mode; + mUpdateTime = java.lang.System.currentTimeMillis(); } public int getMode() { @@ -4148,25 +4245,131 @@ public class AudioService extends IAudioService.Stub public boolean isPrivileged() { return mIsPrivileged; } + + public long getUpdateTime() { + return mUpdateTime; + } + + public void setPlaybackActive(boolean active) { + mPlaybackActive = active; + } + + public void setRecordingActive(boolean active) { + mRecordingActive = active; + } + + /** + * An app is considered active if: + * - It is privileged (has MODIFY_PHONE_STATE permission) + * or + * - It requests mode MODE_IN_COMMUNICATION, and it is either playing + * or recording for VOICE_COMMUNICATION. + * or + * - It requests a mode different from MODE_IN_COMMUNICATION or MODE_NORMAL + */ + public boolean isActive() { + return mIsPrivileged + || ((mMode == AudioSystem.MODE_IN_COMMUNICATION) + && (mRecordingActive || mPlaybackActive)) + || mMode == AudioSystem.MODE_IN_CALL + || mMode == AudioSystem.MODE_RINGTONE + || mMode == AudioSystem.MODE_CALL_SCREENING; + } + + public void dump(PrintWriter pw, int index) { + SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss:SSS"); + + if (index >= 0) { + pw.println(" Requester # " + (index + 1) + ":"); + } + pw.println(" - Mode: " + AudioSystem.modeToString(mMode)); + pw.println(" - Binder: " + mCb); + pw.println(" - Pid: " + mPid); + pw.println(" - Uid: " + mUid); + pw.println(" - Package: " + mPackage); + pw.println(" - Privileged: " + mIsPrivileged); + pw.println(" - Active: " + isActive()); + pw.println(" Playback active: " + mPlaybackActive); + pw.println(" Recording active: " + mRecordingActive); + pw.println(" - update time: " + format.format(new Date(mUpdateTime))); + } + } + + @GuardedBy("mDeviceBroker.mSetModeLock") + private SetModeDeathHandler getAudioModeOwnerHandler() { + // The Audio mode owner is: + // 1) the most recent privileged app in the stack + // 2) the most recent active app in the tack + SetModeDeathHandler modeOwner = null; + SetModeDeathHandler privilegedModeOwner = null; + for (SetModeDeathHandler h : mSetModeDeathHandlers) { + if (h.isActive()) { + // privileged apps are always active + if (h.isPrivileged()) { + if (privilegedModeOwner == null + || h.getUpdateTime() > privilegedModeOwner.getUpdateTime()) { + privilegedModeOwner = h; + } + } else { + if (modeOwner == null + || h.getUpdateTime() > modeOwner.getUpdateTime()) { + modeOwner = h; + } + } + } + } + return privilegedModeOwner != null ? privilegedModeOwner : modeOwner; + } + + /** + * Return the pid of the current audio mode owner + * @return 0 if nobody owns the mode + */ + @GuardedBy("mDeviceBroker.mSetModeLock") + /*package*/ int getModeOwnerPid() { + SetModeDeathHandler hdlr = getAudioModeOwnerHandler(); + if (hdlr != null) { + return hdlr.getPid(); + } + return 0; + } + + /** + * Return the uid of the current audio mode owner + * @return 0 if nobody owns the mode + */ + @GuardedBy("mDeviceBroker.mSetModeLock") + /*package*/ int getModeOwnerUid() { + SetModeDeathHandler hdlr = getAudioModeOwnerHandler(); + if (hdlr != null) { + return hdlr.getUid(); + } + return 0; } /** @see AudioManager#setMode(int) */ public void setMode(int mode, IBinder cb, String callingPackage) { + int pid = Binder.getCallingPid(); + int uid = Binder.getCallingUid(); if (DEBUG_MODE) { - Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); + Log.v(TAG, "setMode(mode=" + mode + ", pid=" + pid + + ", uid=" + uid + ", caller=" + callingPackage + ")"); } if (!checkAudioSettingsPermission("setMode()")) { return; } - final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission( - android.Manifest.permission.MODIFY_PHONE_STATE) - == PackageManager.PERMISSION_GRANTED; - final int callingPid = Binder.getCallingPid(); - if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) { - Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid=" - + callingPid + ", uid=" + Binder.getCallingUid()); + if (cb == null) { + Log.e(TAG, "setMode() called with null binder"); return; } + if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) { + Log.w(TAG, "setMode() invalid mode: " + mode); + return; + } + + if (mode == AudioSystem.MODE_CURRENT) { + mode = getMode(); + } if (mode == AudioSystem.MODE_CALL_SCREENING && !mIsCallScreeningModeSupported) { Log.w(TAG, "setMode(MODE_CALL_SCREENING) not permitted " @@ -4174,171 +4377,152 @@ public class AudioService extends IAudioService.Stub return; } - if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) { + final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_PHONE_STATE) + == PackageManager.PERMISSION_GRANTED; + if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) { + Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid=" + + pid + ", uid=" + Binder.getCallingUid()); return; } - int newModeOwnerPid; + SetModeDeathHandler currentModeHandler = null; synchronized (mDeviceBroker.mSetModeLock) { - if (mode == AudioSystem.MODE_CURRENT) { - mode = mMode; - } - int oldModeOwnerPid = getModeOwnerPid(); - // Do not allow changing mode if a call is active and the requester - // does not have permission to modify phone state or is not the mode owner, - // unless returning to NORMAL mode (will not change current mode owner) or - // not changing mode in which case the mode owner will reflect the last - // requester of current mode - if (!((mode == mMode) || (mode == AudioSystem.MODE_NORMAL)) - && ((mMode == AudioSystem.MODE_IN_CALL) - || (mMode == AudioSystem.MODE_IN_COMMUNICATION)) - && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) { - Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid - + ", uid=" + Binder.getCallingUid() - + ", cannot change mode from " + mMode - + " without permission or being mode owner"); - return; - } - newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(), - hasModifyPhoneStatePermission, callingPackage); - } - // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all - // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode()); - } - - // setModeInt() returns a valid PID if the audio mode was successfully set to - // any mode other than NORMAL. - @GuardedBy("mDeviceBroker.mSetModeLock") - private int setModeInt( - int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { - if (DEBUG_MODE) { - Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid - + ", uid=" + uid + ", caller=" + caller + ")"); - } - int newModeOwnerPid = 0; - if (cb == null) { - Log.e(TAG, "setModeInt() called with null binder"); - return newModeOwnerPid; - } - - SetModeDeathHandler hdlr = null; - Iterator iter = mSetModeDeathHandlers.iterator(); - while (iter.hasNext()) { - SetModeDeathHandler h = (SetModeDeathHandler)iter.next(); - if (h.getPid() == pid) { - hdlr = h; - // Remove from client list so that it is re-inserted at top of list - iter.remove(); - if (hdlr.getMode() == AudioSystem.MODE_IN_COMMUNICATION) { - mAudioHandler.removeEqualMessages(MSG_CHECK_MODE_FOR_UID, hdlr); - } - try { - hdlr.getBinder().unlinkToDeath(hdlr, 0); - if (cb != hdlr.getBinder()){ - hdlr = null; - } - } catch (NoSuchElementException e) { - hdlr = null; - Log.w(TAG, "link does not exist ..."); + for (SetModeDeathHandler h : mSetModeDeathHandlers) { + if (h.getPid() == pid) { + currentModeHandler = h; + break; } - break; } - } - final int oldMode = mMode; - int status = AudioSystem.AUDIO_STATUS_OK; - int actualMode; - do { - actualMode = mode; + if (mode == AudioSystem.MODE_NORMAL) { - // get new mode from client at top the list if any - if (!mSetModeDeathHandlers.isEmpty()) { - hdlr = mSetModeDeathHandlers.get(0); - cb = hdlr.getBinder(); - actualMode = hdlr.getMode(); + if (currentModeHandler != null) { + if (!currentModeHandler.isPrivileged() + && currentModeHandler.getMode() == AudioSystem.MODE_IN_COMMUNICATION) { + mAudioHandler.removeEqualMessages( + MSG_CHECK_MODE_FOR_UID, currentModeHandler); + } + mSetModeDeathHandlers.remove(currentModeHandler); if (DEBUG_MODE) { - Log.w(TAG, " using mode=" + mode + " instead due to death hdlr at pid=" - + hdlr.mPid); + Log.v(TAG, "setMode(" + mode + ") removing hldr for pid: " + pid); + } + try { + currentModeHandler.getBinder().unlinkToDeath(currentModeHandler, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "setMode link does not exist ..."); } } } else { - if (hdlr == null) { - hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller); - } - // Register for client death notification - try { - cb.linkToDeath(hdlr, 0); - } catch (RemoteException e) { - // Client has died! - Log.w(TAG, "setMode() could not link to "+cb+" binder death"); - } - - // Last client to call setMode() is always at top of client list - // as required by SetModeDeathHandler.binderDied() - mSetModeDeathHandlers.add(0, hdlr); - hdlr.setMode(mode); - } - - if (actualMode != mMode) { - final long identity = Binder.clearCallingIdentity(); - status = mAudioSystem.setPhoneState(actualMode, getModeOwnerUid()); - Binder.restoreCallingIdentity(identity); - if (status == AudioSystem.AUDIO_STATUS_OK) { - if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); } - mMode = actualMode; + if (currentModeHandler != null) { + currentModeHandler.setMode(mode); + if (DEBUG_MODE) { + Log.v(TAG, "setMode(" + mode + ") updating hldr for pid: " + pid); + } } else { - if (hdlr != null) { - mSetModeDeathHandlers.remove(hdlr); - cb.unlinkToDeath(hdlr, 0); + currentModeHandler = new SetModeDeathHandler(cb, pid, uid, + hasModifyPhoneStatePermission, callingPackage, mode); + // Register for client death notification + try { + cb.linkToDeath(currentModeHandler, 0); + } catch (RemoteException e) { + // Client has died! + Log.w(TAG, "setMode() could not link to " + cb + " binder death"); + return; + } + mSetModeDeathHandlers.add(currentModeHandler); + if (DEBUG_MODE) { + Log.v(TAG, "setMode(" + mode + ") adding handler for pid=" + pid); } - // force reading new top of mSetModeDeathHandlers stack - if (DEBUG_MODE) { Log.w(TAG, " mode set to MODE_NORMAL after phoneState pb"); } - mode = AudioSystem.MODE_NORMAL; } - } else { - status = AudioSystem.AUDIO_STATUS_OK; - } - } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty()); - - if (status == AudioSystem.AUDIO_STATUS_OK) { - if (actualMode != AudioSystem.MODE_NORMAL) { - newModeOwnerPid = getModeOwnerPid(); - if (newModeOwnerPid == 0) { - Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack"); + if (mode == AudioSystem.MODE_IN_COMMUNICATION) { + // Force active state when entering/updating the stack to avoid glitches when + // an app starts playing/recording after settng the audio mode, + // and send a reminder to check activity after a grace period. + if (!currentModeHandler.isPrivileged()) { + currentModeHandler.setPlaybackActive(true); + currentModeHandler.setRecordingActive(true); + sendMsg(mAudioHandler, + MSG_CHECK_MODE_FOR_UID, + SENDMSG_QUEUE, + 0, + 0, + currentModeHandler, + CHECK_MODE_FOR_UID_PERIOD_MS); + } } } - // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL - mModeLogger.log( - new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode)); - int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); - int device = getDeviceForStream(streamType); - int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device); - setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, caller, - true /*hasModifyAudioSettings*/); - - updateStreamVolumeAlias(true /*updateVolumes*/, caller); - - // change of mode may require volume to be re-applied on some devices - updateAbsVolumeMultiModeDevices(oldMode, actualMode); + sendMsg(mAudioHandler, + MSG_UPDATE_AUDIO_MODE, + SENDMSG_REPLACE, + mode, + pid, + callingPackage, + 0); + } + } - if (actualMode == AudioSystem.MODE_IN_COMMUNICATION - && !hdlr.isPrivileged()) { - sendMsg(mAudioHandler, - MSG_CHECK_MODE_FOR_UID, - SENDMSG_QUEUE, - 0, - 0, - hdlr, - CHECK_MODE_FOR_UID_PERIOD_MS); + @GuardedBy("mDeviceBroker.mSetModeLock") + void onUpdateAudioMode(int requestedMode, int requesterPid, String requesterPackage) { + if (requestedMode == AudioSystem.MODE_CURRENT) { + requestedMode = getMode(); + } + int mode = AudioSystem.MODE_NORMAL; + int uid = 0; + int pid = 0; + SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler(); + if (currentModeHandler != null) { + mode = currentModeHandler.getMode(); + uid = currentModeHandler.getUid(); + pid = currentModeHandler.getPid(); + } + if (DEBUG_MODE) { + Log.v(TAG, "onUpdateAudioMode() mode: " + mode + ", mMode: " + mMode + + " requestedMode: " + requestedMode); + } + if (mode != mMode) { + final long identity = Binder.clearCallingIdentity(); + int status = mAudioSystem.setPhoneState(mode, uid); + Binder.restoreCallingIdentity(identity); + if (status == AudioSystem.AUDIO_STATUS_OK) { + if (DEBUG_MODE) { + Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode); + } + int previousMode = mMode; + mMode = mode; + // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL + mModeLogger.log(new PhoneStateEvent(requesterPackage, requesterPid, + requestedMode, pid, mode)); + + int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); + int device = getDeviceForStream(streamType); + int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device); + setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, + requesterPackage, true /*hasModifyAudioSettings*/); + + updateStreamVolumeAlias(true /*updateVolumes*/, requesterPackage); + + // change of mode may require volume to be re-applied on some devices + updateAbsVolumeMultiModeDevices(previousMode, mode); + + // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO + // connections not started by the application changing the mode when pid changes + mDeviceBroker.postSetModeOwnerPid(pid, mode); + } else { + Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode); } } - return newModeOwnerPid; } /** @see AudioManager#getMode() */ public int getMode() { - return mMode; + synchronized (mDeviceBroker.mSetModeLock) { + SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler(); + if (currentModeHandler != null) { + return currentModeHandler.getMode(); + } + return AudioSystem.MODE_NORMAL; + } } /** cached value read from audiopolicy manager after initialization. */ @@ -5685,11 +5869,6 @@ public class AudioService extends IAudioService.Stub throw new IllegalArgumentException("Illegal BluetoothProfile state for device " + " (dis)connection, got " + state); } - if (state == BluetoothProfile.STATE_CONNECTED) { - mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true); - } else { - mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor); - } mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( device, state, suppressNoisyIntent, musicDevice, "AudioService"); } @@ -7034,6 +7213,9 @@ public class AudioService extends IAudioService.Stub case MSG_PLAYBACK_CONFIG_CHANGE: onPlaybackConfigChange((List<AudioPlaybackConfiguration>) msg.obj); break; + case MSG_RECORDING_CONFIG_CHANGE: + onRecordingConfigChange((List<AudioRecordingConfiguration>) msg.obj); + break; case MSG_BROADCAST_MICROPHONE_MUTE: mSystemServer.sendMicrophoneMuteChangedIntent(); @@ -7044,30 +7226,19 @@ public class AudioService extends IAudioService.Stub if (msg.obj == null) { break; } - // If no other app is currently owning the audio mode and - // the app corresponding to this mode death handler object is still in the - // mode owner stack but not capturing or playing audio after 3 seconds, - // remove it from the stack. - // Otherwise, check again in 3 seconds. + // Update active playback/recording for apps requesting IN_COMMUNICATION + // mode after a grace period following the mode change SetModeDeathHandler h = (SetModeDeathHandler) msg.obj; if (mSetModeDeathHandlers.indexOf(h) < 0) { break; } - if (getModeOwnerUid() != h.getUid() - || mRecordMonitor.isRecordingActiveForUid(h.getUid()) - || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) { - sendMsg(mAudioHandler, - MSG_CHECK_MODE_FOR_UID, - SENDMSG_QUEUE, - 0, - 0, - h, - CHECK_MODE_FOR_UID_PERIOD_MS); - break; + boolean wasActive = h.isActive(); + h.setPlaybackActive(mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())); + h.setRecordingActive(mRecordMonitor.isRecordingActiveForUid(h.getUid())); + if (wasActive != h.isActive()) { + onUpdateAudioMode(AudioSystem.MODE_CURRENT, + android.os.Process.myPid(), mContext.getPackageName()); } - setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(), - h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID"); - mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid())); } break; @@ -7084,10 +7255,16 @@ public class AudioService extends IAudioService.Stub case MSG_REINIT_VOLUMES: onReinitVolumes((String) msg.obj); break; + case MSG_UPDATE_A11Y_SERVICE_UIDS: onUpdateAccessibilityServiceUids(); break; + case MSG_UPDATE_AUDIO_MODE: + synchronized (mDeviceBroker.mSetModeLock) { + onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj); + } + break; } } } @@ -8118,6 +8295,7 @@ public class AudioService extends IAudioService.Stub dumpStreamStates(pw); dumpVolumeGroups(pw); dumpRingerMode(pw); + dumpAudioMode(pw); pw.println("\nAudio routes:"); pw.print(" mMainType=0x"); pw.println(Integer.toHexString( mDeviceBroker.getCurAudioRoutes().mainType)); @@ -9088,6 +9266,18 @@ public class AudioService extends IAudioService.Stub } /** + * Update player session ID + * @param piid Player id to update + * @param sessionId The new audio session ID + */ + public void playerSessionId(int piid, int sessionId) { + if (sessionId <= AudioSystem.AUDIO_SESSION_ALLOCATE) { + throw new IllegalArgumentException("invalid session Id " + sessionId); + } + mPlaybackMonitor.playerSessionId(piid, sessionId, Binder.getCallingUid()); + } + + /** * Update player event * @param piid Player id to update * @param event The new player event diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index 36c67cdbac4b..68a084e6d249 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -197,6 +197,28 @@ public final class PlaybackActivityMonitor } } + /** + * Update player session ID + * @param piid Player id to update + * @param sessionId The new audio session ID + * @param binderUid Calling binder uid + */ + public void playerSessionId(int piid, int sessionId, int binderUid) { + final boolean change; + synchronized (mPlayerLock) { + final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); + if (checkConfigurationCaller(piid, apc, binderUid)) { + change = apc.handleSessionIdEvent(sessionId); + } else { + Log.e(TAG, "Error updating audio session"); + change = false; + } + } + if (change) { + dispatchPlaybackChange(false); + } + } + private static final int FLAGS_FOR_SILENCE_OVERRIDE = AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY | AudioAttributes.FLAG_BYPASS_MUTE; @@ -921,6 +943,7 @@ public final class PlaybackActivityMonitor private final int mClientUid; private final int mClientPid; private final AudioAttributes mPlayerAttr; + private final int mSessionId; NewPlayerEvent(AudioPlaybackConfiguration apc) { mPlayerIId = apc.getPlayerInterfaceId(); @@ -928,6 +951,7 @@ public final class PlaybackActivityMonitor mClientUid = apc.getClientUid(); mClientPid = apc.getClientPid(); mPlayerAttr = apc.getAudioAttributes(); + mSessionId = apc.getSessionId(); } @Override @@ -935,7 +959,8 @@ public final class PlaybackActivityMonitor return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/" + mClientPid + " type:" + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType) - + " attr:" + mPlayerAttr); + + " attr:" + mPlayerAttr + + " session:" + mSessionId); } } 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..b0e42cd137eb 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. @@ -190,7 +197,7 @@ public class FingerprintService extends SystemService implements BiometricServic @Override // Binder call public void enroll(final IBinder token, final byte[] hardwareAuthToken, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName, - boolean shouldLogMetrics) { + @FingerprintManager.EnrollReason int enrollReason) { Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); final Pair<Integer, ServiceProvider> provider = getSingleProvider(); @@ -200,7 +207,7 @@ public class FingerprintService extends SystemService implements BiometricServic } provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId, - receiver, opPackageName, shouldLogMetrics); + receiver, opPackageName, enrollReason); } @Override // Binder call @@ -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/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 303c080c044c..f672ae56e020 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -78,7 +78,7 @@ public interface ServiceProvider { void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, - boolean shouldLogMetrics); + @FingerprintManager.EnrollReason int enrollReason); void cancelEnrollment(int sensorId, @NonNull IBinder token); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java index d092e860e208..37f8e8c2c1ee 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java @@ -65,6 +65,17 @@ public class UdfpsHelper { } } + public static int getReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) { + switch (reason) { + case FingerprintManager.ENROLL_FIND_SENSOR: + return IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR; + case FingerprintManager.ENROLL_ENROLL: + return IUdfpsOverlayController.REASON_ENROLL_ENROLLING; + default: + return IUdfpsOverlayController.REASON_UNKNOWN; + } + } + public static void showUdfpsOverlay(int sensorId, int reason, @Nullable IUdfpsOverlayController udfpsOverlayController) { if (udfpsOverlayController == null) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java index c2a30be6e2cb..ea9c709ec79f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.ITestSession; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.os.Binder; import android.util.Slog; @@ -131,7 +132,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, - mContext.getOpPackageName(), true /* shouldLogMetrics */); + mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index 08cc464b4766..ae64c77f1365 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -25,6 +25,7 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; @@ -43,6 +44,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { private static final String TAG = "FingerprintEnrollClient"; @Nullable private final IUdfpsOverlayController mUdfpsOverlayController; + private final @FingerprintManager.EnrollReason int mEnrollReason; @Nullable private ICancellationSignal mCancellationSignal; private final int mMaxTemplatesPerUser; @@ -52,13 +54,17 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, @Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser, - boolean shouldLogMetrics) { + @FingerprintManager.EnrollReason int enrollReason) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, true /* shouldVibrate */); mUdfpsOverlayController = udfpsOvelayController; mMaxTemplatesPerUser = maxTemplatesPerUser; - setShouldLog(shouldLogMetrics); + + mEnrollReason = enrollReason; + if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) { + setShouldLog(false); + } } @Override @@ -72,6 +78,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { } } + @Override public void onAcquired(int acquiredInfo, int vendorCode) { super.onAcquired(acquiredInfo, vendorCode); @@ -112,7 +119,8 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { @Override protected void startHalOperation() { - UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_ENROLL, + UdfpsHelper.showUdfpsOverlay(getSensorId(), + UdfpsHelper.getReasonFromEnrollReason(mEnrollReason), mUdfpsOverlayController); try { mCancellationSignal = getFreshDaemon().enroll(mSequentialId, 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..ced46e140c0a 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 @@ -28,6 +28,7 @@ import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorProps; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; @@ -96,7 +97,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 } @@ -365,7 +367,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, - @NonNull String opPackageName, boolean shouldLogMetrics) { + @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason) { mHandler.post(() -> { final IFingerprint daemon = getHalInstance(); if (daemon == null) { @@ -387,7 +389,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(sensorId), sensorId, - mUdfpsOverlayController, maxTemplatesPerUser, shouldLogMetrics); + mUdfpsOverlayController, maxTemplatesPerUser, enrollReason); scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java index 6893e72486bc..312ee0a267ac 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.ITestSession; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.os.Binder; import android.util.Slog; @@ -132,7 +133,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, - mContext.getOpPackageName(), true/* shouldLogMetrics */); + mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL); } @Override 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 1135126d86fb..7a74c6a39aa1 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 @@ -33,6 +33,7 @@ import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintServiceReceiver; @@ -49,6 +50,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.SensorServiceStateProto; import com.android.server.biometrics.SensorStateProto; @@ -113,7 +115,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @NonNull private final HalResultController mHalResultController; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; private int mCurrentUserId = UserHandle.USER_NULL; - private boolean mIsUdfps = false; + private final boolean mIsUdfps; private final int mSensorId; private final class BiometricTaskStackListener extends TaskStackListener { @@ -125,7 +127,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 } @@ -335,23 +338,14 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider Slog.e(TAG, "Unable to register user switch observer"); } - final IBiometricsFingerprint daemon = getDaemon(); - mIsUdfps = false; - android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension = - android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom( - daemon); - if (extension != null) { - try { - mIsUdfps = extension.isUdfps(sensorId); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception while quering udfps", e); - mIsUdfps = false; - } - } + // TODO(b/179175438): Remove this code block after transition to AIDL. + // The existence of config_udfps_sensor_props indicates that the sensor is UDFPS. + mIsUdfps = !ArrayUtils.isEmpty( + mContext.getResources().getIntArray(R.array.config_udfps_sensor_props)); final @FingerprintSensorProperties.SensorType int sensorType = mIsUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL - : FingerprintSensorProperties.TYPE_REAR; + : FingerprintSensorProperties.TYPE_REAR; // resetLockout is controlled by the framework, so hardwareAuthToken is not required final boolean resetLockoutRequiresHardwareAuthToken = false; final int maxEnrollmentsPerUser = mContext.getResources() @@ -415,7 +409,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider Slog.d(TAG, "Daemon was null, reconnecting, current operation: " + mScheduler.getCurrentClient()); try { - mDaemon = IBiometricsFingerprint.getService(true /* retry */); + mDaemon = IBiometricsFingerprint.getService(); } catch (java.util.NoSuchElementException e) { // Service doesn't exist or cannot be opened. Slog.w(TAG, "NoSuchElementException", e); @@ -554,7 +548,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, - boolean shouldLogMetrics) { + @FingerprintManager.EnrollReason int enrollReason) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); @@ -562,7 +556,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController, - shouldLogMetrics); + enrollReason); mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index d927aa717fbc..33db64c3259b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -24,6 +24,7 @@ import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; @@ -46,6 +47,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint private static final String TAG = "FingerprintEnrollClient"; @Nullable private final IUdfpsOverlayController mUdfpsOverlayController; + private final @FingerprintManager.EnrollReason int mEnrollReason; FingerprintEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @@ -53,12 +55,16 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController, - boolean shouldLogMetrics) { + @FingerprintManager.EnrollReason int enrollReason) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, true /* shouldVibrate */); mUdfpsOverlayController = udfpsOverlayController; - setShouldLog(shouldLogMetrics); + + mEnrollReason = enrollReason; + if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) { + setShouldLog(false); + } } @Override @@ -76,7 +82,8 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint @Override protected void startHalOperation() { - UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_ENROLL, + UdfpsHelper.showUdfpsOverlay(getSensorId(), + UdfpsHelper.getReasonFromEnrollReason(mEnrollReason), mUdfpsOverlayController); try { // GroupId was never used. In fact, groupId is always the same as userId. diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 6b2a1c950e38..51ba5f775880 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -211,9 +211,9 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override - public void clearOverrideForTest(long changeId, String packageName) { + public boolean clearOverrideForTest(long changeId, String packageName) { checkCompatChangeOverridePermission(); - mCompatConfig.removeOverride(changeId, packageName); + return mCompatConfig.removeOverride(changeId, packageName); } @Override diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index c70bb080b0b1..43d9ade67a11 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/services/core/java/com/android/server/connectivity/DnsManager.java @@ -32,6 +32,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.net.IDnsResolver; +import android.net.InetAddresses; import android.net.LinkProperties; import android.net.Network; import android.net.ResolverOptionsParcel; @@ -190,7 +191,7 @@ public class DnsManager { for (String ipAddress : ipAddresses) { try { latestDnses.add(new Pair(hostname, - InetAddress.parseNumericAddress(ipAddress))); + InetAddresses.parseNumericAddress(ipAddress))); } catch (IllegalArgumentException e) {} } // Remove <hostname, ipAddress> pairs that should not be tracked. diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index 952193b77681..46c49e7fc28c 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -34,9 +34,9 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.net.module.util.NetworkStackConstants; import com.android.server.net.BaseNetworkObserver; -import java.net.Inet4Address; import java.net.Inet6Address; import java.util.Objects; @@ -433,7 +433,7 @@ public class Nat464Xlat extends BaseNetworkObserver { // clat IPv4 address itself (for those apps, it doesn't matter what // the IP of the gateway is, only that there is one). RouteInfo ipv4Default = new RouteInfo( - new LinkAddress(Inet4Address.ANY, 0), + new LinkAddress(NetworkStackConstants.IPV4_ADDR_ANY, 0), clatAddress.getAddress(), mIface); stacked.addRoute(ipv4Default); stacked.addLinkAddress(clatAddress); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index a9a705f07ac4..bff1a5c99dd2 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -122,6 +122,13 @@ import java.util.TreeSet; // // When ConnectivityService disconnects a network: // ----------------------------------------------- +// If a network is just connected, ConnectivityService will think it will be used soon, but might +// not be used. Thus, a 5s timer will be held to prevent the network being torn down immediately. +// This "nascent" state is implemented by the "lingering" logic below without relating to any +// request, and is used in some cases where network requests race with network establishment. The +// nascent state ends when the 5-second timer fires, or as soon as the network satisfies a +// request, whichever is earlier. In this state, the network is considered in the background. +// // If a network has no chance of satisfying any requests (even if it were to become validated // and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel. // @@ -271,7 +278,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // All inactivity timers for this network, sorted by expiry time. A timer is added whenever // a request is moved to a network with a better score, regardless of whether the network is or - // was lingering or not. + // was lingering or not. An inactivity timer is also added when a network connects + // without immediately satisfying any requests. // TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g., // SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire. private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>(); @@ -896,7 +904,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { /** * Sets the specified requestId to linger on this network for the specified time. Called by - * ConnectivityService when the request is moved to another network with a higher score. + * ConnectivityService when the request is moved to another network with a higher score, or + * when a network is newly created. + * + * @param requestId The requestId of the request that no longer need to be served by this + * network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the + * {@code LingerTimer} for a newly created network. */ public void lingerRequest(int requestId, long now, long duration) { if (mInactivityTimerForRequest.get(requestId) != null) { @@ -969,10 +982,23 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { mInactive = false; } - public boolean isLingering() { + public boolean isInactive() { return mInactive; } + public boolean isLingering() { + return mInactive && !isNascent(); + } + + /** + * Return whether the network is just connected and about to be torn down because of not + * satisfying any request. + */ + public boolean isNascent() { + return mInactive && mInactivityTimers.size() == 1 + && mInactivityTimers.first().requestId == NetworkRequest.REQUEST_ID_NONE; + } + public void clearInactivityState() { if (mInactivityMessage != null) { mInactivityMessage.cancel(); @@ -1022,7 +1048,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { + "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{" + networkInfo.toShortString() + "} " + " Score{" + getCurrentScore() + "} " - + (isLingering() ? " lingering" : "") + + (isNascent() ? " nascent" : (isLingering() ? " lingering" : "")) + (everValidated ? " everValidated" : "") + (lastValidated ? " lastValidated" : "") + (partialConnectivity ? " partialConnectivity" : "") diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index d507b5f82bd0..8d21f6f0f59f 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -265,7 +265,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse for (Entry<Integer, Boolean> app : apps.entrySet()) { List<Integer> list = app.getValue() ? system : network; for (int user : users) { - list.add(UserHandle.getUid(user, app.getKey())); + final UserHandle handle = UserHandle.of(user); + if (handle == null) continue; + + list.add(UserHandle.getUid(handle, app.getKey())); } } try { @@ -550,7 +553,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse for (UidRange range : ranges) { for (int userId = range.getStartUser(); userId <= range.getEndUser(); userId++) { for (int appId : appIds) { - final int uid = UserHandle.getUid(userId, appId); + final UserHandle handle = UserHandle.of(userId); + if (handle == null) continue; + + final int uid = UserHandle.getUid(handle, appId); if (range.contains(uid)) { result.add(uid); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index e8062ae0eb57..fc2c7e01efde 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -51,6 +51,7 @@ import android.net.DnsResolver; import android.net.INetd; import android.net.INetworkManagementEventObserver; import android.net.Ikev2VpnProfile; +import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpSecManager; import android.net.IpSecManager.IpSecTunnelInterface; @@ -111,6 +112,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; +import com.android.net.module.util.NetworkStackConstants; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.net.BaseNetworkObserver; @@ -332,7 +334,7 @@ public class Vpn { public InetAddress resolve(final String endpoint) throws ExecutionException, InterruptedException { try { - return InetAddress.parseNumericAddress(endpoint); + return InetAddresses.parseNumericAddress(endpoint); } catch (IllegalArgumentException e) { // Endpoint is not numeric : fall through and resolve } @@ -1125,7 +1127,7 @@ public class Vpn { if (mConfig.dnsServers != null) { for (String dnsServer : mConfig.dnsServers) { - InetAddress address = InetAddress.parseNumericAddress(dnsServer); + InetAddress address = InetAddresses.parseNumericAddress(dnsServer); lp.addDnsServer(address); allowIPv4 |= address instanceof Inet4Address; allowIPv6 |= address instanceof Inet6Address; @@ -1135,10 +1137,12 @@ public class Vpn { lp.setHttpProxy(mConfig.proxyInfo); if (!allowIPv4) { - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); + lp.addRoute(new RouteInfo(new IpPrefix( + NetworkStackConstants.IPV4_ADDR_ANY, 0), RTN_UNREACHABLE)); } if (!allowIPv6) { - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); + lp.addRoute(new RouteInfo(new IpPrefix( + NetworkStackConstants.IPV6_ADDR_ANY, 0), RTN_UNREACHABLE)); } // Concatenate search domains into a string. diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java index 802472fcfba8..e496d77deaf5 100644 --- a/services/core/java/com/android/server/devicestate/DeviceState.java +++ b/services/core/java/com/android/server/devicestate/DeviceState.java @@ -16,8 +16,6 @@ package com.android.server.devicestate; -import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; - import android.annotation.IntRange; import android.annotation.NonNull; @@ -37,16 +35,16 @@ import java.util.Objects; */ public final class DeviceState { /** Unique identifier for the device state. */ - @IntRange(from = INVALID_DEVICE_STATE) + @IntRange(from = 0) private final int mIdentifier; /** String description of the device state. */ @NonNull private final String mName; - public DeviceState(@IntRange(from = INVALID_DEVICE_STATE) int identifier, + public DeviceState(@IntRange(from = 0) int identifier, @NonNull String name) { - if (identifier != INVALID_DEVICE_STATE && identifier < 0) { + if (identifier < 0) { throw new IllegalArgumentException("Identifier must be greater than or equal to zero."); } mIdentifier = identifier; @@ -54,7 +52,7 @@ public final class DeviceState { } /** Returns the unique identifier for the device state. */ - @IntRange(from = INVALID_DEVICE_STATE) + @IntRange(from = 0) public int getIdentifier() { return mIdentifier; } diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 375ec3a0f95f..984a17694e07 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -17,13 +17,13 @@ package com.android.server.devicestate; import static android.Manifest.permission.CONTROL_DEVICE_STATE; -import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; +import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.content.pm.PackageManager; +import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.IDeviceStateManager; import android.hardware.devicestate.IDeviceStateManagerCallback; import android.os.Binder; @@ -31,6 +31,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; @@ -62,8 +64,12 @@ import java.util.Optional; * the {@link DeviceStateProvider} to modify the current device state and communicating with the * {@link DeviceStatePolicy policy} to ensure the system is configured to match the requested state. * </p> + * The service also provides the {@link DeviceStateManager} API allowing clients to listen for + * changes in device state and submit requests to override the device state provided by the + * {@link DeviceStateProvider}. * * @see DeviceStatePolicy + * @see DeviceStateManager */ public final class DeviceStateManagerService extends SystemService { private static final String TAG = "DeviceStateManagerService"; @@ -79,11 +85,11 @@ public final class DeviceStateManagerService extends SystemService { @GuardedBy("mLock") private SparseArray<DeviceState> mDeviceStates = new SparseArray<>(); - // The current committed device state. The default of INVALID_DEVICE_STATE will be replaced by + // The current committed device state. The default of UNSET will be replaced by // the current state after the initial callback from the DeviceStateProvider. @GuardedBy("mLock") @NonNull - private DeviceState mCommittedState = new DeviceState(INVALID_DEVICE_STATE, "INVALID"); + private DeviceState mCommittedState = new DeviceState(0, "UNSET"); // The device state that is currently awaiting callback from the policy to be committed. @GuardedBy("mLock") @NonNull @@ -91,19 +97,23 @@ public final class DeviceStateManagerService extends SystemService { // Whether or not the policy is currently waiting to be notified of the current pending state. @GuardedBy("mLock") private boolean mIsPolicyWaitingForState = false; - // The device state that is currently requested and is next to be configured and committed. - // Can be overwritten by an override state value if requested. - @GuardedBy("mLock") - @NonNull - private Optional<DeviceState> mRequestedState = Optional.empty(); - // The most recently requested override state, or empty if no override is requested. + + // The device state that is set by the device state provider. @GuardedBy("mLock") @NonNull - private Optional<DeviceState> mRequestedOverrideState = Optional.empty(); + private Optional<DeviceState> mBaseState = Optional.empty(); - // List of registered callbacks indexed by process id. + // List of processes registered to receive notifications about changes to device state and + // request status indexed by process id. @GuardedBy("mLock") - private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>(); + private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>(); + // List of override requests with the highest precedence request at the end. + @GuardedBy("mLock") + private final ArrayList<OverrideRequestRecord> mRequestRecords = new ArrayList<>(); + // Set of override requests that are pending a call to notifyStatusIfNeeded() to be notified + // of a change in status. + @GuardedBy("mLock") + private final ArraySet<OverrideRequestRecord> mRequestsPendingStatusChange = new ArraySet<>(); public DeviceStateManagerService(@NonNull Context context) { this(context, new DeviceStatePolicyImpl(context)); @@ -148,55 +158,32 @@ public final class DeviceStateManagerService extends SystemService { } /** - * Returns the requested state. The service will configure the device to match the requested - * state when possible. - */ - @NonNull - Optional<DeviceState> getRequestedState() { - synchronized (mLock) { - return mRequestedState; - } - } - - /** - * Overrides the current device state with the provided state. + * Returns the base state. The service will configure the device to match the base state when + * there is no active request to override the base state. * - * @return {@code true} if the override state is valid and supported, {@code false} otherwise. + * @see #getOverrideState() */ - boolean setOverrideState(int overrideState) { - if (getContext().checkCallingOrSelfPermission(CONTROL_DEVICE_STATE) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + CONTROL_DEVICE_STATE); - } - + @NonNull + Optional<DeviceState> getBaseState() { synchronized (mLock) { - if (overrideState != INVALID_DEVICE_STATE && !isSupportedStateLocked(overrideState)) { - return false; - } - - mRequestedOverrideState = getStateLocked(overrideState); - updatePendingStateLocked(); + return mBaseState; } - - notifyPolicyIfNeeded(); - return true; - } - - /** - * Clears an override state set with {@link #setOverrideState(int)}. - */ - void clearOverrideState() { - setOverrideState(INVALID_DEVICE_STATE); } /** - * Returns the current requested override state, or {@link Optional#empty()} if no override - * state is requested. + * Returns the current override state, or {@link Optional#empty()} if no override state is + * requested. If an override states is present, the returned state will take precedence over + * the base state returned from {@link #getBaseState()}. */ @NonNull Optional<DeviceState> getOverrideState() { synchronized (mLock) { - return mRequestedOverrideState; + if (mRequestRecords.isEmpty()) { + return Optional.empty(); + } + + OverrideRequestRecord topRequest = mRequestRecords.get(mRequestRecords.size() - 1); + return Optional.of(topRequest.mRequestedState); } } @@ -211,6 +198,17 @@ public final class DeviceStateManagerService extends SystemService { } } + /** Returns the list of currently supported device state identifiers. */ + private int[] getSupportedStateIdentifiers() { + synchronized (mLock) { + int[] supportedStates = new int[mDeviceStates.size()]; + for (int i = 0; i < supportedStates.length; i++) { + supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier(); + } + return supportedStates; + } + } + @VisibleForTesting IDeviceStateManager getBinderService() { return mBinderService; @@ -224,22 +222,26 @@ public final class DeviceStateManagerService extends SystemService { mDeviceStates.put(state.getIdentifier(), state); } - if (mRequestedState.isPresent() - && !isSupportedStateLocked(mRequestedState.get().getIdentifier())) { - // The current requested state is no longer valid. We'll clear it here, though + if (mBaseState.isPresent() + && !isSupportedStateLocked(mBaseState.get().getIdentifier())) { + // The current base state is no longer valid. We'll clear it here, though // we won't actually update the current state until a callback comes from the // provider with the most recent state. - mRequestedState = Optional.empty(); + mBaseState = Optional.empty(); } - if (mRequestedOverrideState.isPresent() - && !isSupportedStateLocked(mRequestedOverrideState.get().getIdentifier())) { - // The current override state is no longer valid. We'll clear it here and update - // the committed state if necessary. - mRequestedOverrideState = Optional.empty(); + + final int requestSize = mRequestRecords.size(); + for (int i = 0; i < requestSize; i++) { + OverrideRequestRecord request = mRequestRecords.get(i); + if (!isSupportedStateLocked(request.mRequestedState.getIdentifier())) { + request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED); + } } + updatePendingStateLocked(); } + notifyRequestsOfStatusChangeIfNeeded(); notifyPolicyIfNeeded(); } @@ -261,20 +263,37 @@ public final class DeviceStateManagerService extends SystemService { } /** - * Requests that the system enter the provided {@code state}. The request may not be honored - * under certain conditions, for example if the provided state is not supported. + * Requests to set the base state. The request may not be honored under certain conditions, for + * example if the provided state is not supported. * * @see #isSupportedStateLocked(int) */ - private void requestState(int identifier) { + private void setBaseState(int identifier) { synchronized (mLock) { - final Optional<DeviceState> requestedState = getStateLocked(identifier); - if (requestedState.isPresent()) { - mRequestedState = requestedState; + if (mBaseState.isPresent() && mBaseState.get().getIdentifier() == identifier) { + // Base state hasn't changed. Nothing to do. + return; + } + + final Optional<DeviceState> baseState = getStateLocked(identifier); + if (!baseState.isPresent()) { + throw new IllegalArgumentException("Base state is not supported"); } + + mBaseState = baseState; + + final int requestSize = mRequestRecords.size(); + for (int i = 0; i < requestSize; i++) { + OverrideRequestRecord request = mRequestRecords.get(i); + if ((request.mFlags & FLAG_CANCEL_WHEN_BASE_CHANGES) > 0) { + request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED); + } + } + updatePendingStateLocked(); } + notifyRequestsOfStatusChangeIfNeeded(); notifyPolicyIfNeeded(); } @@ -290,10 +309,10 @@ public final class DeviceStateManagerService extends SystemService { } final DeviceState stateToConfigure; - if (mRequestedOverrideState.isPresent()) { - stateToConfigure = mRequestedOverrideState.get(); + if (!mRequestRecords.isEmpty()) { + stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState; } else { - stateToConfigure = mRequestedState.orElse(null); + stateToConfigure = mBaseState.orElse(null); } if (stateToConfigure == null) { @@ -360,6 +379,13 @@ public final class DeviceStateManagerService extends SystemService { } mCommittedState = mPendingState.get(); newState = mCommittedState.getIdentifier(); + + if (!mRequestRecords.isEmpty()) { + final OverrideRequestRecord topRequest = + mRequestRecords.get(mRequestRecords.size() - 1); + topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE); + } + mPendingState = Optional.empty(); updatePendingStateLocked(); } @@ -367,6 +393,9 @@ public final class DeviceStateManagerService extends SystemService { // Notify callbacks of a change. notifyDeviceStateChanged(newState); + // Notify the top request that it's active. + notifyRequestsOfStatusChangeIfNeeded(); + // Try to configure the next state if needed. notifyPolicyIfNeeded(); } @@ -377,43 +406,69 @@ public final class DeviceStateManagerService extends SystemService { "Attempting to notify callbacks with service lock held."); } - // Grab the lock and copy the callbacks. - ArrayList<CallbackRecord> callbacks; + // Grab the lock and copy the process records. + ArrayList<ProcessRecord> registeredProcesses; synchronized (mLock) { - if (mCallbacks.size() == 0) { + if (mProcessRecords.size() == 0) { return; } - callbacks = new ArrayList<>(); - for (int i = 0; i < mCallbacks.size(); i++) { - callbacks.add(mCallbacks.valueAt(i)); + registeredProcesses = new ArrayList<>(); + for (int i = 0; i < mProcessRecords.size(); i++) { + registeredProcesses.add(mProcessRecords.valueAt(i)); + } + } + + // After releasing the lock, send the notifications out. + for (int i = 0; i < registeredProcesses.size(); i++) { + registeredProcesses.get(i).notifyDeviceStateAsync(deviceState); + } + } + + /** + * Notifies all dirty requests (requests that have a change in status, but have not yet been + * notified) that their status has changed. + */ + private void notifyRequestsOfStatusChangeIfNeeded() { + if (Thread.holdsLock(mLock)) { + throw new IllegalStateException( + "Attempting to notify requests with service lock held."); + } + + ArraySet<OverrideRequestRecord> dirtyRequests; + synchronized (mLock) { + if (mRequestsPendingStatusChange.isEmpty()) { + return; } + + dirtyRequests = new ArraySet<>(mRequestsPendingStatusChange); + mRequestsPendingStatusChange.clear(); } // After releasing the lock, send the notifications out. - for (int i = 0; i < callbacks.size(); i++) { - callbacks.get(i).notifyDeviceStateAsync(deviceState); + for (int i = 0; i < dirtyRequests.size(); i++) { + dirtyRequests.valueAt(i).notifyStatusIfNeeded(); } } - private void registerCallbackInternal(IDeviceStateManagerCallback callback, int callingPid) { + private void registerProcess(int pid, IDeviceStateManagerCallback callback) { int currentState; - CallbackRecord record; + ProcessRecord record; // Grab the lock to register the callback and get the current state. synchronized (mLock) { - if (mCallbacks.contains(callingPid)) { + if (mProcessRecords.contains(pid)) { throw new SecurityException("The calling process has already registered an" + " IDeviceStateManagerCallback."); } - record = new CallbackRecord(callback, callingPid); + record = new ProcessRecord(callback, pid); try { callback.asBinder().linkToDeath(record, 0); } catch (RemoteException ex) { throw new RuntimeException(ex); } - mCallbacks.put(callingPid, record); + mProcessRecords.put(pid, record); currentState = mCommittedState.getIdentifier(); } @@ -421,10 +476,86 @@ public final class DeviceStateManagerService extends SystemService { record.notifyDeviceStateAsync(currentState); } - private void unregisterCallbackInternal(CallbackRecord record) { + private void handleProcessDied(ProcessRecord processRecord) { + synchronized (mLock) { + // Cancel all requests from this process. + final int requestCount = processRecord.mRequestRecords.size(); + for (int i = 0; i < requestCount; i++) { + final OverrideRequestRecord request = processRecord.mRequestRecords.valueAt(i); + // Cancel the request but don't mark it as dirty since there's no need to send + // notifications if the process has died. + request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED, + false /* markDirty */); + } + + mProcessRecords.remove(processRecord.mPid); + + updatePendingStateLocked(); + } + + notifyPolicyIfNeeded(); + } + + private void requestStateInternal(int state, int flags, int callingPid, + @NonNull IBinder token) { + synchronized (mLock) { + final ProcessRecord processRecord = mProcessRecords.get(callingPid); + if (processRecord == null) { + throw new IllegalStateException("Process " + callingPid + + " has no registered callback."); + } + + if (processRecord.mRequestRecords.get(token) != null) { + throw new IllegalStateException("Request has already been made for the supplied" + + " token: " + token); + } + + final Optional<DeviceState> deviceState = getStateLocked(state); + if (!deviceState.isPresent()) { + throw new IllegalArgumentException("Requested state: " + state + + " is not supported."); + } + + OverrideRequestRecord topRecord = mRequestRecords.isEmpty() + ? null : mRequestRecords.get(mRequestRecords.size() - 1); + if (topRecord != null) { + topRecord.setStatusLocked(OverrideRequestRecord.STATUS_SUSPENDED); + } + + final OverrideRequestRecord request = + new OverrideRequestRecord(processRecord, token, deviceState.get(), flags); + mRequestRecords.add(request); + processRecord.mRequestRecords.put(request.mToken, request); + // We don't set the status of the new request to ACTIVE here as it will be set in + // commitPendingState(). + + updatePendingStateLocked(); + } + + notifyRequestsOfStatusChangeIfNeeded(); + notifyPolicyIfNeeded(); + } + + private void cancelRequestInternal(int callingPid, @NonNull IBinder token) { synchronized (mLock) { - mCallbacks.remove(record.mPid); + final ProcessRecord processRecord = mProcessRecords.get(callingPid); + if (processRecord == null) { + throw new IllegalStateException("Process " + callingPid + + " has no registered callback."); + } + + OverrideRequestRecord request = processRecord.mRequestRecords.get(token); + if (request == null) { + throw new IllegalStateException("No known request for the given token"); + } + + request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED); + + updatePendingStateLocked(); } + + notifyRequestsOfStatusChangeIfNeeded(); + notifyPolicyIfNeeded(); } private void dumpInternal(PrintWriter pw) { @@ -433,15 +564,26 @@ public final class DeviceStateManagerService extends SystemService { synchronized (mLock) { pw.println(" mCommittedState=" + mCommittedState); pw.println(" mPendingState=" + mPendingState); - pw.println(" mRequestedState=" + mRequestedState); - pw.println(" mRequestedOverrideState=" + mRequestedOverrideState); + pw.println(" mBaseState=" + mBaseState); + pw.println(" mOverrideState=" + getOverrideState()); - final int callbackCount = mCallbacks.size(); + final int processCount = mProcessRecords.size(); pw.println(); - pw.println("Callbacks: size=" + callbackCount); - for (int i = 0; i < callbackCount; i++) { - CallbackRecord callback = mCallbacks.valueAt(i); - pw.println(" " + i + ": mPid=" + callback.mPid); + pw.println("Registered processes: size=" + processCount); + for (int i = 0; i < processCount; i++) { + ProcessRecord processRecord = mProcessRecords.valueAt(i); + pw.println(" " + i + ": mPid=" + processRecord.mPid); + } + + final int requestCount = mRequestRecords.size(); + pw.println(); + pw.println("Override requests: size=" + requestCount); + for (int i = 0; i < requestCount; i++) { + OverrideRequestRecord requestRecord = mRequestRecords.get(i); + pw.println(" " + i + ": mPid=" + requestRecord.mProcessRecord.mPid + + ", mRequestedState=" + requestRecord.mRequestedState + + ", mFlags=" + requestRecord.mFlags + + ", mStatus=" + requestRecord.statusToString(requestRecord.mStatus)); } } } @@ -452,12 +594,6 @@ public final class DeviceStateManagerService extends SystemService { if (newDeviceStates.length == 0) { throw new IllegalArgumentException("Supported device states must not be empty"); } - for (int i = 0; i < newDeviceStates.length; i++) { - if (newDeviceStates[i].getIdentifier() == INVALID_DEVICE_STATE) { - throw new IllegalArgumentException( - "Supported device states includes INVALID_DEVICE_STATE identifier"); - } - } updateSupportedStates(newDeviceStates); } @@ -467,22 +603,24 @@ public final class DeviceStateManagerService extends SystemService { throw new IllegalArgumentException("Invalid identifier: " + identifier); } - requestState(identifier); + setBaseState(identifier); } } - private final class CallbackRecord implements IBinder.DeathRecipient { + private final class ProcessRecord implements IBinder.DeathRecipient { private final IDeviceStateManagerCallback mCallback; private final int mPid; - CallbackRecord(IDeviceStateManagerCallback callback, int pid) { + private final ArrayMap<IBinder, OverrideRequestRecord> mRequestRecords = new ArrayMap<>(); + + ProcessRecord(IDeviceStateManagerCallback callback, int pid) { mCallback = callback; mPid = pid; } @Override public void binderDied() { - unregisterCallbackInternal(this); + handleProcessDied(this); } public void notifyDeviceStateAsync(int devicestate) { @@ -493,6 +631,119 @@ public final class DeviceStateManagerService extends SystemService { ex); } } + + public void notifyRequestActiveAsync(OverrideRequestRecord request) { + try { + mCallback.onRequestActive(request.mToken); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", + ex); + } + } + + public void notifyRequestSuspendedAsync(OverrideRequestRecord request) { + try { + mCallback.onRequestSuspended(request.mToken); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", + ex); + } + } + + public void notifyRequestCanceledAsync(OverrideRequestRecord request) { + try { + mCallback.onRequestCanceled(request.mToken); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.", + ex); + } + } + } + + /** A record describing a request to override the state of the device. */ + private final class OverrideRequestRecord { + public static final int STATUS_UNKNOWN = 0; + public static final int STATUS_ACTIVE = 1; + public static final int STATUS_SUSPENDED = 2; + public static final int STATUS_CANCELED = 3; + + @Nullable + public String statusToString(int status) { + switch (status) { + case STATUS_ACTIVE: + return "ACTIVE"; + case STATUS_SUSPENDED: + return "SUSPENDED"; + case STATUS_CANCELED: + return "CANCELED"; + case STATUS_UNKNOWN: + return "UNKNOWN"; + default: + return null; + } + } + + private final ProcessRecord mProcessRecord; + @NonNull + private final IBinder mToken; + @NonNull + private final DeviceState mRequestedState; + private final int mFlags; + + private int mStatus = STATUS_UNKNOWN; + private int mLastNotifiedStatus = STATUS_UNKNOWN; + + OverrideRequestRecord(@NonNull ProcessRecord processRecord, @NonNull IBinder token, + @NonNull DeviceState requestedState, int flags) { + mProcessRecord = processRecord; + mToken = token; + mRequestedState = requestedState; + mFlags = flags; + } + + public void setStatusLocked(int status) { + setStatusLocked(status, true /* markDirty */); + } + + public void setStatusLocked(int status, boolean markDirty) { + if (mStatus != status) { + if (mStatus == STATUS_CANCELED) { + throw new IllegalStateException( + "Can not alter the status of a request after set to CANCELED."); + } + + mStatus = status; + + if (mStatus == STATUS_CANCELED) { + mRequestRecords.remove(this); + mProcessRecord.mRequestRecords.remove(mToken); + } + + if (markDirty) { + mRequestsPendingStatusChange.add(this); + } + } + } + + public void notifyStatusIfNeeded() { + int stateToReport; + synchronized (mLock) { + if (mLastNotifiedStatus == mStatus) { + return; + } + + stateToReport = mStatus; + mLastNotifiedStatus = mStatus; + } + + if (stateToReport == STATUS_ACTIVE) { + mProcessRecord.notifyRequestActiveAsync(this); + } else if (stateToReport == STATUS_SUSPENDED) { + mProcessRecord.notifyRequestSuspendedAsync(this); + } else if (stateToReport == STATUS_CANCELED) { + mProcessRecord.notifyRequestCanceledAsync(this); + } + } } /** Implementation of {@link IDeviceStateManager} published as a binder service. */ @@ -506,13 +757,59 @@ public final class DeviceStateManagerService extends SystemService { final int callingPid = Binder.getCallingPid(); final long token = Binder.clearCallingIdentity(); try { - registerCallbackInternal(callback, callingPid); + registerProcess(callingPid, callback); } finally { Binder.restoreCallingIdentity(token); } } @Override // Binder call + public int[] getSupportedDeviceStates() { + final long token = Binder.clearCallingIdentity(); + try { + return getSupportedStateIdentifiers(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public void requestState(IBinder token, int state, int flags) { + getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, + "Permission required to request device state."); + + if (token == null) { + throw new IllegalArgumentException("Request token must not be null."); + } + + final int callingPid = Binder.getCallingPid(); + final long callingIdentity = Binder.clearCallingIdentity(); + try { + requestStateInternal(state, flags, callingPid, token); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + + @Override // Binder call + public void cancelRequest(IBinder token) { + getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, + "Permission required to clear requested device state."); + + if (token == null) { + throw new IllegalArgumentException("Request token must not be null."); + } + + final int callingPid = Binder.getCallingPid(); + final long callingIdentity = Binder.clearCallingIdentity(); + try { + cancelRequestInternal(callingPid, token); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + + @Override // Binder call public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver result) { new DeviceStateManagerShellCommand(DeviceStateManagerService.this) diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java index 7914531f9910..6cc55a6c4774 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java @@ -16,6 +16,13 @@ package com.android.server.devicestate; +import static android.Manifest.permission.CONTROL_DEVICE_STATE; + +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.devicestate.DeviceStateManager; +import android.hardware.devicestate.DeviceStateRequest; +import android.os.Binder; import android.os.ShellCommand; import java.io.PrintWriter; @@ -27,10 +34,15 @@ import java.util.Optional; * Use with {@code adb shell cmd device_state ...}. */ public class DeviceStateManagerShellCommand extends ShellCommand { - private final DeviceStateManagerService mInternal; + @Nullable + private static DeviceStateRequest sLastRequest; + + private final DeviceStateManagerService mService; + private final DeviceStateManager mClient; public DeviceStateManagerShellCommand(DeviceStateManagerService service) { - mInternal = service; + mService = service; + mClient = service.getContext().getSystemService(DeviceStateManager.class); } @Override @@ -51,15 +63,15 @@ public class DeviceStateManagerShellCommand extends ShellCommand { } private void printState(PrintWriter pw) { - DeviceState committedState = mInternal.getCommittedState(); - Optional<DeviceState> requestedState = mInternal.getRequestedState(); - Optional<DeviceState> requestedOverrideState = mInternal.getOverrideState(); + DeviceState committedState = mService.getCommittedState(); + Optional<DeviceState> baseState = mService.getBaseState(); + Optional<DeviceState> overrideState = mService.getOverrideState(); pw.println("Committed state: " + committedState); - if (requestedOverrideState.isPresent()) { + if (overrideState.isPresent()) { pw.println("----------------------"); - pw.println("Base state: " + requestedState.orElse(null)); - pw.println("Override state: " + requestedOverrideState.get()); + pw.println("Base state: " + baseState.orElse(null)); + pw.println("Override state: " + overrideState.get()); } } @@ -67,32 +79,51 @@ public class DeviceStateManagerShellCommand extends ShellCommand { final String nextArg = getNextArg(); if (nextArg == null) { printState(pw); - } else if ("reset".equals(nextArg)) { - mInternal.clearOverrideState(); - } else { - int requestedState; - try { - requestedState = Integer.parseInt(nextArg); - } catch (NumberFormatException e) { - getErrPrintWriter().println("Error: requested state should be an integer"); - return -1; - } + } - boolean success = mInternal.setOverrideState(requestedState); - if (!success) { - getErrPrintWriter().println("Error: failed to set override state. Run:"); - getErrPrintWriter().println(""); - getErrPrintWriter().println(" print-states"); - getErrPrintWriter().println(""); - getErrPrintWriter().println("to get the list of currently supported device states"); - return -1; + final Context context = mService.getContext(); + context.enforceCallingOrSelfPermission( + CONTROL_DEVICE_STATE, + "Permission required to request device state."); + final long callingIdentity = Binder.clearCallingIdentity(); + try { + if ("reset".equals(nextArg)) { + if (sLastRequest != null) { + mClient.cancelRequest(sLastRequest); + sLastRequest = null; + } + } else { + int requestedState = Integer.parseInt(nextArg); + DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build(); + + mClient.requestState(request, null /* executor */, null /* callback */); + if (sLastRequest != null) { + mClient.cancelRequest(sLastRequest); + } + + sLastRequest = request; } + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: requested state should be an integer"); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println("Error: " + e.getMessage()); + getErrPrintWriter().println("-------------------"); + getErrPrintWriter().println("Run:"); + getErrPrintWriter().println(""); + getErrPrintWriter().println(" print-states"); + getErrPrintWriter().println(""); + getErrPrintWriter().println("to get the list of currently supported device states"); + return -1; + } finally { + Binder.restoreCallingIdentity(callingIdentity); } + return 0; } private int runPrintStates(PrintWriter pw) { - DeviceState[] states = mInternal.getSupportedStates(); + DeviceState[] states = mService.getSupportedStates(); pw.print("Supported states: [\n"); for (int i = 0; i < states.length; i++) { pw.print(" " + states[i] + ",\n"); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index e5151d84c33e..0950d5dd076f 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -514,8 +514,8 @@ public final class DisplayManagerService extends SystemService { DeviceStateManager deviceStateManager = mContext.getSystemService(DeviceStateManager.class); - deviceStateManager.registerDeviceStateListener(new DeviceStateListener(), - new HandlerExecutor(mHandler)); + deviceStateManager.addDeviceStateListener(new HandlerExecutor(mHandler), + new DeviceStateListener()); scheduleTraversalLocked(false); } diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java index 3e2b5ab795be..1b27572ad8de 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -345,6 +345,7 @@ public final class FontManagerService extends IFontManager.Stub { synchronized (mSerializedFontMapLock) { mSerializedFontMap = serializeFontMap; } + return; } catch (IOException | ErrnoException e) { Slog.w(TAG, "Failed to serialize updatable font map. " + "Retrying with system image fonts.", e); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index d8e124a00104..1a4c8b7d6571 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2546,7 +2546,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + mCurTokenDisplayId); } mIWindowManager.addWindowToken(mCurToken, LayoutParams.TYPE_INPUT_METHOD, - mCurTokenDisplayId); + mCurTokenDisplayId, null /* options */); } catch (RemoteException e) { } return new InputBindResult( @@ -5227,6 +5227,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + boolean asProto = false; for (int argIndex = 0; argIndex < args.length; argIndex++) { if (args[argIndex].equals(PROTO_ARG)) { @@ -5249,8 +5251,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - if (useProto) { final ProtoOutputStream proto = new ProtoOutputStream(fd); dumpDebug(proto, InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE); diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 6fec9063ba94..1dd3d4190e8a 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1309,7 +1309,8 @@ public final class MultiClientInputMethodManagerService { final Binder token = new Binder(); Binder.withCleanCallingIdentity( PooledLambda.obtainRunnable(WindowManagerInternal::addWindowToken, - mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId)); + mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId, + null /* options */)); mPerUserData.mDisplayIdToImeWindowTokenMap.add(new TokenInfo(token, displayId)); return token; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6843733eea9f..571b6934e425 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3041,7 +3041,8 @@ public class NotificationManagerService extends SystemService { } Binder windowToken = new Binder(); - mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId); + mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId, + null /* options */); record = getToastRecord(callingUid, callingPid, pkg, isSystemToast, token, text, callback, duration, windowToken, displayId, textCallback); mToastQueue.add(record); diff --git a/services/core/java/com/android/server/pm/DefaultAppProvider.java b/services/core/java/com/android/server/pm/DefaultAppProvider.java index a17967fcc76e..c18d0e9ef35e 100644 --- a/services/core/java/com/android/server/pm/DefaultAppProvider.java +++ b/services/core/java/com/android/server/pm/DefaultAppProvider.java @@ -41,14 +41,18 @@ import java.util.function.Supplier; public class DefaultAppProvider { @NonNull private final Supplier<RoleManager> mRoleManagerSupplier; + @NonNull + private final Supplier<UserManagerInternal> mUserManagerInternalSupplier; /** * Create a new instance of this class * * @param roleManagerSupplier the supplier for {@link RoleManager} */ - public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier) { + public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier, + @NonNull Supplier<UserManagerInternal> userManagerInternalSupplier) { mRoleManagerSupplier = roleManagerSupplier; + mUserManagerInternalSupplier = userManagerInternalSupplier; } /** @@ -132,7 +136,8 @@ public class DefaultAppProvider { */ @Nullable public String getDefaultHome(@NonNull int userId) { - return getRoleHolder(RoleManager.ROLE_HOME, userId); + return getRoleHolder(RoleManager.ROLE_HOME, + mUserManagerInternalSupplier.get().getProfileParentId(userId)); } /** diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java index f5ec595cc45a..ecafdfdbd6f1 100644 --- a/services/core/java/com/android/server/pm/IncrementalStates.java +++ b/services/core/java/com/android/server/pm/IncrementalStates.java @@ -249,24 +249,6 @@ public final class IncrementalStates { } /** - * @return the current startable state. - */ - public boolean isStartable() { - synchronized (mLock) { - return mStartableState.isStartable(); - } - } - - /** - * @return Whether the package is still being loaded or has been fully loaded. - */ - public boolean isLoading() { - synchronized (mLock) { - return mLoadingState.isLoading(); - } - } - - /** * @return all current states in a Parcelable. */ public IncrementalStatesInfo getIncrementalStatesInfo() { diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index c4a23f961072..f240d85572c4 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -1316,10 +1316,6 @@ public class LauncherAppsService extends SystemService { } finally { mListeners.finishBroadcast(); } - PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); - pmi.registerInstalledLoadingProgressCallback(packageName, - new PackageLoadingProgressCallback(packageName, user), - user.getIdentifier()); super.onPackageAdded(packageName, uid); } @@ -1542,38 +1538,5 @@ public class LauncherAppsService extends SystemService { checkCallbackCount(); } } - - class PackageLoadingProgressCallback extends - PackageManagerInternal.InstalledLoadingProgressCallback { - private String mPackageName; - private UserHandle mUser; - - PackageLoadingProgressCallback(String packageName, UserHandle user) { - super(mCallbackHandler); - mPackageName = packageName; - mUser = user; - } - - @Override - public void onLoadingProgressChanged(float progress) { - final int n = mListeners.beginBroadcast(); - try { - for (int i = 0; i < n; i++) { - IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); - BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); - if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) { - continue; - } - try { - listener.onPackageLoadingProgressChanged(mUser, mPackageName, progress); - } catch (RemoteException re) { - Slog.d(TAG, "Callback failed ", re); - } - } - } finally { - mListeners.finishBroadcast(); - } - } - } } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 498c314cbc68..81b65b294456 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -24,7 +24,6 @@ import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS; import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.MODE_IGNORED; import static android.content.Intent.ACTION_MAIN; -import static android.content.Intent.CATEGORY_BROWSABLE; import static android.content.Intent.CATEGORY_DEFAULT; import static android.content.Intent.CATEGORY_HOME; import static android.content.Intent.EXTRA_LONG_VERSION_CODE; @@ -68,11 +67,7 @@ import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE; import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP; import static android.content.pm.PackageManager.INSTALL_STAGED; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; -import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.content.pm.PackageManager.MATCH_APEX; @@ -378,11 +373,6 @@ import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.dex.ViewCompiler; -import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; -import com.android.server.pm.verify.domain.DomainVerificationService; -import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; -import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1; -import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2; import com.android.server.pm.parsing.PackageCacher; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.PackageParser2; @@ -396,6 +386,11 @@ import com.android.server.pm.permission.LegacyPermissionManagerService; import com.android.server.pm.permission.Permission; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationService; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1; +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2; import com.android.server.rollback.RollbackManagerInternal; import com.android.server.security.VerityUtils; import com.android.server.storage.DeviceStorageMonitorInternal; @@ -405,7 +400,6 @@ import com.android.server.utils.Watchable; import com.android.server.utils.Watched; import com.android.server.utils.WatchedArrayMap; import com.android.server.utils.WatchedSparseBooleanArray; -import com.android.server.utils.WatchedSparseIntArray; import com.android.server.utils.Watcher; import com.android.server.wm.ActivityTaskManagerInternal; @@ -467,7 +461,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.function.Supplier; /** * Keep track of all those APKs everywhere. @@ -3623,8 +3616,6 @@ public class PackageManagerService extends IPackageManager.Stub final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); - enforceCrossUserPermission(callingUid, userId, - /* requireFullPermission */ false, /* checkShell */ false, "getPackagesForUid"); return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp); } @@ -5770,8 +5761,8 @@ public class PackageManagerService extends IPackageManager.Stub (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()), (i, pm) -> (IncrementalManager) i.getContext().getSystemService(Context.INCREMENTAL_SERVICE), - (i, pm) -> new DefaultAppProvider(() -> context.getSystemService( - RoleManager.class)), + (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class), + () -> LocalServices.getService(UserManagerInternal.class)), (i, pm) -> new DisplayMetrics(), (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore, i.getDisplayMetrics(), pm.mCacheDir, @@ -8949,6 +8940,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public List<String> getAllPackages() { + enforceSystemOrRootOrShell("getAllPackages is limited to privileged callers"); final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); synchronized (mLock) { @@ -19169,6 +19161,8 @@ public class PackageManagerService extends IPackageManager.Stub extras, 0 /*flags*/, null /*targetPackage*/, null /*finishedReceiver*/, mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList, null); + // Unregister progress listener + mIncrementalManager.unregisterLoadingProgressCallbacks(codePath); // Unregister health listener as it will always be healthy from now mIncrementalManager.unregisterHealthListener(codePath); } @@ -27046,47 +27040,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public boolean registerInstalledLoadingProgressCallback(String packageName, - PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) { - final PackageSetting ps = getPackageSettingForUser(packageName, Binder.getCallingUid(), - userId); - if (ps == null) { - return false; - } - if (!ps.isPackageLoading()) { - Slog.w(TAG, - "Failed registering loading progress callback. Package is fully loaded."); - return false; - } - if (mIncrementalManager == null) { - Slog.w(TAG, - "Failed registering loading progress callback. Incremental is not enabled"); - return false; - } - return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(), - (IPackageLoadingProgressCallback) callback.getBinder()); - } - - @Override - public boolean unregisterInstalledLoadingProgressCallback(String packageName, - PackageManagerInternal.InstalledLoadingProgressCallback callback) { - final PackageSetting ps; - synchronized (mLock) { - ps = mSettings.getPackageLPr(packageName); - if (ps == null) { - Slog.w(TAG, "Failed unregistering loading progress callback. Package " - + packageName + " is not installed"); - return false; - } - } - if (mIncrementalManager == null) { - return false; - } - return mIncrementalManager.unregisterLoadingProgressCallback(ps.getPathString(), - (IPackageLoadingProgressCallback) callback.getBinder()); - } - - @Override public IncrementalStatesInfo getIncrementalStatesInfo( @NonNull String packageName, int filterCallingUid, int userId) { final PackageSetting ps = getPackageSettingForUser(packageName, filterCallingUid, diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 69e84b536004..ca5d2b49b99d 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -344,8 +344,8 @@ public class PackageSetting extends PackageSettingBase { installSource.originatingPackageName); proto.end(sourceToken); } - proto.write(PackageProto.StatesProto.IS_STARTABLE, incrementalStates.isStartable()); - proto.write(PackageProto.StatesProto.IS_LOADING, incrementalStates.isLoading()); + proto.write(PackageProto.StatesProto.IS_STARTABLE, isPackageStartable()); + proto.write(PackageProto.StatesProto.IS_LOADING, isPackageLoading()); writeUsersInfoToProto(proto, PackageProto.USERS); writePackageUserPermissionsProto(proto, PackageProto.USER_PERMISSIONS, users, dataProvider); proto.end(packageToken); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 8aa553d68b98..3a142837e063 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -26,8 +26,6 @@ import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.IncrementalStatesInfo; -import android.content.pm.IntentFilterVerificationInfo; -import android.content.pm.PackageManager; import android.content.pm.PackageManager.UninstallReason; import android.content.pm.PackageParser; import android.content.pm.PackageUserState; @@ -42,8 +40,6 @@ import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; -import com.android.server.pm.verify.domain.DomainVerificationService; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.File; @@ -731,14 +727,14 @@ public abstract class PackageSettingBase extends SettingBase { * @return True if package is startable, false otherwise. */ public boolean isPackageStartable() { - return incrementalStates.isStartable(); + return getIncrementalStates().isStartable(); } /** * @return True if package is still being loaded, false if the package is fully loaded. */ public boolean isPackageLoading() { - return incrementalStates.isLoading(); + return getIncrementalStates().isLoading(); } /** diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 89729b585b14..9b092c000172 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -1535,6 +1535,27 @@ class ShortcutPackage extends ShortcutPackageItem { pw.println(")"); } + public void dumpShortcuts(@NonNull PrintWriter pw, int matchFlags) { + final boolean matchDynamic = (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0; + final boolean matchPinned = (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0; + final boolean matchManifest = (matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST) != 0; + final boolean matchCached = (matchFlags & ShortcutManager.FLAG_MATCH_CACHED) != 0; + + final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0) + | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0) + | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0) + | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0); + + final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts; + final int size = shortcuts.size(); + for (int i = 0; i < size; i++) { + final ShortcutInfo si = shortcuts.valueAt(i); + if ((si.getFlags() & shortcutFlags) != 0) { + pw.println(si.toDumpString("")); + } + } + } + @Override public JSONObject dumpCheckin(boolean clear) throws JSONException { final JSONObject result = super.dumpCheckin(clear); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 3c4457db6cf0..863e3fe5c6a3 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -40,6 +40,7 @@ import android.content.IntentSender.SendIntentException; import android.content.LocusId; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ComponentInfo; import android.content.pm.IPackageManager; import android.content.pm.IShortcutService; import android.content.pm.LauncherApps; @@ -4556,6 +4557,10 @@ public class ShortcutService extends IShortcutService.Stub { private int mUserId = UserHandle.USER_SYSTEM; + private int mShortcutMatchFlags = ShortcutManager.FLAG_MATCH_CACHED + | ShortcutManager.FLAG_MATCH_DYNAMIC | ShortcutManager.FLAG_MATCH_MANIFEST + | ShortcutManager.FLAG_MATCH_PINNED; + private void parseOptionsLocked(boolean takeUser) throws CommandException { String opt; @@ -4571,6 +4576,9 @@ public class ShortcutService extends IShortcutService.Stub { break; } // fallthrough + case "--flags": + mShortcutMatchFlags = Integer.parseInt(getNextArgRequired()); + break; default: throw new CommandException("Unknown option: " + opt); } @@ -4606,9 +4614,15 @@ public class ShortcutService extends IShortcutService.Stub { case "clear-shortcuts": handleClearShortcuts(); break; + case "get-shortcuts": + handleGetShortcuts(); + break; case "verify-states": // hidden command to verify various internal states. handleVerifyStates(); break; + case "has-shortcut-access": + handleHasShortcutAccess(); + break; default: return handleDefaultCommands(cmd); } @@ -4640,7 +4654,7 @@ public class ShortcutService extends IShortcutService.Stub { pw.println("[Deprecated] cmd shortcut get-default-launcher [--user USER_ID]"); pw.println(" Show the default launcher"); pw.println(" Note: This command is deprecated. Callers should query the default" - + " launcher directly from RoleManager instead."); + + " launcher from RoleManager instead."); pw.println(); pw.println("cmd shortcut unload-user [--user USER_ID]"); pw.println(" Unload a user from the memory"); @@ -4649,6 +4663,13 @@ public class ShortcutService extends IShortcutService.Stub { pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE"); pw.println(" Remove all shortcuts from a package, including pinned shortcuts"); pw.println(); + pw.println("cmd shortcut get-shortcuts [--user USER_ID] [--flags FLAGS] PACKAGE"); + pw.println(" Show the shortcuts for a package that match the given flags"); + pw.println(); + pw.println("cmd shortcut has-shortcut-access [--user USER_ID] PACKAGE"); + pw.println(" Prints \"true\" if the package can access shortcuts," + + " \"false\" otherwise"); + pw.println(); } private void handleResetThrottling() throws CommandException { @@ -4693,11 +4714,24 @@ public class ShortcutService extends IShortcutService.Stub { private void handleGetDefaultLauncher() throws CommandException { synchronized (mLock) { parseOptionsLocked(/* takeUser =*/ true); + + final String defaultLauncher = getDefaultLauncher(mUserId); + if (defaultLauncher == null) { + throw new CommandException( + "Failed to get the default launcher for user " + mUserId); + } + + // Get the class name of the component from PM to keep the old behaviour. final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); - // Default launcher from package manager. - final ComponentName defaultLauncher = mPackageManagerInternal - .getHomeActivitiesAsUser(allHomeCandidates, getParentOrSelfUserId(mUserId)); - getOutPrintWriter().println("Launcher: " + defaultLauncher); + mPackageManagerInternal.getHomeActivitiesAsUser(allHomeCandidates, + getParentOrSelfUserId(mUserId)); + for (ResolveInfo ri : allHomeCandidates) { + final ComponentInfo ci = ri.getComponentInfo(); + if (ci.packageName.equals(defaultLauncher)) { + getOutPrintWriter().println("Launcher: " + ci.getComponentName()); + break; + } + } } } @@ -4723,6 +4757,24 @@ public class ShortcutService extends IShortcutService.Stub { } } + private void handleGetShortcuts() throws CommandException { + synchronized (mLock) { + parseOptionsLocked(/* takeUser =*/ true); + final String packageName = getNextArgRequired(); + + Slog.i(TAG, "cmd: handleGetShortcuts: user=" + mUserId + ", flags=" + + mShortcutMatchFlags + ", package=" + packageName); + + final ShortcutUser user = ShortcutService.this.getUserShortcutsLocked(mUserId); + final ShortcutPackage p = user.getPackageShortcutsIfExists(packageName); + if (p == null) { + return; + } + + p.dumpShortcuts(getOutPrintWriter(), mShortcutMatchFlags); + } + } + private void handleVerifyStates() throws CommandException { try { verifyStatesForce(); // This will throw when there's an issue. @@ -4730,6 +4782,16 @@ public class ShortcutService extends IShortcutService.Stub { throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th)); } } + + private void handleHasShortcutAccess() throws CommandException { + synchronized (mLock) { + parseOptionsLocked(/* takeUser =*/ true); + final String packageName = getNextArgRequired(); + + boolean shortcutAccess = hasShortcutHostPermissionInner(packageName, mUserId); + getOutPrintWriter().println(Boolean.toString(shortcutAccess)); + } + } } // === Unit test support === diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java index c10e828d8c3d..82fc22c51afc 100644 --- a/services/core/java/com/android/server/policy/DisplayFoldController.java +++ b/services/core/java/com/android/server/policy/DisplayFoldController.java @@ -74,8 +74,8 @@ class DisplayFoldController { mHandler = handler; DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class); - deviceStateManager.registerDeviceStateListener(new DeviceStateListener(context), - new HandlerExecutor(handler)); + deviceStateManager.addDeviceStateListener(new HandlerExecutor(handler), + new DeviceStateListener(context)); } void finishedGoingToSleep() { diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index c77e266ee11a..db33e750d803 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -417,8 +417,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { WindowManagerFuncs windowManagerFuncs); /** - * Check permissions when adding a window or a window token from - * {@link android.app.WindowContext}. + * Check permissions when adding a window. * * @param type The window type * @param isRoundedCornerOverlay {@code true} to indicate the adding window is @@ -431,7 +430,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * else an error code, usually * {@link WindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add. * - * @see IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle, String) * @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY */ int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName, diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java index 57e39b6c6829..b995b19c5841 100644 --- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java +++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java @@ -45,6 +45,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.infra.ServiceConnector; +import java.lang.ref.WeakReference; + /** Manages the connection to the remote rotation resolver service. */ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResolverService> { @@ -128,13 +130,20 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol mProposedRotation = proposedRotation; mCurrentRotation = currentRotation; mPackageName = packageName; - mIRotationResolverCallback = new RotationResolverCallback(); + mIRotationResolverCallback = new RotationResolverCallback(this); mCancellationSignalInternal = cancellationSignal; mRequestStartTimeMillis = SystemClock.elapsedRealtime(); } void cancelInternal() { + synchronized (mLock) { + if (mIsFulfilled) { + Slog.v(TAG, "Trying to cancel the request that has been already fulfilled."); + return; + } + mIsFulfilled = true; + } Handler.getMain().post(() -> { synchronized (mLock) { try { @@ -147,9 +156,6 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol } } }); - synchronized (mLock) { - mIsFulfilled = true; - } mCallbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED); } @@ -160,44 +166,53 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol ipw.decreaseIndent(); } - private class RotationResolverCallback extends IRotationResolverCallback.Stub { + private static class RotationResolverCallback extends IRotationResolverCallback.Stub { + private WeakReference<RotationRequest> mRequestWeakReference; + + RotationResolverCallback(RotationRequest request) { + this.mRequestWeakReference = new WeakReference<>(request); + } + @Override public void onSuccess(int rotation) { - synchronized (mLock) { - if (mIsFulfilled) { + final RotationRequest request = mRequestWeakReference.get(); + synchronized (request.mLock) { + if (request.mIsFulfilled) { Slog.w(TAG, "Callback received after the rotation request is fulfilled."); return; } - mIsFulfilled = true; - mCallbackInternal.onSuccess(rotation); + request.mIsFulfilled = true; + request.mCallbackInternal.onSuccess(rotation); final long timeToCalculate = - SystemClock.elapsedRealtime() - mRequestStartTimeMillis; - logRotationStats(mProposedRotation, mCurrentRotation, rotation, + SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis; + logRotationStats(request.mProposedRotation, request.mCurrentRotation, rotation, timeToCalculate); } } @Override public void onFailure(int error) { - synchronized (mLock) { - if (mIsFulfilled) { + final RotationRequest request = mRequestWeakReference.get(); + synchronized (request.mLock) { + if (request.mIsFulfilled) { Slog.w(TAG, "Callback received after the rotation request is fulfilled."); return; } - mIsFulfilled = true; - mCallbackInternal.onFailure(error); + request.mIsFulfilled = true; + request.mCallbackInternal.onFailure(error); final long timeToCalculate = - SystemClock.elapsedRealtime() - mRequestStartTimeMillis; - logRotationStats(mProposedRotation, mCurrentRotation, RESOLUTION_FAILURE, - timeToCalculate); + SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis; + logRotationStats(request.mProposedRotation, request.mCurrentRotation, + RESOLUTION_FAILURE, timeToCalculate); } } @Override public void onCancellable(@NonNull ICancellationSignal cancellation) { - synchronized (mLock) { - mCancellation = cancellation; - if (mCancellationSignalInternal.isCanceled()) { + final RotationRequest request = mRequestWeakReference.get(); + synchronized (request.mLock) { + request.mCancellation = cancellation; + if (request.mCancellationSignalInternal.isCanceled()) { // Dispatch the cancellation signal if the client has cancelled the request. try { cancellation.cancel(); diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java index 3dbc32ad54d8..6f7c016cb3f6 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java @@ -122,14 +122,9 @@ final class RotationResolverManagerPerUserService extends } }); - if (mRemoteService != null) { - mRemoteService.resolveRotationLocked(mCurrentRequest); - mCurrentRequest.mIsDispatched = true; - } else { - Slog.w(TAG, "Remote service is not available at this moment."); - callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED); - cancelLocked(); - } + + mRemoteService.resolveRotationLocked(mCurrentRequest); + mCurrentRequest.mIsDispatched = true; } @GuardedBy("mLock") @@ -198,15 +193,6 @@ final class RotationResolverManagerPerUserService extends if (mCurrentRequest == null) { return; } - - if (mCurrentRequest.mIsFulfilled) { - if (isVerbose()) { - Slog.d(TAG, "Trying to cancel the request that has been already fulfilled."); - } - mCurrentRequest = null; - return; - } - mCurrentRequest.cancelInternal(); mCurrentRequest = null; } diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java index 4a37e7960912..03d76649e7ee 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java @@ -191,7 +191,7 @@ public class RotationResolverManagerService extends TAG); final RotationResolverManagerPerUserService service = getServiceForUserLocked( UserHandle.getCallingUserId()); - new RotationResolverShellCommend(service).exec(this, in, out, err, args, callback, + new RotationResolverShellCommand(service).exec(this, in, out, err, args, callback, resultReceiver); } } diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java index 0a873892b5bf..54a9edba4e03 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java @@ -26,13 +26,13 @@ import android.view.Surface; import java.io.PrintWriter; -final class RotationResolverShellCommend extends ShellCommand { +final class RotationResolverShellCommand extends ShellCommand { private static final int INITIAL_RESULT_CODE = -1; @NonNull private final RotationResolverManagerPerUserService mService; - RotationResolverShellCommend(@NonNull RotationResolverManagerPerUserService service) { + RotationResolverShellCommand(@NonNull RotationResolverManagerPerUserService service) { mService = service; } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 31984531d31f..5697564ce93f 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1094,7 +1094,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return; } if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken); - mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId); + mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId, + null /* options */); final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId); try { connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false, diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 2ecefeafcf5c..3bbc81a696e6 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -16,12 +16,27 @@ package com.android.server.wm; +import static android.os.Build.IS_USER; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; +import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY; +import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER; +import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_H; +import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_L; +import static com.android.server.accessibility.AccessibilityTraceProto.ACCESSIBILITY_SERVICE; +import static com.android.server.accessibility.AccessibilityTraceProto.CALENDAR_TIME; +import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PARAMS; +import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PKG; +import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_STACKS; +import static com.android.server.accessibility.AccessibilityTraceProto.ELAPSED_REALTIME_NANOS; +import static com.android.server.accessibility.AccessibilityTraceProto.PROCESS_NAME; +import static com.android.server.accessibility.AccessibilityTraceProto.THREAD_ID_NAME; +import static com.android.server.accessibility.AccessibilityTraceProto.WHERE; +import static com.android.server.accessibility.AccessibilityTraceProto.WINDOW_MANAGER_SERVICE; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.utils.RegionUtils.forEachRect; @@ -29,7 +44,9 @@ import static com.android.server.wm.utils.RegionUtils.forEachRect; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.NonNull; +import android.app.Application; import android.content.Context; +import android.content.pm.PackageManagerInternal; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; @@ -41,15 +58,20 @@ import android.graphics.PorterDuff.Mode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; +import android.os.Binder; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Process; +import android.os.SystemClock; import android.util.ArraySet; import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; import android.util.TypedValue; +import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.InsetsSource; import android.view.MagnificationSpec; @@ -64,13 +86,22 @@ import android.view.animation.Interpolator; import com.android.internal.R; import com.android.internal.os.SomeArgs; +import com.android.internal.util.TraceBuffer; +import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; +import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; +import java.io.File; +import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -79,26 +110,37 @@ import java.util.Set; * This class contains the accessibility related logic of the window manager. */ final class AccessibilityController { + private static final String TAG = AccessibilityController.class.getSimpleName(); - private final WindowManagerService mService; + private static final Object STATIC_LOCK = new Object(); + static AccessibilityControllerInternal + getAccessibilityControllerInternal(WindowManagerService service) { + return AccessibilityControllerInternalImpl.getInstance(service); + } + private final AccessibilityTracing mAccessibilityTracing; + private final WindowManagerService mService; private static final Rect EMPTY_RECT = new Rect(); private static final float[] sTempFloats = new float[9]; - public AccessibilityController(WindowManagerService service) { + AccessibilityController(WindowManagerService service) { mService = service; + mAccessibilityTracing = AccessibilityTracing.getInstance(service); } private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>(); - private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver = new SparseArray<>(); // Set to true if initializing window population complete. private boolean mAllObserversInitialized = true; - public boolean setMagnificationCallbacksLocked(int displayId, - MagnificationCallbacks callbacks) { + boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".setMagnificationCallbacks", + "displayId=" + displayId + "; callbacks={" + callbacks + "}"); + } boolean result = false; if (callbacks != null) { if (mDisplayMagnifiers.get(displayId) != null) { @@ -118,7 +160,7 @@ final class AccessibilityController { if (displayMagnifier == null) { throw new IllegalStateException("Magnification callbacks already cleared!"); } - displayMagnifier.destroyLocked(); + displayMagnifier.destroy(); mDisplayMagnifiers.remove(displayId); result = true; } @@ -133,8 +175,13 @@ final class AccessibilityController { * @param callback The callback. * @return {@code false} if display id is not valid or an embedded display. */ - public boolean setWindowsForAccessibilityCallbackLocked(int displayId, + boolean setWindowsForAccessibilityCallback(int displayId, WindowsForAccessibilityCallback callback) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".setWindowsForAccessibilityCallback", + "displayId=" + displayId + "; callback={" + callback + "}"); + } final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId); if (dc == null) { return false; @@ -147,7 +194,7 @@ final class AccessibilityController { // empty, that means this mapping didn't be set, and needs to do this again. // This happened when accessibility window observer is disabled and enabled again. if (mWindowsForAccessibilityObserver.get(displayId) == null) { - handleWindowObserverOfEmbeddedDisplayLocked(displayId, dc.getParentWindow()); + handleWindowObserverOfEmbeddedDisplay(displayId, dc.getParentWindow()); } return false; } else if (mWindowsForAccessibilityObserver.get(displayId) != null) { @@ -181,7 +228,12 @@ final class AccessibilityController { return true; } - public void performComputeChangedWindowsNotLocked(int displayId, boolean forceSend) { + void performComputeChangedWindowsNot(int displayId, boolean forceSend) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".performComputeChangedWindowsNot", + "displayId=" + displayId + "; forceSend=" + forceSend); + } WindowsForAccessibilityObserver observer = null; synchronized (mService.mGlobalLock) { final WindowsForAccessibilityObserver windowsForA11yObserver = @@ -191,86 +243,119 @@ final class AccessibilityController { } } if (observer != null) { - observer.performComputeChangedWindowsNotLocked(forceSend); + observer.performComputeChangedWindows(forceSend); } } - public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) { + void setMagnificationSpec(int displayId, MagnificationSpec spec) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".setMagnificationSpec", + "displayId=" + displayId + "; spec={" + spec + "}"); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.setMagnificationSpecLocked(spec); + displayMagnifier.setMagnificationSpec(spec); } final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayId); if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); + windowsForA11yObserver.scheduleComputeChangedWindows(); } } - public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) { + void getMagnificationRegion(int displayId, Region outMagnificationRegion) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".getMagnificationRegion", + "displayId=" + displayId + "; outMagnificationRegion={" + outMagnificationRegion + + "}"); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion); + displayMagnifier.getMagnificationRegion(outMagnificationRegion); } } - public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) { + void onRectangleOnScreenRequested(int displayId, Rect rectangle) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".onRectangleOnScreenRequested", + "displayId=" + displayId + "; rectangle={" + rectangle + "}"); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle); + displayMagnifier.onRectangleOnScreenRequested(rectangle); } // Not relevant for the window observer. } - public void onWindowLayersChangedLocked(int displayId) { + void onWindowLayersChanged(int displayId) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".onWindowLayersChanged", "displayId=" + displayId); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.onWindowLayersChangedLocked(); + displayMagnifier.onWindowLayersChanged(); } final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayId); if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); + windowsForA11yObserver.scheduleComputeChangedWindows(); } } - public void onRotationChangedLocked(DisplayContent displayContent) { + void onRotationChanged(DisplayContent displayContent) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".onRotationChanged", + "displayContent={" + displayContent + "}"); + } final int displayId = displayContent.getDisplayId(); final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.onRotationChangedLocked(displayContent); + displayMagnifier.onRotationChanged(displayContent); } final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayId); if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); + windowsForA11yObserver.scheduleComputeChangedWindows(); } } - public void onAppWindowTransitionLocked(int displayId, int transition) { + void onAppWindowTransition(int displayId, int transition) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".onAppWindowTransition", + "displayId=" + displayId + "; transition=" + transition); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.onAppWindowTransitionLocked(displayId, transition); + displayMagnifier.onAppWindowTransition(displayId, transition); } // Not relevant for the window observer. } - public void onWindowTransitionLocked(WindowState windowState, int transition) { + void onWindowTransition(WindowState windowState, int transition) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".onWindowTransition", + "windowState={" + windowState + "}; transition=" + transition); + } final int displayId = windowState.getDisplayId(); final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.onWindowTransitionLocked(windowState, transition); + displayMagnifier.onWindowTransition(windowState, transition); } final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayId); if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); + windowsForA11yObserver.scheduleComputeChangedWindows(); } } - public void onWindowFocusChangedNotLocked(int displayId) { + void onWindowFocusChangedNot(int displayId) { // Not relevant for the display magnifier. - + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".onWindowFocusChangedNot", "displayId=" + displayId); + } WindowsForAccessibilityObserver observer = null; synchronized (mService.mGlobalLock) { final WindowsForAccessibilityObserver windowsForA11yObserver = @@ -280,7 +365,7 @@ final class AccessibilityController { } } if (observer != null) { - observer.performComputeChangedWindowsNotLocked(false); + observer.performComputeChangedWindows(false); } // Since we abandon initializing observers if no window has focus, make sure all observers // are initialized. @@ -311,7 +396,7 @@ final class AccessibilityController { boolean areAllObserversInitialized = true; for (int i = unInitializedObservers.size() - 1; i >= 0; --i) { final WindowsForAccessibilityObserver observer = unInitializedObservers.get(i); - observer.performComputeChangedWindowsNotLocked(true); + observer.performComputeChangedWindows(true); areAllObserversInitialized &= observer.mInitialized; } synchronized (mService.mGlobalLock) { @@ -324,50 +409,89 @@ final class AccessibilityController { * another display is also taken into consideration. * @param displayIds the display ids of displays when the situation happens. */ - public void onSomeWindowResizedOrMovedLocked(int... displayIds) { + void onSomeWindowResizedOrMoved(int... displayIds) { + onSomeWindowResizedOrMovedWithCallingUid(Binder.getCallingUid(), displayIds); + } + + void onSomeWindowResizedOrMovedWithCallingUid(int callingUid, int... displayIds) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".onSomeWindowResizedOrMoved", + "displayIds={" + displayIds.toString() + "}", + "".getBytes(), + callingUid); + } // Not relevant for the display magnifier. for (int i = 0; i < displayIds.length; i++) { final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayIds[i]); if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); + windowsForA11yObserver.scheduleComputeChangedWindows(); } } } - public void drawMagnifiedRegionBorderIfNeededLocked(int displayId, - SurfaceControl.Transaction t) { + void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + TAG + ".drawMagnifiedRegionBorderIfNeeded", + "displayId=" + displayId + "; transaction={" + t + "}"); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(t); + displayMagnifier.drawMagnifiedRegionBorderIfNeeded(t); } // Not relevant for the window observer. } - public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { + MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".getMagnificationSpecForWindow", + "windowState={" + windowState + "}"); + } final int displayId = windowState.getDisplayId(); final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - return displayMagnifier.getMagnificationSpecForWindowLocked(windowState); + return displayMagnifier.getMagnificationSpecForWindow(windowState); } return null; } - public boolean hasCallbacksLocked() { + boolean hasCallbacks() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".hasCallbacks"); + } return (mDisplayMagnifiers.size() > 0 || mWindowsForAccessibilityObserver.size() > 0); } - public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) { + void setForceShowMagnifiableBounds(int displayId, boolean show) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".setForceShowMagnifiableBounds", + "displayId=" + displayId + "; show=" + show); + } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.setForceShowMagnifiableBoundsLocked(show); + displayMagnifier.setForceShowMagnifiableBounds(show); displayMagnifier.showMagnificationBoundsIfNeeded(); } } - public void handleWindowObserverOfEmbeddedDisplayLocked(int embeddedDisplayId, + void handleWindowObserverOfEmbeddedDisplay(int embeddedDisplayId, WindowState parentWindow) { + handleWindowObserverOfEmbeddedDisplay( + embeddedDisplayId, parentWindow, Binder.getCallingUid()); + } + + void handleWindowObserverOfEmbeddedDisplay( + int embeddedDisplayId, WindowState parentWindow, int callingUid) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(TAG + ".handleWindowObserverOfEmbeddedDisplay", + "embeddedDisplayId=" + embeddedDisplayId + "; parentWindowState={" + + parentWindow + "}", + "".getBytes(), + callingUid); + } if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) { return; } @@ -390,7 +514,7 @@ final class AccessibilityController { } } - private static void populateTransformationMatrixLocked(WindowState windowState, + private static void populateTransformationMatrix(WindowState windowState, Matrix outMatrix) { windowState.getTransformationMatrix(sTempFloats, outMatrix); } @@ -451,6 +575,7 @@ final class AccessibilityController { private final Handler mHandler; private final DisplayContent mDisplayContent; private final Display mDisplay; + private final AccessibilityTracing mAccessibilityTracing; private final MagnificationCallbacks mCallbacks; @@ -458,7 +583,7 @@ final class AccessibilityController { private boolean mForceShowMagnifiableBounds = false; - public DisplayMagnifier(WindowManagerService windowManagerService, + DisplayMagnifier(WindowManagerService windowManagerService, DisplayContent displayContent, Display display, MagnificationCallbacks callbacks) { @@ -469,36 +594,58 @@ final class AccessibilityController { mDisplay = display; mHandler = new MyHandler(mService.mH.getLooper()); mMagnifedViewport = new MagnifiedViewport(); + mAccessibilityTracing = AccessibilityTracing.getInstance(mService); mLongAnimationDuration = mDisplayContext.getResources().getInteger( com.android.internal.R.integer.config_longAnimTime); + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".DisplayMagnifier.constructor", + "windowManagerService={" + windowManagerService + "}; displayContent={" + + displayContent + "}; display={" + display + "}; callbacks={" + + callbacks + "}"); + } } - public void setMagnificationSpecLocked(MagnificationSpec spec) { - mMagnifedViewport.updateMagnificationSpecLocked(spec); - mMagnifedViewport.recomputeBoundsLocked(); + void setMagnificationSpec(MagnificationSpec spec) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + LOG_TAG + ".setMagnificationSpec", "spec={" + spec + "}"); + } + mMagnifedViewport.updateMagnificationSpec(spec); + mMagnifedViewport.recomputeBounds(); mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec); mService.scheduleAnimationLocked(); } - public void setForceShowMagnifiableBoundsLocked(boolean show) { + void setForceShowMagnifiableBounds(boolean show) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + LOG_TAG + ".setForceShowMagnifiableBounds", "show=" + show); + } mForceShowMagnifiableBounds = show; - mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true); + mMagnifedViewport.setMagnifiedRegionBorderShown(show, true); } - public boolean isForceShowingMagnifiableBoundsLocked() { + boolean isForceShowingMagnifiableBounds() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".isForceShowingMagnifiableBounds"); + } return mForceShowMagnifiableBounds; } - public void onRectangleOnScreenRequestedLocked(Rect rectangle) { + void onRectangleOnScreenRequested(Rect rectangle) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + LOG_TAG + ".onRectangleOnScreenRequested", "rectangle={" + rectangle + "}"); + } if (DEBUG_RECTANGLE_REQUESTED) { Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); } - if (!mMagnifedViewport.isMagnifyingLocked()) { + if (!mMagnifedViewport.isMagnifying()) { return; } Rect magnifiedRegionBounds = mTempRect2; - mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds); + mMagnifedViewport.getMagnifiedFrameInContentCoords(magnifiedRegionBounds); if (magnifiedRegionBounds.contains(rectangle)) { return; } @@ -511,31 +658,42 @@ final class AccessibilityController { args).sendToTarget(); } - public void onWindowLayersChangedLocked() { + void onWindowLayersChanged() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".onWindowLayersChanged"); + } if (DEBUG_LAYERS) { Slog.i(LOG_TAG, "Layers changed."); } - mMagnifedViewport.recomputeBoundsLocked(); + mMagnifedViewport.recomputeBounds(); mService.scheduleAnimationLocked(); } - public void onRotationChangedLocked(DisplayContent displayContent) { + void onRotationChanged(DisplayContent displayContent) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + LOG_TAG + ".onRotationChanged", "displayContent={" + displayContent + "}"); + } if (DEBUG_ROTATION) { final int rotation = displayContent.getRotation(); Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation) + " displayId: " + displayContent.getDisplayId()); } - mMagnifedViewport.onRotationChangedLocked(displayContent.getPendingTransaction()); + mMagnifedViewport.onRotationChanged(displayContent.getPendingTransaction()); mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED); } - public void onAppWindowTransitionLocked(int displayId, int transition) { + void onAppWindowTransition(int displayId, int transition) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".onAppWindowTransition", + "displayId=" + displayId + "; transition=" + transition); + } if (DEBUG_WINDOW_TRANSITIONS) { Slog.i(LOG_TAG, "Window transition: " + AppTransition.appTransitionOldToString(transition) + " displayId: " + displayId); } - final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); + final boolean magnifying = mMagnifedViewport.isMagnifying(); if (magnifying) { switch (transition) { case WindowManager.TRANSIT_OLD_ACTIVITY_OPEN: @@ -550,13 +708,17 @@ final class AccessibilityController { } } - public void onWindowTransitionLocked(WindowState windowState, int transition) { + void onWindowTransition(WindowState windowState, int transition) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".onWindowTransition", + "windowState={" + windowState + "}; transition=" + transition); + } if (DEBUG_WINDOW_TRANSITIONS) { Slog.i(LOG_TAG, "Window transition: " + AppTransition.appTransitionOldToString(transition) + " displayId: " + windowState.getDisplayId()); } - final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); + final boolean magnifying = mMagnifedViewport.isMagnifying(); final int type = windowState.mAttrs.type; switch (transition) { case WindowManagerPolicy.TRANSIT_ENTER: @@ -586,7 +748,7 @@ final class AccessibilityController { case WindowManager.LayoutParams.TYPE_QS_DIALOG: case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: { Rect magnifiedRegionBounds = mTempRect2; - mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked( + mMagnifedViewport.getMagnifiedFrameInContentCoords( magnifiedRegionBounds); Rect touchableRegionBounds = mTempRect1; windowState.getTouchableRegion(mTempRegion1); @@ -604,8 +766,12 @@ final class AccessibilityController { } } - public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { - MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked(); + MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow", + "windowState={" + windowState + "}"); + } + MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec(); if (spec != null && !spec.isNop()) { if (!windowState.shouldMagnify()) { return null; @@ -614,24 +780,38 @@ final class AccessibilityController { return spec; } - public void getMagnificationRegionLocked(Region outMagnificationRegion) { + void getMagnificationRegion(Region outMagnificationRegion) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationRegion", + "outMagnificationRegion={" + outMagnificationRegion + "}"); + } // Make sure we're working with the most current bounds - mMagnifedViewport.recomputeBoundsLocked(); - mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion); + mMagnifedViewport.recomputeBounds(); + mMagnifedViewport.getMagnificationRegion(outMagnificationRegion); } - public void destroyLocked() { + void destroy() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".destroy"); + } mMagnifedViewport.destroyWindow(); } // Can be called outside of a surface transaction - public void showMagnificationBoundsIfNeeded() { + void showMagnificationBoundsIfNeeded() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".showMagnificationBoundsIfNeeded"); + } mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED) .sendToTarget(); } - public void drawMagnifiedRegionBorderIfNeededLocked(SurfaceControl.Transaction t) { - mMagnifedViewport.drawWindowIfNeededLocked(t); + void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded", + "transition={" + t + "}"); + } + mMagnifedViewport.drawWindowIfNeeded(t); } void dump(PrintWriter pw, String prefix) { @@ -665,7 +845,7 @@ final class AccessibilityController { private boolean mFullRedrawNeeded; private int mTempLayer = 0; - public MagnifiedViewport() { + MagnifiedViewport() { mBorderWidth = mDisplayContext.getResources().getDimension( com.android.internal.R.dimen.accessibility_magnification_indicator_width); mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2); @@ -681,14 +861,14 @@ final class AccessibilityController { mCircularPath = null; } - recomputeBoundsLocked(); + recomputeBounds(); } - public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) { + void getMagnificationRegion(@NonNull Region outMagnificationRegion) { outMagnificationRegion.set(mMagnificationRegion); } - public void updateMagnificationSpecLocked(MagnificationSpec spec) { + void updateMagnificationSpec(MagnificationSpec spec) { if (spec != null) { mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY); } else { @@ -698,12 +878,12 @@ final class AccessibilityController { // to show the border. We will do so when the pending message is handled. if (!mHandler.hasMessages( MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) { - setMagnifiedRegionBorderShownLocked( - isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true); + setMagnifiedRegionBorderShown( + isMagnifying() || isForceShowingMagnifiableBounds(), true); } } - public void recomputeBoundsLocked() { + void recomputeBounds() { mDisplay.getRealSize(mTempPoint); final int screenWidth = mTempPoint.x; final int screenHeight = mTempPoint.y; @@ -721,7 +901,7 @@ final class AccessibilityController { SparseArray<WindowState> visibleWindows = mTempWindowStates; visibleWindows.clear(); - populateWindowsOnScreenLocked(visibleWindows); + populateWindowsOnScreen(visibleWindows); final int visibleWindowCount = visibleWindows.size(); for (int i = visibleWindowCount - 1; i >= 0; i--) { @@ -736,7 +916,7 @@ final class AccessibilityController { // Consider the touchable portion of the window Matrix matrix = mTempMatrix; - populateTransformationMatrixLocked(windowState, matrix); + populateTransformationMatrix(windowState, matrix); Region touchableRegion = mTempRegion3; windowState.getTouchableRegion(touchableRegion); Rect touchableFrame = mTempRect1; @@ -848,24 +1028,24 @@ final class AccessibilityController { return letterboxBounds; } - public void onRotationChangedLocked(SurfaceControl.Transaction t) { + void onRotationChanged(SurfaceControl.Transaction t) { // If we are showing the magnification border, hide it immediately so // the user does not see strange artifacts during rotation. The screenshot // used for rotation already has the border. After the rotation is complete // we will show the border. - if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) { - setMagnifiedRegionBorderShownLocked(false, false); + if (isMagnifying() || isForceShowingMagnifiableBounds()) { + setMagnifiedRegionBorderShown(false, false); final long delay = (long) (mLongAnimationDuration * mService.getWindowAnimationScaleLocked()); Message message = mHandler.obtainMessage( MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED); mHandler.sendMessageDelayed(message, delay); } - recomputeBoundsLocked(); + recomputeBounds(); mWindow.updateSize(t); } - public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) { + void setMagnifiedRegionBorderShown(boolean shown, boolean animate) { if (shown) { mFullRedrawNeeded = true; mOldMagnificationRegion.set(0, 0, 0, 0); @@ -873,31 +1053,31 @@ final class AccessibilityController { mWindow.setShown(shown, animate); } - public void getMagnifiedFrameInContentCoordsLocked(Rect rect) { + void getMagnifiedFrameInContentCoords(Rect rect) { MagnificationSpec spec = mMagnificationSpec; mMagnificationRegion.getBounds(rect); rect.offset((int) -spec.offsetX, (int) -spec.offsetY); rect.scale(1.0f / spec.scale); } - public boolean isMagnifyingLocked() { + boolean isMagnifying() { return mMagnificationSpec.scale > 1.0f; } - public MagnificationSpec getMagnificationSpecLocked() { + MagnificationSpec getMagnificationSpec() { return mMagnificationSpec; } - public void drawWindowIfNeededLocked(SurfaceControl.Transaction t) { - recomputeBoundsLocked(); + void drawWindowIfNeeded(SurfaceControl.Transaction t) { + recomputeBounds(); mWindow.drawIfNeeded(t); } - public void destroyWindow() { + void destroyWindow() { mWindow.releaseSurface(); } - private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { + private void populateWindowsOnScreen(SparseArray<WindowState> outWindows) { mTempLayer = 0; mDisplayContent.forAllWindows((w) -> { if (w.isOnScreen() && w.isVisible() @@ -929,7 +1109,7 @@ final class AccessibilityController { private boolean mInvalidated; - public ViewportWindow(Context context) { + ViewportWindow(Context context) { SurfaceControl surfaceControl = null; try { mDisplay.getRealSize(mTempPoint); @@ -971,7 +1151,7 @@ final class AccessibilityController { mInvalidated = true; } - public void setShown(boolean shown, boolean animate) { + void setShown(boolean shown, boolean animate) { synchronized (mService.mGlobalLock) { if (mShown == shown) { return; @@ -986,13 +1166,13 @@ final class AccessibilityController { @SuppressWarnings("unused") // Called reflectively from an animator. - public int getAlpha() { + int getAlpha() { synchronized (mService.mGlobalLock) { return mAlpha; } } - public void setAlpha(int alpha) { + void setAlpha(int alpha) { synchronized (mService.mGlobalLock) { if (mAlpha == alpha) { return; @@ -1005,7 +1185,7 @@ final class AccessibilityController { } } - public void setBounds(Region bounds) { + void setBounds(Region bounds) { synchronized (mService.mGlobalLock) { if (mBounds.equals(bounds)) { return; @@ -1018,7 +1198,7 @@ final class AccessibilityController { } } - public void updateSize(SurfaceControl.Transaction t) { + void updateSize(SurfaceControl.Transaction t) { synchronized (mService.mGlobalLock) { mDisplay.getRealSize(mTempPoint); t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y); @@ -1026,7 +1206,7 @@ final class AccessibilityController { } } - public void invalidate(Rect dirtyRect) { + void invalidate(Rect dirtyRect) { if (dirtyRect != null) { mDirtyRect.set(dirtyRect); } else { @@ -1036,7 +1216,7 @@ final class AccessibilityController { mService.scheduleAnimationLocked(); } - public void drawIfNeeded(SurfaceControl.Transaction t) { + void drawIfNeeded(SurfaceControl.Transaction t) { synchronized (mService.mGlobalLock) { if (!mInvalidated) { return; @@ -1078,7 +1258,7 @@ final class AccessibilityController { } } - public void releaseSurface() { + void releaseSurface() { mService.mTransactionFactory.get().remove(mSurfaceControl).apply(); mSurface.release(); } @@ -1101,7 +1281,7 @@ final class AccessibilityController { private final ValueAnimator mShowHideFrameAnimator; - public AnimationController(Context context, Looper looper) { + AnimationController(Context context, Looper looper) { super(looper); mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this, PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA); @@ -1114,7 +1294,7 @@ final class AccessibilityController { mShowHideFrameAnimator.setDuration(longAnimationDuration); } - public void onFrameShownStateChanged(boolean shown, boolean animate) { + void onFrameShownStateChanged(boolean shown, boolean animate) { obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED, shown ? 1 : 0, animate ? 1 : 0).sendToTarget(); } @@ -1158,7 +1338,7 @@ final class AccessibilityController { public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4; public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5; - public MyHandler(Looper looper) { + MyHandler(Looper looper) { super(looper); } @@ -1193,9 +1373,9 @@ final class AccessibilityController { case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { synchronized (mService.mGlobalLock) { - if (mMagnifedViewport.isMagnifyingLocked() - || isForceShowingMagnifiableBoundsLocked()) { - mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true); + if (mMagnifedViewport.isMagnifying() + || isForceShowingMagnifiableBounds()) { + mMagnifedViewport.setMagnifiedRegionBorderShown(true, true); mService.scheduleAnimationLocked(); } } @@ -1252,6 +1432,8 @@ final class AccessibilityController { private final Handler mHandler; + private final AccessibilityTracing mAccessibilityTracing; + private final WindowsForAccessibilityCallback mCallback; private final int mDisplayId; @@ -1263,24 +1445,32 @@ final class AccessibilityController { // Set to true if initializing window population complete. private boolean mInitialized; - public WindowsForAccessibilityObserver(WindowManagerService windowManagerService, + WindowsForAccessibilityObserver(WindowManagerService windowManagerService, int displayId, WindowsForAccessibilityCallback callback) { mService = windowManagerService; mCallback = callback; mDisplayId = displayId; mHandler = new MyHandler(mService.mH.getLooper()); + mAccessibilityTracing = AccessibilityTracing.getInstance(mService); mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration .getSendRecurringAccessibilityEventsInterval(); computeChangedWindows(true); } - public void performComputeChangedWindowsNotLocked(boolean forceSend) { + void performComputeChangedWindows(boolean forceSend) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".performComputeChangedWindows", + "forceSend=" + forceSend); + } mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS); computeChangedWindows(forceSend); } - public void scheduleComputeChangedWindowsLocked() { + void scheduleComputeChangedWindows() { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState(LOG_TAG + ".scheduleComputeChangedWindows"); + } if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) { mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS, mRecurringAccessibilityEventsIntervalMillis); @@ -1307,7 +1497,11 @@ final class AccessibilityController { * * @param forceSend Send the windows the accessibility even if they haven't changed. */ - public void computeChangedWindows(boolean forceSend) { + void computeChangedWindows(boolean forceSend) { + if (mAccessibilityTracing.isEnabled()) { + mAccessibilityTracing.logState( + LOG_TAG + ".computeChangedWindows", "forceSend=" + forceSend); + } if (DEBUG) { Slog.i(LOG_TAG, "computeChangedWindows()"); } @@ -1343,7 +1537,7 @@ final class AccessibilityController { unaccountedSpace.set(0, 0, screenWidth, screenHeight); final SparseArray<WindowState> visibleWindows = mTempWindowStates; - populateVisibleWindowsOnScreenLocked(visibleWindows); + populateVisibleWindowsOnScreen(visibleWindows); Set<IBinder> addedWindows = mTempBinderSet; addedWindows.clear(); @@ -1518,7 +1712,7 @@ final class AccessibilityController { // Map the frame to get what appears on the screen. Matrix matrix = mTempMatrix; - populateTransformationMatrixLocked(windowState, matrix); + populateTransformationMatrix(windowState, matrix); forEachRect(touchableRegion, rect -> { // Move to origin as all transforms are captured by the matrix. @@ -1563,7 +1757,7 @@ final class AccessibilityController { && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION); } - private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { + private void populateVisibleWindowsOnScreen(SparseArray<WindowState> outWindows) { final List<WindowState> tempWindowStatesList = new ArrayList<>(); final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); if (dc == null) { @@ -1637,4 +1831,292 @@ final class AccessibilityController { } } } + + private static final class AccessibilityControllerInternalImpl + implements AccessibilityControllerInternal { + + private static AccessibilityControllerInternal sInstance; + static AccessibilityControllerInternal getInstance(WindowManagerService service) { + synchronized (STATIC_LOCK) { + if (sInstance == null) { + sInstance = new AccessibilityControllerInternalImpl(service); + } + return sInstance; + } + } + + private final AccessibilityTracing mTracing; + private AccessibilityControllerInternalImpl(WindowManagerService service) { + mTracing = AccessibilityTracing.getInstance(service); + } + + @Override + public void startTrace() { + mTracing.startTrace(); + } + + @Override + public void stopTrace() { + mTracing.stopTrace(); + } + + @Override + public boolean isEnabled() { + return mTracing.isEnabled(); + } + + @Override + public void logTrace( + String where, String callingParams, byte[] a11yDump, int callingUid, + StackTraceElement[] stackTrace) { + mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace); + } + } + + private static final class AccessibilityTracing { + private static AccessibilityTracing sInstance; + static AccessibilityTracing getInstance(WindowManagerService service) { + synchronized (STATIC_LOCK) { + if (sInstance == null) { + sInstance = new AccessibilityTracing(service); + } + return sInstance; + } + } + + private static final int BUFFER_CAPACITY = 4096 * 1024; + private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb"; + private static final String TRACE_DIRECTORY = "/data/misc/a11ytrace/"; + private static final String TAG = "AccessibilityTracing"; + private static final long MAGIC_NUMBER_VALUE = + ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; + + private final Object mLock = new Object(); + private final WindowManagerService mService; + private final File mTraceFile; + private final TraceBuffer mBuffer; + private final LogHandler mHandler; + private volatile boolean mEnabled; + + AccessibilityTracing(WindowManagerService service) { + mService = service; + mTraceFile = new File(TRACE_FILENAME); + mBuffer = new TraceBuffer(BUFFER_CAPACITY); + HandlerThread workThread = new HandlerThread(TAG); + workThread.start(); + mHandler = new LogHandler(workThread.getLooper()); + } + + /** + * Start the trace. + */ + void startTrace() { + if (IS_USER) { + Slog.e(TAG, "Error: Tracing is not supported on user builds."); + return; + } + synchronized (mLock) { + try { + Files.createDirectories(Paths.get(TRACE_DIRECTORY)); + mTraceFile.createNewFile(); + } catch (Exception e) { + Slog.e(TAG, "Error: Failed to create trace file."); + return; + } + mEnabled = true; + mBuffer.resetBuffer(); + } + } + + /** + * Stops the trace and write the current buffer to disk + */ + void stopTrace() { + if (IS_USER) { + Slog.e(TAG, "Error: Tracing is not supported on user builds."); + return; + } + synchronized (mLock) { + mEnabled = false; + if (mEnabled) { + Slog.e(TAG, "Error: tracing enabled while waiting for flush."); + return; + } + writeTraceToFile(); + } + } + + boolean isEnabled() { + return mEnabled; + } + + /** + * Write an accessibility trace log entry. + */ + void logState(String where) { + if (!mEnabled) { + return; + } + logState(where, ""); + } + + /** + * Write an accessibility trace log entry. + */ + void logState(String where, String callingParams) { + if (!mEnabled) { + return; + } + logState(where, callingParams, "".getBytes()); + } + + /** + * Write an accessibility trace log entry. + */ + void logState(String where, String callingParams, byte[] a11yDump) { + if (!mEnabled) { + return; + } + logState(where, callingParams, a11yDump, Binder.getCallingUid()); + } + + /** + * Write an accessibility trace log entry. + */ + void logState( + String where, String callingParams, byte[] a11yDump, int callingUid) { + if (!mEnabled) { + return; + } + StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); + + logState(where, callingParams, a11yDump, callingUid, stackTraceElements); + } + + /** + * Write an accessibility trace log entry. + */ + void logState(String where, String callingParams, byte[] a11yDump, int callingUid, + StackTraceElement[] stackTrace) { + if (!mEnabled) { + return; + } + + log(where, callingParams, a11yDump, callingUid, stackTrace); + } + + private String toStackTraceString(StackTraceElement[] stackTraceElements) { + if (stackTraceElements == null) { + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + boolean skip = true; + for (int i = 0; i < stackTraceElements.length; i++) { + if (stackTraceElements[i].toString().contains( + AccessibilityTracing.class.getSimpleName())) { + skip = false; + } else if (!skip) { + stringBuilder.append(stackTraceElements[i].toString()).append("\n"); + } + } + return stringBuilder.toString(); + } + + /** + * Write the current state to the buffer + */ + private void log(String where, String callingParams, byte[] a11yDump, int callingUid, + StackTraceElement[] stackTrace) { + SimpleDateFormat fm = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + SomeArgs args = SomeArgs.obtain(); + args.arg1 = SystemClock.elapsedRealtimeNanos(); + args.arg2 = fm.format(new Date()).toString(); + args.arg3 = where; + args.arg4 = Process.myPid() + ":" + Application.getProcessName(); + args.arg5 = Thread.currentThread().getId() + ":" + Thread.currentThread().getName(); + args.arg6 = callingUid; + args.arg7 = callingParams; + args.arg8 = stackTrace; + args.arg9 = a11yDump; + mHandler.obtainMessage(LogHandler.MESSAGE_LOG_TRACE_ENTRY, args).sendToTarget(); + } + + /** + * Writes the trace buffer to new file for the bugreport. + */ + void writeTraceToFile() { + mHandler.sendEmptyMessage(LogHandler.MESSAGE_WRITE_FILE); + } + + private class LogHandler extends Handler { + public static final int MESSAGE_LOG_TRACE_ENTRY = 1; + public static final int MESSAGE_WRITE_FILE = 2; + + LogHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MESSAGE_LOG_TRACE_ENTRY: { + final SomeArgs args = (SomeArgs) message.obj; + try { + ProtoOutputStream os = new ProtoOutputStream(); + PackageManagerInternal pmInternal = + LocalServices.getService(PackageManagerInternal.class); + + long tokenOuter = os.start(ENTRY); + String callingStack = + toStackTraceString((StackTraceElement[]) args.arg8); + + os.write(ELAPSED_REALTIME_NANOS, (long) args.arg1); + os.write(CALENDAR_TIME, (String) args.arg2); + os.write(WHERE, (String) args.arg3); + os.write(PROCESS_NAME, (String) args.arg4); + os.write(THREAD_ID_NAME, (String) args.arg5); + os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg6)); + os.write(CALLING_PARAMS, (String) args.arg7); + os.write(CALLING_STACKS, callingStack); + os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg9); + + long tokenInner = os.start(WINDOW_MANAGER_SERVICE); + synchronized (mService.mGlobalLock) { + mService.dumpDebugLocked(os, WindowTraceLogLevel.ALL); + } + os.end(tokenInner); + + os.end(tokenOuter); + synchronized (mLock) { + mBuffer.add(os); + } + } catch (Exception e) { + Slog.e(TAG, "Exception while tracing state", e); + } + break; + } + case MESSAGE_WRITE_FILE: { + synchronized (mLock) { + writeTraceToFileInternal(); + } + break; + } + } + } + } + + /** + * Writes the trace buffer to disk. + */ + private void writeTraceToFileInternal() { + try { + ProtoOutputStream proto = new ProtoOutputStream(); + proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); + mBuffer.writeTraceToFile(mTraceFile, proto); + } catch (IOException e) { + Slog.e(TAG, "Unable to write buffer to file", e); + } + } + } + } diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 6bca4843e009..3a0eb397d210 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -978,6 +978,7 @@ class ActivityMetricsLogger { final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info, r, (int) startupTimeMs); BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot)); + mLastTransitionInfo.remove(r); if (!info.isInterestingToLoggerAndObserver()) { return infoSnapshot; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index f29b57ff9305..ccb349b9c5ab 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6528,7 +6528,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Allowing closing {@link ActivityRecord} to participate can lead to an Activity in another // task being started in the wrong orientation during the transition. if (!getDisplayContent().mClosingApps.contains(this) - && (isVisible() || getDisplayContent().mOpeningApps.contains(this))) { + && (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) { return mOrientation; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 2d6e9b2c68c5..4bcef408d9a0 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -130,6 +130,7 @@ import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.ActivityThread; import android.app.AlertDialog; +import android.app.AnrController; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.Dialog; @@ -494,6 +495,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { */ private volatile long mLastStopAppSwitchesTime; + private final List<AnrController> mAnrController = new ArrayList<>(); IActivityController mController = null; boolean mControllerIsAMonkey = false; @@ -2058,6 +2060,40 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mAppSwitchesAllowed; } + /** Register an {@link AnrController} to control the ANR dialog behavior */ + public void registerAnrController(AnrController controller) { + synchronized (mGlobalLock) { + mAnrController.add(controller); + } + } + + /** Unregister an {@link AnrController} */ + public void unregisterAnrController(AnrController controller) { + synchronized (mGlobalLock) { + mAnrController.remove(controller); + } + } + + /** @return the max ANR delay from all registered {@link AnrController} instances */ + public long getMaxAnrDelayMillis(ApplicationInfo info) { + if (info == null || info.packageName == null) { + return 0; + } + + final ArrayList<AnrController> controllers; + synchronized (mGlobalLock) { + controllers = new ArrayList<>(mAnrController); + } + + final String packageName = info.packageName; + long maxDelayMs = 0; + for (AnrController controller : controllers) { + maxDelayMs = Math.max(maxDelayMs, controller.getAnrDelayMillis(packageName, info.uid)); + } + maxDelayMs = Math.max(maxDelayMs, 0); + return maxDelayMs; + } + @Override public void setActivityController(IActivityController controller, boolean imAMonkey) { mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 4575cf7e9060..9ca7eca07dc6 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -724,8 +724,7 @@ public class AppTransitionController { final AccessibilityController accessibilityController = mDisplayContent.mWmService.mAccessibilityController; if (accessibilityController != null) { - accessibilityController.onAppWindowTransitionLocked( - mDisplayContent.getDisplayId(), transit); + accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 3ab79525de97..112fe526f0a8 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -685,6 +685,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ private boolean mInEnsureActivitiesVisible = false; + // Used to indicate that the movement of child tasks to top will not move the display to top as + // well and thus won't change the top resumed / focused record + boolean mDontMoveToTop; + private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> { WindowStateAnimator winAnimator = w.mWinAnimator; final ActivityRecord activity = w.mActivityRecord; @@ -948,7 +952,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } final ActivityRecord activity = w.mActivityRecord; - if (activity != null) { + if (activity != null && activity.isVisibleRequested()) { activity.updateLetterboxSurface(w); final boolean updateAllDrawn = activity.updateDrawnWindowStates(w); if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) { @@ -1226,7 +1230,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (mWmService.mAccessibilityController != null) { final int prevDisplayId = prevDc != null ? prevDc.getDisplayId() : INVALID_DISPLAY; - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(prevDisplayId, + mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(prevDisplayId, getDisplayId()); } } @@ -1447,7 +1451,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } config = new Configuration(); computeScreenConfiguration(config); - } else if (currentConfig != null) { + } else if (currentConfig != null + // If waiting for a remote rotation, don't prematurely update configuration. + && !mDisplayRotation.isWaitingForRemoteRotation()) { // No obvious action we need to take, but if our current state mismatches the // activity manager's, update it, disregarding font scale, which should remain set // to the value of the previous configuration. @@ -1850,7 +1856,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onRotationChangedLocked(this); + mWmService.mAccessibilityController.onRotationChanged(this); } } @@ -3400,7 +3406,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } void updateAccessibilityOnWindowFocusChanged(AccessibilityController accessibilityController) { - accessibilityController.onWindowFocusChangedNotLocked(getDisplayId()); + accessibilityController.onWindowFocusChangedNot(getDisplayId()); } private static void onWindowFocusChanged(WindowState oldFocus, WindowState newFocus) { @@ -4963,7 +4969,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (!mLocationInParentWindow.equals(x, y)) { mLocationInParentWindow.set(x, y); if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(mDisplayId); + mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(mDisplayId); } notifyLocationInParentDisplayChanged(); } @@ -5960,36 +5966,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - /** - * Returns the number of window tokens without surface on this display. A {@link WindowToken} - * won't have its {@link SurfaceControl} until a window is added to a {@link WindowToken}. - * The purpose of this method is to accumulate non-window containing {@link WindowToken}s and - * limit the usage if the count exceeds a number. - * - * @param callingUid app calling uid - * @return the number of window tokens without surface on this display - * @see WindowToken#addWindow(WindowState) - */ - int getWindowTokensWithoutSurfaceCount(int callingUid) { - List<WindowToken> tokens = new ArrayList<>(mTokenMap.values()); - int count = 0; - for (int i = tokens.size() - 1; i >= 0; i--) { - final WindowToken token = tokens.get(i); - if (callingUid != token.getOwnerUid()) { - continue; - } - // Skip if token is an Activity - if (token.asActivityRecord() != null) { - continue; - } - if (token.mSurfaceControl != null) { - continue; - } - count++; - } - return count; - } - MagnificationSpec getMagnificationSpec() { return mMagnificationSpec; } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 48e4df7a57ce..35611c055ea5 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -481,12 +481,12 @@ public class DisplayRotation { mRotation = rotation; + mDisplayContent.setLayoutNeeded(); + mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT, mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION); - mDisplayContent.setLayoutNeeded(); - if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) { // The screen rotation animation uses a screenshot to freeze the screen while windows // resize underneath. When we are rotating seamlessly, we allow the elements to diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index b82fdd237f2b..6d5abe1e2f31 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -16,11 +16,11 @@ package com.android.server.wm; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY; import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; -import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; -import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO; import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED; @@ -196,6 +196,14 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } + void setDontMoveToTop(DisplayContent dc, boolean dontMoveToTop) { + DisplayInfo displayInfo = dc.getDisplayInfo(); + SettingsProvider.SettingsEntry overrideSettings = + mSettingsProvider.getSettings(displayInfo); + overrideSettings.mDontMoveToTop = dontMoveToTop; + mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); + } + boolean shouldShowSystemDecorsLocked(DisplayContent dc) { if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { // Default display should show system decors. @@ -274,6 +282,10 @@ class DisplayWindowSettings { final int forcedScalingMode = settings.mForcedScalingMode != null ? settings.mForcedScalingMode : FORCE_SCALING_MODE_AUTO; dc.mDisplayScalingDisabled = forcedScalingMode == FORCE_SCALING_MODE_DISABLED; + + boolean dontMoveToTop = settings.mDontMoveToTop != null + ? settings.mDontMoveToTop : false; + dc.mDontMoveToTop = dontMoveToTop; } /** @@ -358,6 +370,8 @@ class DisplayWindowSettings { Boolean mIgnoreOrientationRequest; @Nullable Boolean mIgnoreDisplayCutout; + @Nullable + Boolean mDontMoveToTop; SettingsEntry() {} @@ -432,6 +446,10 @@ class DisplayWindowSettings { mIgnoreDisplayCutout = other.mIgnoreDisplayCutout; changed = true; } + if (other.mDontMoveToTop != mDontMoveToTop) { + mDontMoveToTop = other.mDontMoveToTop; + changed = true; + } return changed; } @@ -515,6 +533,11 @@ class DisplayWindowSettings { mIgnoreDisplayCutout = delta.mIgnoreDisplayCutout; changed = true; } + if (delta.mDontMoveToTop != null + && delta.mDontMoveToTop != mDontMoveToTop) { + mDontMoveToTop = delta.mDontMoveToTop; + changed = true; + } return changed; } @@ -531,7 +554,8 @@ class DisplayWindowSettings { && mImePolicy == null && mFixedToUserRotation == null && mIgnoreOrientationRequest == null - && mIgnoreDisplayCutout == null; + && mIgnoreDisplayCutout == null + && mDontMoveToTop == null; } @Override @@ -553,7 +577,8 @@ class DisplayWindowSettings { && Objects.equals(mImePolicy, that.mImePolicy) && Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation) && Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest) - && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout); + && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout) + && Objects.equals(mDontMoveToTop, that.mDontMoveToTop); } @Override @@ -561,7 +586,8 @@ class DisplayWindowSettings { return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth, mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode, mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy, - mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout); + mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout, + mDontMoveToTop); } @Override @@ -581,6 +607,7 @@ class DisplayWindowSettings { + ", mFixedToUserRotation=" + mFixedToUserRotation + ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest + ", mIgnoreDisplayCutout=" + mIgnoreDisplayCutout + + ", mDontMoveToTop=" + mDontMoveToTop + '}'; } } diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index 5f3ab43eca7c..737f8107f83f 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -405,6 +405,9 @@ class DisplayWindowSettingsProvider implements SettingsProvider { "ignoreOrientationRequest", null /* defaultValue */); settingsEntry.mIgnoreDisplayCutout = getBooleanAttribute(parser, "ignoreDisplayCutout", null /* defaultValue */); + settingsEntry.mDontMoveToTop = getBooleanAttribute(parser, + "dontMoveToTop", null /* defaultValue */); + fileData.mSettings.put(name, settingsEntry); } XmlUtils.skipCurrentTag(parser); @@ -496,6 +499,10 @@ class DisplayWindowSettingsProvider implements SettingsProvider { out.attributeBoolean(null, "ignoreDisplayCutout", settingsEntry.mIgnoreDisplayCutout); } + if (settingsEntry.mDontMoveToTop != null) { + out.attributeBoolean(null, "dontMoveToTop", + settingsEntry.mDontMoveToTop); + } out.endTag(null, "display"); } diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 803bec8941a8..de4bdaa57efa 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -47,7 +47,7 @@ public class DockedStackDividerController { mTouchRegion.set(touchRegion); // We need to report touchable region changes to accessibility. if (mDisplayContent.mWmService.mAccessibilityController != null) { - mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked( + mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved( mDisplayContent.getDisplayId()); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 07769aeff679..2c97f783e14d 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -847,8 +847,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> Slog.i(TAG, ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces"); } - // Send any pending task-info changes that were queued-up during a layout deferment - mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges"); mWmService.openSurfaceTransaction(); try { @@ -865,6 +863,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } + // Send any pending task-info changes that were queued-up during a layout deferment + mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); mWmService.mAnimator.executeAfterPrepareSurfacesRunnables(); checkAppTransitionReady(surfacePlacer); diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java index 62c0527dfe1b..0902948bf559 100644 --- a/services/core/java/com/android/server/wm/ShellRoot.java +++ b/services/core/java/com/android/server/wm/ShellRoot.java @@ -191,7 +191,7 @@ public class ShellRoot { } } if (mDisplayContent.mWmService.mAccessibilityController != null) { - mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked( + mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved( mDisplayContent.getDisplayId()); } } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 63732d8d4bdc..40248c43fe5d 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -404,7 +404,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } // We don't allow untrusted display to top when root task moves to top, // until user tapping this display to change display position as top intentionally. - if (!mDisplayContent.isTrusted() && !getParent().isOnTop()) { + // + // Displays with {@code mDontMoveToTop} property set to {@code true} won't be + // allowed to top neither. + if ((!mDisplayContent.isTrusted() || mDisplayContent.mDontMoveToTop) + && !getParent().isOnTop()) { includingParents = false; } final int targetPosition = findPositionForRootTask(position, child, false /* adding */); diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index f572e8e2f0aa..43303d4a5d7e 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; @@ -51,7 +50,7 @@ class WallpaperWindowToken extends WindowToken { WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) { - super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, INVALID_UID, + super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, false /* roundedCornerOverlay */, false /* fromClientToken */, options); dc.mWallpaperController.addWallpaperToken(this); setWindowingMode(WINDOWING_MODE_FULLSCREEN); diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 1a0e16b9c771..91a6664e0b36 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -164,7 +164,7 @@ public class WindowAnimator { dc.checkAppWindowsReadyToShow(); if (accessibilityController != null) { - accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId, + accessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index eed3299cc93d..bb04ace423c2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -23,6 +23,7 @@ import android.content.Context; import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManagerInternal; +import android.os.Bundle; import android.os.IBinder; import android.view.Display; import android.view.IInputFilter; @@ -48,6 +49,41 @@ import java.util.List; public abstract class WindowManagerInternal { /** + * Interface for accessibility features implemented by AccessibilityController inside + * WindowManager. + */ + public interface AccessibilityControllerInternal { + /** + * Enable the accessibility trace logging. + */ + void startTrace(); + + /** + * Disable the accessibility trace logging. + */ + void stopTrace(); + + /** + * Is trace enabled or not. + */ + boolean isEnabled(); + + /** + * Add an accessibility trace entry. + * + * @param where A string to identify this log entry, which can be used to filter/search + * through the tracing file. + * @param callingParams The parameters for the method to be logged. + * @param a11yDump The proto byte array for a11y state when the entry is generated. + * @param callingUid The calling uid. + * @param stackTrace The stack trace, null if not needed. + */ + void logTrace( + String where, String callingParams, byte[] a11yDump, int callingUid, + StackTraceElement[] stackTrace); + } + + /** * Interface to receive a callback when the windows reported for * accessibility changed. */ @@ -224,6 +260,11 @@ public abstract class WindowManagerInternal { } /** + * Request the interface to access features implemented by AccessibilityController. + */ + public abstract AccessibilityControllerInternal getAccessibilityController(); + + /** * Request that the window manager call * {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager} * within a surface transaction at a later time. @@ -368,8 +409,10 @@ public abstract class WindowManagerInternal { * @param token The token to add. * @param type The window type. * @param displayId The display to add the token to. + * @param options A bundle used to pass window-related options. */ - public abstract void addWindowToken(android.os.IBinder token, int type, int displayId); + public abstract void addWindowToken(@NonNull android.os.IBinder token, int type, int displayId, + @Nullable Bundle options); /** * Removes a window token. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8965cab0b725..a41761f529be 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -23,7 +23,6 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.Manifest.permission.RESTRICTED_VR_ACCESS; -import static android.Manifest.permission.STATUS_BAR_SERVICE; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; @@ -35,7 +34,6 @@ import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEME import static android.content.pm.PackageManager.FEATURE_PC; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; -import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; import static android.os.Process.myPid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -82,7 +80,6 @@ import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_RELAUNCH; import static android.view.WindowManagerGlobal.ADD_OKAY; -import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS; import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; @@ -457,12 +454,6 @@ public class WindowManagerService extends IWindowManager.Stub private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000; - /** The maximum count of window tokens without surface that an app can register. */ - private static final int MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE = 5; - - /** System UI can create more window context... */ - private static final int SYSTEM_UI_MULTIPLIER = 2; - /** * Override of task letterbox aspect ratio that is set via ADB with * set-task-letterbox-aspect-ratio or via {@link @@ -1610,7 +1601,7 @@ public class WindowManagerService extends IWindowManager.Stub final Bundle options = mWindowContextListenerController .getOptions(windowContextToken); token = new WindowToken(this, binder, type, false /* persistOnEmpty */, - displayContent, session.mCanAddInternalSystemWindow, callingUid, + displayContent, session.mCanAddInternalSystemWindow, isRoundedCornerOverlay, true /* fromClientToken */, options); } else { final IBinder binder = attrs.token != null ? attrs.token : client.asBinder(); @@ -2176,6 +2167,7 @@ public class WindowManagerService extends IWindowManager.Stub void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets, Rect visibleInsets, Region touchableRegion) { + int uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -2201,8 +2193,8 @@ public class WindowManagerService extends IWindowManager.Stub // We need to report touchable region changes to accessibility. if (mAccessibilityController != null) { - mAccessibilityController.onSomeWindowResizedOrMovedLocked( - w.getDisplayContent().getDisplayId()); + mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid( + uid, w.getDisplayContent().getDisplayId()); } } } @@ -2216,7 +2208,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mAccessibilityController != null) { WindowState window = mWindowMap.get(token); if (window != null) { - mAccessibilityController.onRectangleOnScreenRequestedLocked( + mAccessibilityController.onRectangleOnScreenRequested( window.getDisplayId(), rectangle); } } @@ -2319,8 +2311,8 @@ public class WindowManagerService extends IWindowManager.Stub if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0) && (mAccessibilityController != null)) { // No move or resize, but the controller checks for title changes as well - mAccessibilityController.onSomeWindowResizedOrMovedLocked( - win.getDisplayContent().getDisplayId()); + mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid( + uid, win.getDisplayContent().getDisplayId()); } if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) { @@ -2620,7 +2612,7 @@ public class WindowManagerService extends IWindowManager.Stub win.destroySurface(false, stopped); } if (mAccessibilityController != null) { - mAccessibilityController.onWindowTransitionLocked(win, transit); + mAccessibilityController.onWindowTransition(win, transit); } return focusMayChange; @@ -2713,91 +2705,36 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void addWindowToken(IBinder binder, int type, int displayId) { - addWindowTokenWithOptions(binder, type, displayId, null /* options */, - null /* packageName */, false /* fromClientToken */); - } - - @Override - public int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, - String packageName) { - if (tokenCountExceed()) { - return ADD_TOO_MANY_TOKENS; + public void addWindowToken(@NonNull IBinder binder, int type, int displayId, + @Nullable Bundle options) { + if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - return addWindowTokenWithOptions(binder, type, displayId, options, packageName, - true /* fromClientToken */); - } - private boolean tokenCountExceed() { - final int callingUid = Binder.getCallingUid(); - // Don't check if caller is from system server. - if (callingUid == myPid()) { - return false; - } - final int limit = - (checkCallingPermission(STATUS_BAR_SERVICE, "addWindowTokenWithOptions")) - ? MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE * SYSTEM_UI_MULTIPLIER - : MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE; synchronized (mGlobalLock) { - int[] count = new int[1]; - mRoot.forAllDisplays(d -> count[0] += d.getWindowTokensWithoutSurfaceCount(callingUid)); - return count[0] >= limit; - } - } - - private int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, - String packageName, boolean fromClientToken) { - final boolean callerCanManageAppTokens = - checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()"); - // WindowContext users usually don't hold MANAGE_APP_TOKEN permission. Check permissions - // by checkAddPermission. - if (!callerCanManageAppTokens) { - final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */, - packageName, new int[1]); - if (res != ADD_OKAY) { - return res; + final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */); + if (dc == null) { + ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s" + + " for non-exiting displayId=%d", binder, displayId); + return; } - } - - final int callingUid = Binder.getCallingUid(); - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - if (!callerCanManageAppTokens) { - if (packageName == null || !unprivilegedAppCanCreateTokenWith( - null /* parentWindow */, callingUid, type, type, binder, - packageName)) { - throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); - } - } - final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */); - if (dc == null) { - ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s" - + " for non-exiting displayId=%d", binder, displayId); - return WindowManagerGlobal.ADD_INVALID_DISPLAY; - } - - WindowToken token = dc.getWindowToken(binder); - if (token != null) { - ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s" - + " for already created window token: %s" - + " displayId=%d", binder, token, displayId); - return WindowManagerGlobal.ADD_DUPLICATE_ADD; - } - // TODO(window-container): Clean up dead tokens - if (type == TYPE_WALLPAPER) { - new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens, - options); - } else { - new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens, - callingUid, false /* roundedCornerOverlay */, fromClientToken, options); - } + WindowToken token = dc.getWindowToken(binder); + if (token != null) { + ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s" + + " for already created window token: %s" + + " displayId=%d", binder, token, displayId); + return; + } + if (type == TYPE_WALLPAPER) { + new WallpaperWindowToken(this, binder, true, dc, + true /* ownerCanManageAppTokens */, options); + } else { + new WindowToken(this, binder, type, true /* persistOnEmpty */, dc, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, + false /* fromClientToken */, options); } - } finally { - Binder.restoreCallingIdentity(origId); } - return WindowManagerGlobal.ADD_OKAY; } /** @@ -2877,38 +2814,26 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void removeWindowToken(IBinder binder, int displayId) { - final boolean callerCanManageAppTokens = - checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()"); - final WindowToken windowToken; - synchronized (mGlobalLock) { - windowToken = mRoot.getWindowToken(binder); - } - if (windowToken == null) { - ProtoLog.w(WM_ERROR, - "removeWindowToken: Attempted to remove non-existing token: %s", binder); - return; - } - final int callingUid = Binder.getCallingUid(); - - // If MANAGE_APP_TOKEN permission is not held(usually from WindowContext), callers can only - // remove the window tokens which they added themselves. - if (!callerCanManageAppTokens && (windowToken.getOwnerUid() == INVALID_UID - || callingUid != windowToken.getOwnerUid())) { - throw new SecurityException("removeWindowToken: Requires MANAGE_APP_TOKENS permission" - + " to remove token owned by another uid"); + if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) { + throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } - final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s" + " for non-exiting displayId=%d", binder, displayId); return; } - - dc.removeWindowToken(binder); + final WindowToken token = dc.removeWindowToken(binder); + if (token == null) { + ProtoLog.w(WM_ERROR, + "removeWindowToken: Attempted to remove non-existing token: %s", + binder); + return; + } dc.getInputMonitor().updateInputWindowsLw(true /*force*/); } } finally { @@ -7151,6 +7076,7 @@ public class WindowManagerService extends IWindowManager.Stub checkCallerOwnsDisplay(displayId); synchronized (mGlobalLock) { + int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { final WindowState win = windowForClientLocked(null, client, false); @@ -7162,8 +7088,8 @@ public class WindowManagerService extends IWindowManager.Stub // Notifies AccessibilityController to re-compute the window observer of // this embedded display if (mAccessibilityController != null) { - mAccessibilityController.handleWindowObserverOfEmbeddedDisplayLocked(displayId, - win); + mAccessibilityController.handleWindowObserverOfEmbeddedDisplay( + displayId, win, uid); } } finally { Binder.restoreCallingIdentity(token); @@ -7529,6 +7455,12 @@ public class WindowManagerService extends IWindowManager.Stub private final class LocalService extends WindowManagerInternal { @Override + public AccessibilityControllerInternal getAccessibilityController() { + return AccessibilityController.getAccessibilityControllerInternal( + WindowManagerService.this); + } + + @Override public void clearSnapshotCache() { synchronized (mGlobalLock) { mTaskSnapshotController.clearSnapshotCache(); @@ -7546,7 +7478,7 @@ public class WindowManagerService extends IWindowManager.Stub public void setMagnificationSpec(int displayId, MagnificationSpec spec) { synchronized (mGlobalLock) { if (mAccessibilityController != null) { - mAccessibilityController.setMagnificationSpecLocked(displayId, spec); + mAccessibilityController.setMagnificationSpec(displayId, spec); } else { throw new IllegalStateException("Magnification callbacks not set!"); } @@ -7557,7 +7489,7 @@ public class WindowManagerService extends IWindowManager.Stub public void setForceShowMagnifiableBounds(int displayId, boolean show) { synchronized (mGlobalLock) { if (mAccessibilityController != null) { - mAccessibilityController.setForceShowMagnifiableBoundsLocked(displayId, show); + mAccessibilityController.setForceShowMagnifiableBounds(displayId, show); } else { throw new IllegalStateException("Magnification callbacks not set!"); } @@ -7568,8 +7500,7 @@ public class WindowManagerService extends IWindowManager.Stub public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) { synchronized (mGlobalLock) { if (mAccessibilityController != null) { - mAccessibilityController.getMagnificationRegionLocked(displayId, - magnificationRegion); + mAccessibilityController.getMagnificationRegion(displayId, magnificationRegion); } else { throw new IllegalStateException("Magnification callbacks not set!"); } @@ -7585,7 +7516,7 @@ public class WindowManagerService extends IWindowManager.Stub } MagnificationSpec spec = null; if (mAccessibilityController != null) { - spec = mAccessibilityController.getMagnificationSpecForWindowLocked(windowState); + spec = mAccessibilityController.getMagnificationSpecForWindow(windowState); } if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) { return null; @@ -7607,9 +7538,9 @@ public class WindowManagerService extends IWindowManager.Stub mAccessibilityController = new AccessibilityController( WindowManagerService.this); } - boolean result = mAccessibilityController.setMagnificationCallbacksLocked( + boolean result = mAccessibilityController.setMagnificationCallbacks( displayId, callbacks); - if (!mAccessibilityController.hasCallbacksLocked()) { + if (!mAccessibilityController.hasCallbacks()) { mAccessibilityController = null; } return result; @@ -7625,9 +7556,9 @@ public class WindowManagerService extends IWindowManager.Stub WindowManagerService.this); } final boolean result = - mAccessibilityController.setWindowsForAccessibilityCallbackLocked( + mAccessibilityController.setWindowsForAccessibilityCallback( displayId, callback); - if (!mAccessibilityController.hasCallbacksLocked()) { + if (!mAccessibilityController.hasCallbacks()) { mAccessibilityController = null; } return result; @@ -7716,8 +7647,9 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void addWindowToken(IBinder token, int type, int displayId) { - WindowManagerService.this.addWindowToken(token, type, displayId); + public void addWindowToken(IBinder token, int type, int displayId, + @Nullable Bundle options) { + WindowManagerService.this.addWindowToken(token, type, displayId, options); } @Override @@ -7825,7 +7757,7 @@ public class WindowManagerService extends IWindowManager.Stub accessibilityController = mAccessibilityController; } if (accessibilityController != null) { - accessibilityController.performComputeChangedWindowsNotLocked(displayId, true); + accessibilityController.performComputeChangedWindowsNot(displayId, true); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 89cb163d5a55..fc1c7edb234c 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1721,7 +1721,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override boolean isVisibleRequested() { - return isVisible(); + return isVisible() && (mActivityRecord == null || mActivityRecord.isVisibleRequested()); } /** @@ -2020,7 +2020,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final int winTransit = TRANSIT_EXIT; mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */); if (accessibilityController != null) { - accessibilityController.onWindowTransitionLocked(this, winTransit); + accessibilityController.onWindowTransition(this, winTransit); } } setDisplayLayoutNeeded(); @@ -2034,7 +2034,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isVisibleNow()) { mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false); if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT); + mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT); } changed = true; if (displayContent != null) { @@ -2112,7 +2112,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId()); + mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId()); } updateLocationInParentDisplayIfNeeded(); @@ -2132,7 +2132,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP && !mAnimatingExit && (mWindowFrames.mRelFrame.top != mWindowFrames.mLastRelFrame.top || mWindowFrames.mRelFrame.left != mWindowFrames.mLastRelFrame.left) - && (!mIsChildWindow || !getParentWindow().hasMoved()); + && (!mIsChildWindow || !getParentWindow().hasMoved()) + && !mWmService.mAtmService.getTransitionController().isCollecting(); } boolean isObscuringDisplay() { @@ -2345,7 +2346,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWmService.requestTraversal(); } if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit); + mWmService.mAccessibilityController.onWindowTransition(this, transit); } } final boolean isAnimating = mAnimatingExit || isAnimating(TRANSITION | PARENTS, @@ -3635,6 +3636,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mActivityRecord != null && mActivityRecord.isRelaunching()) { return; } + // If the activity is invisible or going invisible, don't report either since it is going + // away. This is likely during a transition so we want to preserve the original state. + if (mActivityRecord != null && !mActivityRecord.isVisibleRequested()) { + return; + } if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag()); @@ -3672,7 +3678,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP displayId); if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(displayId); + mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId); } updateLocationInParentDisplayIfNeeded(); } catch (RemoteException e) { @@ -4775,7 +4781,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId()); + mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId()); } if (!isSelfOrAncestorWindowAnimatingExit()) { @@ -5304,7 +5310,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP updateSurfacePositionNonOrganized(); // Send information to SufaceFlinger about the priority of the current window. updateFrameRateSelectionPriorityIfNeeded(); - updateGlobalScaleIfNeeded(); + if (isVisibleRequested()) updateGlobalScaleIfNeeded(); mWinAnimator.prepareSurfaceLocked(getSyncTransaction()); super.prepareSurfaces(); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index d164f30fde2c..2da3dda831e1 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -817,7 +817,7 @@ class WindowStateAnimator { } if (mService.mAccessibilityController != null) { - mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit); + mService.mAccessibilityController.onWindowTransition(mWin, transit); } } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index cd18311d7d54..c3a4609c02a1 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; @@ -113,8 +112,6 @@ class WindowToken extends WindowContainer<WindowState> { */ private final boolean mFromClientToken; - private final int mOwnerUid; - /** * Used to fix the transform of the token to be rotated to a rotation different than it's * display. The window frames and surfaces corresponding to this token will be layouted and @@ -205,27 +202,19 @@ class WindowToken extends WindowContainer<WindowState> { WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) { - this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, INVALID_UID, - roundedCornerOverlay, false /* fromClientToken */); - } - - WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, - DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, - boolean roundedCornerOverlay, boolean fromClientToken) { - this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, ownerUid, - roundedCornerOverlay, fromClientToken, null /* options */); + this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, + roundedCornerOverlay, false /* fromClientToken */, null /* options */); } WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, - DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, - boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) { + DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay, + boolean fromClientToken, @Nullable Bundle options) { super(service); token = _token; windowType = type; mOptions = options; mPersistOnEmpty = persistOnEmpty; mOwnerCanManageAppTokens = ownerCanManageAppTokens; - mOwnerUid = ownerUid; mRoundedCornerOverlay = roundedCornerOverlay; mFromClientToken = fromClientToken; if (dc != null) { @@ -739,10 +728,6 @@ class WindowToken extends WindowContainer<WindowState> { mRoundedCornerOverlay); } - int getOwnerUid() { - return mOwnerUid; - } - boolean isFromClient() { return mFromClientToken; } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index cc7e00a43a6e..91be0564a26f 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -134,7 +134,7 @@ cc_defaults { "android.hardware.broadcastradio@1.0", "android.hardware.broadcastradio@1.1", "android.hardware.contexthub@1.0", - "android.hardware.gnss-cpp", + "android.hardware.gnss-V1-cpp", "android.hardware.gnss@1.0", "android.hardware.gnss@1.1", "android.hardware.gnss@2.0", @@ -147,12 +147,12 @@ cc_defaults { "android.hardware.light@2.0", "android.hardware.power@1.0", "android.hardware.power@1.1", - "android.hardware.power-cpp", + "android.hardware.power-V1-cpp", "android.hardware.power.stats@1.0", "android.hardware.power.stats-ndk_platform", "android.hardware.thermal@1.0", "android.hardware.tv.input@1.0", - "android.hardware.vibrator-unstable-cpp", + "android.hardware.vibrator-V2-cpp", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", @@ -162,7 +162,7 @@ cc_defaults { "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", "android.frameworks.stats@1.0", - "android.system.suspend.control-cpp", + "android.system.suspend.control-V1-cpp", "android.system.suspend.control.internal-cpp", "android.system.suspend@1.0", "service.incremental", diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index 48f8b1505d3a..59b7367102c8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -136,6 +136,8 @@ class ActiveAdmin { private static final String TAG_PASSWORD_COMPLEXITY = "password-complexity"; private static final String TAG_ORGANIZATION_ID = "organization-id"; private static final String TAG_ENROLLMENT_SPECIFIC_ID = "enrollment-specific-id"; + private static final String TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS = + "admin-can-grant-sensors-permissions"; private static final String ATTR_VALUE = "value"; private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification"; private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications"; @@ -277,6 +279,7 @@ class ActiveAdmin { boolean mCommonCriteriaMode; public String mOrganizationId; public String mEnrollmentSpecificId; + public boolean mAdminCanGrantSensorsPermissions; ActiveAdmin(DeviceAdminInfo info, boolean isParent) { this.info = info; @@ -543,6 +546,8 @@ class ActiveAdmin { if (!TextUtils.isEmpty(mEnrollmentSpecificId)) { writeTextToXml(out, TAG_ENROLLMENT_SPECIFIC_ID, mEnrollmentSpecificId); } + writeAttributeValueToXml(out, TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS, + mAdminCanGrantSensorsPermissions); } void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException { @@ -792,6 +797,9 @@ class ActiveAdmin { Log.w(DevicePolicyManagerService.LOG_TAG, "Missing Enrollment-specific ID."); } + } else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) { + mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE, + false); } else { Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -1143,5 +1151,8 @@ class ActiveAdmin { pw.print("mEnrollmentSpecificId="); pw.println(mEnrollmentSpecificId); } + + pw.print("mAdminCanGrantSensorsPermissions"); + pw.println(mAdminCanGrantSensorsPermissions); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 160033edb093..b52347f509f8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -129,6 +129,9 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) { } - public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) { + public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {} + + public boolean canAdminGrantSensorsPermissionsForUser(int userId) { + return false; } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java index 8b2beb22bead..8ea21ec74ad6 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java @@ -46,11 +46,15 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { @GuardedBy("mLock") private final SparseIntArray mPermissionPolicy = new SparseIntArray(); + @GuardedBy("mLock") + private final SparseBooleanArray mCanGrantSensorsPermissions = new SparseBooleanArray(); + public void onUserRemoved(int userHandle) { synchronized (mLock) { mScreenCaptureDisabled.delete(userHandle); mPasswordQuality.delete(userHandle); mPermissionPolicy.delete(userHandle); + mCanGrantSensorsPermissions.delete(userHandle); } } @@ -97,6 +101,21 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { } } + @Override + public boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle) { + synchronized (mLock) { + return mCanGrantSensorsPermissions.get(userHandle, false); + } + } + + /** Sets ahmin control over permission grants for user. */ + public void setAdminCanGrantSensorsPermissions(@UserIdInt int userHandle, + boolean canGrant) { + synchronized (mLock) { + mCanGrantSensorsPermissions.put(userHandle, canGrant); + } + } + /** Dump content */ public void dump(IndentingPrintWriter pw) { pw.println("Device policy cache:"); @@ -104,6 +123,8 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { pw.println("Screen capture disabled: " + mScreenCaptureDisabled.toString()); pw.println("Password quality: " + mPasswordQuality.toString()); pw.println("Permission policy: " + mPermissionPolicy.toString()); + pw.println("Admin can grant sensors permission: " + + mCanGrantSensorsPermissions.toString()); pw.decreaseIndent(); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 717e77b22bdc..404b0cf8c7b8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -313,6 +313,7 @@ import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.SystemServerInitThreadPool; import com.android.server.SystemService; import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo; +import com.android.server.devicepolicy.Owners.OwnerDto; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.pm.RestrictionsSet; @@ -1130,6 +1131,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason); } + // Used by DevicePolicyManagerServiceShellCommand + List<OwnerDto> listAllOwners() { + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); + + List<OwnerDto> owners = mOwners.listAllOwners(); + synchronized (getLockObject()) { + for (int i = 0; i < owners.size(); i++) { + OwnerDto owner = owners.get(i); + owner.isAffiliated = isUserAffiliatedWithDeviceLocked(owner.userId); + } + } + + return owners; + } + /** * Unit test will subclass it to inject mocks. */ @@ -2974,6 +2991,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { updatePasswordQualityCacheForUserGroup( userId == UserHandle.USER_SYSTEM ? UserHandle.USER_ALL : userId); updatePermissionPolicyCache(userId); + updateAdminCanGrantSensorsPermissionCache(userId); startOwnerService(userId, "start-user"); } @@ -13466,15 +13484,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mOwners.hasDeviceOwner()) { return false; } - if (userId == mOwners.getDeviceOwnerUserId()) { - // The user that the DO is installed on is always affiliated with the device. - return true; - } if (userId == UserHandle.USER_SYSTEM) { // The system user is always affiliated in a DO device, // even if in headless system user mode. return true; } + if (userId == mOwners.getDeviceOwnerUserId()) { + // The user that the DO is installed on is always affiliated with the device. + return true; + } final ComponentName profileOwner = getProfileOwnerAsUser(userId); if (profileOwner == null) { @@ -16317,6 +16335,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } disallowAddUser(); + setAdminCanGrantSensorsPermissionForUserUnchecked(caller.getUserId(), + provisioningParams.canDeviceOwnerGrantSensorsPermissions()); } catch (Exception e) { DevicePolicyEventLogger .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR) @@ -16456,4 +16476,35 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } }); } + + private void setAdminCanGrantSensorsPermissionForUserUnchecked(int userId, boolean canGrant) { + synchronized (getLockObject()) { + ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId); + + Preconditions.checkState( + isDeviceOwner(owner) && owner.getUserHandle().getIdentifier() == userId, + "May only be set on a the user of a device owner."); + + owner.mAdminCanGrantSensorsPermissions = canGrant; + mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant); + saveSettingsLocked(userId); + } + } + + private void updateAdminCanGrantSensorsPermissionCache(int userId) { + synchronized (getLockObject()) { + ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId); + final boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false; + mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant); + } + } + + @Override + public boolean canAdminGrantSensorsPermissionsForUser(int userId) { + if (!mHasFeature) { + return false; + } + + return mPolicyCache.canAdminGrantSensorsPermissionsForUser(userId); + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java index fc1d83158801..222c987d906f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java @@ -18,13 +18,17 @@ package com.android.server.devicepolicy; import android.app.admin.DevicePolicyManager; import android.os.ShellCommand; +import com.android.server.devicepolicy.Owners.OwnerDto; + import java.io.PrintWriter; +import java.util.List; import java.util.Objects; final class DevicePolicyManagerServiceShellCommand extends ShellCommand { private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe"; private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe"; + private static final String CMD_LIST_OWNERS = "list-owners"; private final DevicePolicyManagerService mService; @@ -51,6 +55,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { return runIsSafeOperation(pw); case CMD_SET_SAFE_OPERATION: return runSetSafeOperation(pw); + case CMD_LIST_OWNERS: + return runListOwners(pw); default: return onInvalidCommand(pw, cmd); } @@ -76,6 +82,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { pw.printf(" %s <OPERATION_ID> <REASON_ID>\n", CMD_SET_SAFE_OPERATION); pw.printf(" Emulates the result of the next call to check if the given operation is safe" + " \n\n"); + pw.printf(" %s\n", CMD_LIST_OWNERS); + pw.printf(" Lists the device / profile owners per user \n\n"); } private int runIsSafeOperation(PrintWriter pw) { @@ -97,4 +105,36 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { DevicePolicyManager.unsafeOperationReasonToString(reason)); return 0; } + + private int runListOwners(PrintWriter pw) { + List<OwnerDto> owners = mService.listAllOwners(); + if (owners.isEmpty()) { + pw.println("none"); + return 0; + } + int size = owners.size(); + if (size == 1) { + pw.println("1 owner:"); + } else { + pw.printf("%d owners:\n", size); + } + + for (int i = 0; i < size; i++) { + OwnerDto owner = owners.get(i); + pw.printf("User %2d: admin=%s", owner.userId, owner.admin.flattenToShortString()); + if (owner.isDeviceOwner) { + pw.print(",DeviceOwner"); + } + if (owner.isProfileOwner) { + pw.print(",ProfileOwner"); + } + if (owner.isAffiliated) { + pw.print(",Affiliated"); + } + pw.println(); + } + + return 0; + } + } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 809afe01da2d..1e70d59a5fd5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -17,6 +17,7 @@ package com.android.server.devicepolicy; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.AppOpsManagerInternal; import android.app.admin.SystemUpdateInfo; @@ -57,6 +58,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.time.LocalDate; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -433,6 +435,23 @@ class Owners { } } + List<OwnerDto> listAllOwners() { + List<OwnerDto> owners = new ArrayList<>(); + synchronized (mLock) { + if (mDeviceOwner != null) { + owners.add(new OwnerDto(mDeviceOwnerUserId, mDeviceOwner.admin, + /* isDeviceOwner= */ true)); + } + for (int i = 0; i < mProfileOwners.size(); i++) { + int userId = mProfileOwners.keyAt(i); + OwnerInfo info = mProfileOwners.valueAt(i); + owners.add(new OwnerDto(userId, info.admin, /* isDeviceOwner= */ false)); + } + } + return owners; + } + + SystemUpdatePolicy getSystemUpdatePolicy() { synchronized (mLock) { return mSystemUpdatePolicy; @@ -1076,6 +1095,24 @@ class Owners { } } + /** + * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}. + */ + static final class OwnerDto { + public final @UserIdInt int userId; + public final ComponentName admin; + public final boolean isDeviceOwner; + public final boolean isProfileOwner; + public boolean isAffiliated; + + private OwnerDto(@UserIdInt int userId, ComponentName admin, boolean isDeviceOwner) { + this.userId = userId; + this.admin = Objects.requireNonNull(admin, "admin must not be null"); + this.isDeviceOwner = isDeviceOwner; + this.isProfileOwner = !isDeviceOwner; + } + } + public void dump(IndentingPrintWriter pw) { boolean needBlank = false; if (mDeviceOwner != null) { diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp index e978ed4000e0..7534c7c40a3d 100644 --- a/services/incremental/Android.bp +++ b/services/incremental/Android.bp @@ -51,9 +51,9 @@ cc_defaults { static_libs: [ "libbase", "libext2_uuid", - "libdataloader_aidl-unstable-cpp", - "libincremental_aidl-unstable-cpp", - "libincremental_manager_aidl-unstable-cpp", + "libdataloader_aidl-cpp", + "libincremental_aidl-cpp", + "libincremental_manager_aidl-cpp", "libprotobuf-cpp-lite", "service.incremental.proto", "libutils", diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 6daa381f526e..7a0cb8e5dead 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -59,9 +59,9 @@ android_test { }, libs: [ - "android.hardware.power-java", + "android.hardware.power-V1-java", "android.hardware.tv.cec-V1.0-java", - "android.hardware.vibrator-java", + "android.hardware.vibrator-V1-java", "android.hidl.manager-V1.0-java", "android.test.mock", "android.test.base", @@ -88,7 +88,7 @@ android_test { "libui", "libunwindstack", "libutils", - "netd_aidl_interface-cpp", + "netd_aidl_interface-V5-cpp", ], dxflags: ["--multi-dex"], diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index 54da6436ad89..1a2266139405 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -19,10 +19,14 @@ package com.android.server.devicestate; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertThrows; +import android.hardware.devicestate.DeviceStateRequest; import android.hardware.devicestate.IDeviceStateManagerCallback; +import android.os.Binder; +import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; @@ -33,6 +37,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.HashMap; import java.util.Optional; import javax.annotation.Nullable; @@ -61,87 +66,69 @@ public final class DeviceStateManagerServiceTest { } @Test - public void requestStateChange() { + public void baseStateChanged() { assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); - mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier()); + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); } @Test - public void requestStateChange_pendingState() { + public void baseStateChanged_withStatePendingPolicyCallback() { mPolicy.blockConfigure(); - mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier()); + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); - mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier()); + mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier()); assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); mPolicy.resumeConfigure(); assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); } @Test - public void requestStateChange_unsupportedState() { - mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE.getIdentifier()); + public void baseStateChanged_unsupportedState() { + assertThrows(IllegalArgumentException.class, () -> { + mProvider.setState(UNSUPPORTED_DEVICE_STATE.getIdentifier()); + }); + assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); } @Test - public void requestStateChange_invalidState() { + public void baseStateChanged_invalidState() { assertThrows(IllegalArgumentException.class, () -> { - mProvider.notifyRequestState(INVALID_DEVICE_STATE); + mProvider.setState(INVALID_DEVICE_STATE); }); - } - - @Test - public void requestOverrideState() { - mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier()); - // Committed state changes as there is a requested override. - assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); - assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), - OTHER_DEVICE_STATE.getIdentifier()); - - // Committed state is set back to the requested state once the override is cleared. - mService.clearOverrideState(); - assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); - assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), - DEFAULT_DEVICE_STATE.getIdentifier()); - } - @Test - public void requestOverrideState_unsupportedState() { - mService.setOverrideState(UNSUPPORTED_DEVICE_STATE.getIdentifier()); - // Committed state remains the same as the override state is unsupported. assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getPendingState(), Optional.empty()); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); } @@ -150,7 +137,7 @@ public final class DeviceStateManagerServiceTest { public void supportedStatesChanged() { assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE }); @@ -158,46 +145,27 @@ public final class DeviceStateManagerServiceTest { // supported. assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); } @Test - public void supportedStatesChanged_unsupportedRequestedState() { + public void supportedStatesChanged_unsupportedBaseState() { assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE }); // The current requested state is cleared because it is no longer supported. assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState(), Optional.empty()); + assertEquals(mService.getBaseState(), Optional.empty()); - mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier()); + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE); - } - - @Test - public void supportedStatesChanged_unsupportedOverrideState() { - mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier()); - // Committed state changes as there is a requested override. - assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); - assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), - OTHER_DEVICE_STATE.getIdentifier()); - - mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE }); - - // Committed state is set back to the requested state as the override state is no longer - // supported. - assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); - assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE); - assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), - DEFAULT_DEVICE_STATE.getIdentifier()); + assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); } @Test @@ -205,17 +173,17 @@ public final class DeviceStateManagerServiceTest { TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); mService.getBinderService().registerCallback(callback); - mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier()); + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); assertNotNull(callback.getLastNotifiedValue()); assertEquals(callback.getLastNotifiedValue().intValue(), OTHER_DEVICE_STATE.getIdentifier()); - mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier()); + mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier()); assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE.getIdentifier()); mPolicy.blockConfigure(); - mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier()); + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); // The callback should not have been notified of the state change as the policy is still // pending callback. assertEquals(callback.getLastNotifiedValue().intValue(), @@ -237,6 +205,148 @@ public final class DeviceStateManagerServiceTest { DEFAULT_DEVICE_STATE.getIdentifier()); } + @Test + public void getSupportedDeviceStates() throws RemoteException { + final int[] expectedStates = new int[] { 0, 1 }; + assertEquals(mService.getBinderService().getSupportedDeviceStates(), expectedStates); + } + + @Test + public void requestState() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + final IBinder token = new Binder(); + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_UNKNOWN); + + mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(), + 0 /* flags */); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_ACTIVE); + // Committed state changes as there is a requested override. + assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + OTHER_DEVICE_STATE.getIdentifier()); + + + mService.getBinderService().cancelRequest(token); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_CANCELED); + // Committed state is set back to the requested state once the override is cleared. + assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertFalse(mService.getOverrideState().isPresent()); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + DEFAULT_DEVICE_STATE.getIdentifier()); + } + + @Test + public void requestState_flagCancelWhenBaseChanges() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + final IBinder token = new Binder(); + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_UNKNOWN); + + mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(), + DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_ACTIVE); + + // Committed state changes as there is a requested override. + assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + OTHER_DEVICE_STATE.getIdentifier()); + + mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); + + // Request is canceled because the base state changed. + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_CANCELED); + // Committed state is set back to the requested state once the override is cleared. + assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); + assertFalse(mService.getOverrideState().isPresent()); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + OTHER_DEVICE_STATE.getIdentifier()); + } + + @Test + public void requestState_becomesUnsupported() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + final IBinder token = new Binder(); + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_UNKNOWN); + + mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(), + 0 /* flags */); + + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_ACTIVE); + // Committed state changes as there is a requested override. + assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + OTHER_DEVICE_STATE.getIdentifier()); + + mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE }); + + // Request is canceled because the state is no longer supported. + assertEquals(callback.getLastNotifiedStatus(token), + TestDeviceStateManagerCallback.STATUS_CANCELED); + // Committed state is set back to the requested state as the override state is no longer + // supported. + assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertFalse(mService.getOverrideState().isPresent()); + assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), + DEFAULT_DEVICE_STATE.getIdentifier()); + } + + @Test + public void requestState_unsupportedState() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + assertThrows(IllegalArgumentException.class, () -> { + final IBinder token = new Binder(); + mService.getBinderService().requestState(token, + UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */); + }); + } + + @Test + public void requestState_invalidState() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + + assertThrows(IllegalArgumentException.class, () -> { + final IBinder token = new Binder(); + mService.getBinderService().requestState(token, INVALID_DEVICE_STATE, 0 /* flags */); + }); + } + + @Test + public void requestState_beforeRegisteringCallback() { + assertThrows(IllegalStateException.class, () -> { + final IBinder token = new Binder(); + mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(), + 0 /* flags */); + }); + } + private static final class TestDeviceStatePolicy implements DeviceStatePolicy { private final DeviceStateProvider mProvider; private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE; @@ -306,23 +416,48 @@ public final class DeviceStateManagerServiceTest { mListener.onSupportedDeviceStatesChanged(supportedDeviceStates); } - public void notifyRequestState(int identifier) { + public void setState(int identifier) { mListener.onStateChanged(identifier); } } private static final class TestDeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub { - Integer mLastNotifiedValue; + public static final int STATUS_UNKNOWN = 0; + public static final int STATUS_ACTIVE = 1; + public static final int STATUS_SUSPENDED = 2; + public static final int STATUS_CANCELED = 3; + + private Integer mLastNotifiedValue; + private final HashMap<IBinder, Integer> mLastNotifiedStatus = new HashMap<>(); @Override public void onDeviceStateChanged(int deviceState) { mLastNotifiedValue = deviceState; } + @Override + public void onRequestActive(IBinder token) { + mLastNotifiedStatus.put(token, STATUS_ACTIVE); + } + + @Override + public void onRequestSuspended(IBinder token) { + mLastNotifiedStatus.put(token, STATUS_SUSPENDED); + } + + @Override + public void onRequestCanceled(IBinder token) { + mLastNotifiedStatus.put(token, STATUS_CANCELED); + } + @Nullable Integer getLastNotifiedValue() { return mLastNotifiedValue; } + + int getLastNotifiedStatus(IBinder requestToken) { + return mLastNotifiedStatus.getOrDefault(requestToken, STATUS_UNKNOWN); + } } } diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java index 9ba096766be2..7b9a00d582be 100644 --- a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java @@ -71,12 +71,12 @@ public class IncrementalStatesTest { @Before public void setUp() { mIncrementalStates = new IncrementalStates(); - assertFalse(mIncrementalStates.isStartable()); + assertFalse(mIncrementalStates.getIncrementalStatesInfo().isStartable()); mIncrementalStates.setCallback(mCallback); mIncrementalStates.onCommit(true); // Test that package is now startable and loading - assertTrue(mIncrementalStates.isStartable()); - assertTrue(mIncrementalStates.isLoading()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isLoading()); mUnstartableCalled.close(); mFullyLoadedCalled.close(); } @@ -90,7 +90,7 @@ public class IncrementalStatesTest { IStorageHealthListener.HEALTH_STATUS_UNHEALTHY); // Test that package is still startable assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get()); } @@ -104,7 +104,7 @@ public class IncrementalStatesTest { IStorageHealthListener.HEALTH_STATUS_READS_PENDING); // Test that package is still startable assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); } /** @@ -116,7 +116,7 @@ public class IncrementalStatesTest { IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE); // Test that package is still startable assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get()); } @@ -130,7 +130,7 @@ public class IncrementalStatesTest { IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT); // Test that package is still startable assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get()); } @@ -145,12 +145,12 @@ public class IncrementalStatesTest { mIncrementalStates.setProgress(1.0f); // Test that package is now fully loaded assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS)); - assertFalse(mIncrementalStates.isLoading()); + assertFalse(mIncrementalStates.getIncrementalStatesInfo().isLoading()); mIncrementalStates.onStorageHealthStatusChanged( IStorageHealthListener.HEALTH_STATUS_UNHEALTHY); // Test that package is still startable assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); } /** @@ -159,6 +159,6 @@ public class IncrementalStatesTest { @Test public void testStartableTransition_AppCrashOrAnr() { mIncrementalStates.onCrashOrAnr(); - assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable()); } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java index e6c3d7c3fc5b..b21b04979424 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.array; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertContains; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException; +import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertHaveIds; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertSuccess; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list; @@ -25,6 +26,8 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resultContains; import android.content.ComponentName; +import android.content.pm.LauncherApps; +import android.content.pm.ShortcutManager; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -47,6 +50,9 @@ import java.util.concurrent.atomic.AtomicInteger; */ @SmallTest public class ShortcutManagerTest7 extends BaseShortcutManagerTest { + + private static final int CACHE_OWNER = LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS; + private List<String> callShellCommand(String... args) throws IOException, RemoteException { // For reset to work, the current time needs to be incrementing. @@ -215,11 +221,13 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest { // This command is deprecated. Will remove the test later. public void testLauncherCommands() throws Exception { + prepareGetRoleHoldersAsUser(getSystemLauncher().activityInfo.packageName, USER_0); prepareGetHomeActivitiesAsUser( /* preferred */ getSystemLauncher().activityInfo.getComponentName(), list(getSystemLauncher(), getFallbackLauncher()), USER_0); + prepareGetRoleHoldersAsUser(CALLING_PACKAGE_2, USER_10); prepareGetHomeActivitiesAsUser( /* preferred */ cn(CALLING_PACKAGE_2, "name"), list(getSystemLauncher(), getFallbackLauncher(), @@ -241,6 +249,7 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest { "Launcher: ComponentInfo{com.android.test.2/name}"); // Change user-0's launcher. + prepareGetRoleHoldersAsUser(CALLING_PACKAGE_1, USER_0); prepareGetHomeActivitiesAsUser( /* preferred */ cn(CALLING_PACKAGE_1, "name"), list(ri(CALLING_PACKAGE_1, "name", false, 0)), @@ -323,6 +332,72 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest { }); } + public void testGetShortcuts() throws Exception { + + mRunningUsers.put(USER_10, true); + + // Add two manifests and two dynamics. + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), + R.xml.shortcut_2); + updatePackageVersion(CALLING_PACKAGE_1, 1); + mService.mPackageMonitor.onReceive(getTestContext(), + genPackageAddIntent(CALLING_PACKAGE_1, USER_10)); + + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertTrue(mManager.addDynamicShortcuts(list( + makeLongLivedShortcut("s1"), makeShortcut("s2")))); + }); + runWithCaller(LAUNCHER_1, USER_10, () -> { + mInjectCheckAccessShortcutsPermission = true; + mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_10, + CACHE_OWNER); + mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_10); + }); + + runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { + assertWith(getCallerShortcuts()) + .haveIds("ms1", "ms2", "s1", "s2") + .areAllEnabled() + + .selectPinned() + .haveIds("ms2", "s2"); + }); + + + mRunningUsers.put(USER_10, true); + mUnlockedUsers.put(USER_10, true); + + mInjectedCallingUid = Process.SHELL_UID; + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_CACHED), CALLING_PACKAGE_1), + "s1"); + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_DYNAMIC), CALLING_PACKAGE_1), + "s1", "s2"); + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_MANIFEST), CALLING_PACKAGE_1), + "ms1", "ms2"); + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_PINNED), CALLING_PACKAGE_1), + "ms2", "s2"); + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_DYNAMIC + | ShortcutManager.FLAG_MATCH_PINNED), CALLING_PACKAGE_1), + "ms2", "s1", "s2"); + + assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags", + Integer.toString(ShortcutManager.FLAG_MATCH_MANIFEST + | ShortcutManager.FLAG_MATCH_CACHED), CALLING_PACKAGE_1), + "ms1", "ms2", "s1"); + + } + public void testDumpsysArgs() { checkDumpsysArgs(null, true, false, false); checkDumpsysArgs(array("-u"), true, true, false); diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp index c2cb688175b2..35ca3544d62e 100644 --- a/services/tests/shortcutmanagerutils/Android.bp +++ b/services/tests/shortcutmanagerutils/Android.bp @@ -22,5 +22,9 @@ java_library { "android.test.runner.stubs", ], + static_libs: [ + "compatibility-device-util-axt", + ], + sdk_version: "test_current", } diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java index 907f887ec228..5182b3b69655 100644 --- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java +++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java @@ -15,6 +15,8 @@ */ package com.android.server.pm.shortcutmanagertest; +import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -34,6 +36,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.Instrumentation; +import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; import android.content.LocusId; @@ -50,6 +53,7 @@ import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.UserHandle; +import android.os.UserManager; import android.test.MoreAsserts; import android.util.Log; @@ -136,6 +140,20 @@ public class ShortcutManagerTestUtils { return sb.toString(); } + public static List<String> extractShortcutIds(List<String> result) { + final String prefix = "ShortcutInfo {id="; + final String postfix = ", "; + + List<String> ids = new ArrayList<>(); + for (String line : result) { + if (line.contains(prefix)) { + ids.add(line.substring( + line.indexOf(prefix) + prefix.length(), line.indexOf(postfix))); + } + } + return ids; + } + public static boolean resultContains(List<String> result, String expected) { for (String line : result) { if (line.contains(expected)) { @@ -160,6 +178,16 @@ public class ShortcutManagerTestUtils { return result; } + public static List<String> assertHaveIds(List<String> result, String... expectedIds) { + assertSuccess(result); + + final SortedSet<String> expected = new TreeSet<>(list(expectedIds)); + final SortedSet<String> actual = new TreeSet<>(extractShortcutIds(result)); + assertEquals(expected, actual); + + return result; + } + public static List<String> runCommand(Instrumentation instrumentation, String command) { return runCommand(instrumentation, command, null); } @@ -193,30 +221,60 @@ public class ShortcutManagerTestUtils { return runShortcutCommand(instrumentation, command, result -> result.contains("Success")); } - public static String getDefaultLauncher(Instrumentation instrumentation) { - final String PREFIX = "Launcher: ComponentInfo{"; - final String POSTFIX = "}"; - final List<String> result = runShortcutCommandForSuccess( - instrumentation, "get-default-launcher --user " - + instrumentation.getContext().getUserId()); - for (String s : result) { - if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) { - return s.substring(PREFIX.length(), s.length() - POSTFIX.length()); + private static UserHandle getParentUser(Context context) { + final UserHandle user = context.getUser(); + final UserManager userManager = context.getSystemService(UserManager.class); + if (!userManager.isManagedProfile(user.getIdentifier())) { + return user; + } + + final List<UserHandle> profiles = userManager.getUserProfiles(); + for (UserHandle handle : profiles) { + if (!userManager.isManagedProfile(handle.getIdentifier())) { + return handle; } } - fail("Default launcher not found"); return null; } - public static void setDefaultLauncher(Instrumentation instrumentation, String component) { - runCommand(instrumentation, "cmd package set-home-activity --user " - + instrumentation.getContext().getUserId() + " " + component, - result -> result.contains("Success")); + public static String getDefaultLauncher(Instrumentation instrumentation) throws Exception { + final Context context = instrumentation.getContext(); + final RoleManager roleManager = context.getSystemService(RoleManager.class); + final UserHandle user = getParentUser(context); + List<String> roleHolders = callWithShellPermissionIdentity( + () -> roleManager.getRoleHoldersAsUser(RoleManager.ROLE_HOME, user)); + if (roleHolders.size() == 1) { + return roleHolders.get(0); + } + fail("Failed to get the default launcher for user " + context.getUserId()); + return null; + } + + public static void setDefaultLauncher(Instrumentation instrumentation, String packageName) { + runCommandForNoOutput(instrumentation, "cmd role add-role-holder --user " + + instrumentation.getContext().getUserId() + " " + RoleManager.ROLE_HOME + " " + + packageName + " 0"); + waitUntil("Failed to get shortcut access", + () -> hasShortcutAccess(instrumentation, packageName), 20); } public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) { - setDefaultLauncher(instrumentation, packageContext.getPackageName() - + "/android.content.pm.cts.shortcutmanager.packages.Launcher"); + setDefaultLauncher(instrumentation, packageContext.getPackageName()); + } + + public static boolean hasShortcutAccess(Instrumentation instrumentation, String packageName) { + final List<String> result = runShortcutCommandForSuccess(instrumentation, + "has-shortcut-access --user " + instrumentation.getContext().getUserId() + + " " + packageName); + for (String s : result) { + if (s.startsWith("true")) { + return true; + } else if (s.startsWith("false")) { + return false; + } + } + fail("Failed to check shortcut access"); + return false; } public static void overrideConfig(Instrumentation instrumentation, String config) { diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index e5646db7731f..1dd42127ec06 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -60,6 +60,6 @@ android_test { "libui", "libunwindstack", "libutils", - "netd_aidl_interface-cpp", + "netd_aidl_interface-V5-cpp", ], } diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index cf977b4a18db..ddf2844012e0 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -51,7 +51,7 @@ android_test { ], libs: [ - "android.hardware.power-java", + "android.hardware.power-V1-java", "android.test.mock", "android.test.base", "android.test.runner", diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 6b69ee921c8b..002859e3366b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -96,6 +96,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { .setTask(mTrampolineActivity.getTask()) .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TopActivity")) .build(); + // becomes invisible when covered by mTopActivity + mTrampolineActivity.mVisibleRequested = false; } @After @@ -230,7 +232,6 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { @Test public void testOnActivityLaunchCancelled_finishedBeforeDrawn() { - mTopActivity.mVisibleRequested = true; doReturn(true).when(mTopActivity).isReportedDrawn(); // Create an activity with different process that meets process switch. diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 93851109200b..72b84396e985 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -2180,6 +2180,7 @@ public class ActivityRecordTests extends WindowTestsBase { activity.setOccludesParent(true); activity.setVisible(false); + activity.mVisibleRequested = false; // Can not specify orientation if app isn't visible even though it occludes parent. assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation()); // Can specify orientation if the current orientation candidate is orientation behind. diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 22ee886c46fe..3a7954b3a5ce 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -1424,6 +1424,9 @@ public class ActivityStackTests extends WindowTestsBase { final ActivityRecord nonTopVisibleActivity = new ActivityBuilder(mAtm).setTask(task).build(); new ActivityBuilder(mAtm).setTask(task).build(); + // The scenario we are testing is when the app isn't visible yet. + nonTopVisibleActivity.setVisible(false); + nonTopVisibleActivity.mVisibleRequested = false; doReturn(false).when(nonTopVisibleActivity).attachedToProcess(); doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked(); doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(), diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 36adf28b276a..37bc23e6a17d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -844,6 +844,9 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityRecord singleTaskActivity = createSingleTaskActivityOn( secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); + // Activity should start invisible since we are bringing it to front. + singleTaskActivity.setVisible(false); + singleTaskActivity.mVisibleRequested = false; // Create another activity on top of the secondary display. final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN, diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index 2f4c8e256760..d13e4dcaf9fd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -15,7 +15,7 @@ */ package com.android.server.wm; -import static android.os.Process.INVALID_UID; + import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -587,7 +587,7 @@ public class DisplayAreaPolicyBuilderTest { final WindowToken token = new WindowToken(mWms, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); policy.addWindow(token); @@ -621,11 +621,11 @@ public class DisplayAreaPolicyBuilderTest { final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class), TYPE_WALLPAPER, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); policy.addWindow(token1); policy.addWindow(token2); @@ -672,15 +672,15 @@ public class DisplayAreaPolicyBuilderTest { options2.putInt("HIERARCHY_ROOT_ID", mGroupRoot2.mFeatureId); final WindowToken token0 = new WindowToken(mWms, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, options1); final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, false /* fromClientToken */, options2); policy.addWindow(token0); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 4bea9a2eea45..4c2d12436858 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1608,6 +1608,16 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testGetOrCreateRootHomeTask_dontMoveToTop() { + DisplayContent display = createNewDisplay(); + display.mDontMoveToTop = true; + TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); + + assertNull(taskDisplayArea.getRootHomeTask()); + assertNull(taskDisplayArea.getOrCreateRootHomeTask()); + } + + @Test public void testValidWindowingLayer() { final SurfaceControl windowingLayer = mDisplayContent.getWindowingLayer(); assertNotNull(windowingLayer); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java index 33e8fc0bd94b..3e05c86898e2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java @@ -217,6 +217,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); overrideSettings.mShouldShowSystemDecors = true; overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; + overrideSettings.mDontMoveToTop = true; provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings); assertTrue(mOverrideSettingsStorage.wasWriteSuccessful()); @@ -227,6 +228,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { getStoredDisplayAttributeValue(mOverrideSettingsStorage, "shouldShowSystemDecors")); assertEquals("Attribute value must be stored", "0", getStoredDisplayAttributeValue(mOverrideSettingsStorage, "imePolicy")); + assertEquals("Attribute value must be stored", "true", + getStoredDisplayAttributeValue(mOverrideSettingsStorage, "dontMoveToTop")); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index d83e9c21fae7..e22cda60d423 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -156,6 +156,9 @@ public class TaskDisplayAreaTests extends WindowTestsBase { spyOn(mDisplayContent); doReturn(true).when(mDisplayContent).isTrusted(); + // Allow child stack to move to top. + mDisplayContent.mDontMoveToTop = false; + // The display contains pinned stack that was added in {@link #setUp}. final Task stack = createTaskStackOnDisplay(mDisplayContent); final Task task = createTaskInStack(stack, 0 /* userId */); @@ -173,6 +176,65 @@ public class TaskDisplayAreaTests extends WindowTestsBase { } @Test + public void testMovingChildTaskOnTop() { + // Make sure the display is trusted display which capable to move the stack to top. + spyOn(mDisplayContent); + doReturn(true).when(mDisplayContent).isTrusted(); + + // Allow child stack to move to top. + mDisplayContent.mDontMoveToTop = false; + + // The display contains pinned stack that was added in {@link #setUp}. + Task stack = createTaskStackOnDisplay(mDisplayContent); + Task task = createTaskInStack(stack, 0 /* userId */); + + // Add another display at top. + mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(), + false /* includingParents */); + + // Ensure that original display ({@code mDisplayContent}) is not on top. + assertEquals("Testing DisplayContent should not be on the top", + mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent)); + + // Move the task of {@code mDisplayContent} to top. + stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); + + // Ensure that original display ({@code mDisplayContent}) is now on the top. + assertEquals("The testing DisplayContent should be moved to top with task", + mWm.mRoot.getChildCount() - 1, mWm.mRoot.mChildren.indexOf(mDisplayContent)); + } + + @Test + public void testDontMovingChildTaskOnTop() { + // Make sure the display is trusted display which capable to move the stack to top. + spyOn(mDisplayContent); + doReturn(true).when(mDisplayContent).isTrusted(); + + // Allow child stack to move to top. + mDisplayContent.mDontMoveToTop = true; + + // The display contains pinned stack that was added in {@link #setUp}. + Task stack = createTaskStackOnDisplay(mDisplayContent); + Task task = createTaskInStack(stack, 0 /* userId */); + + // Add another display at top. + mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(), + false /* includingParents */); + + // Ensure that original display ({@code mDisplayContent}) is not on top. + assertEquals("Testing DisplayContent should not be on the top", + mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent)); + + // Try moving the task of {@code mDisplayContent} to top. + stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); + + // Ensure that original display ({@code mDisplayContent}) hasn't moved and is not + // on the top. + assertEquals("The testing DisplayContent should not be moved to top with task", + mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent)); + } + + @Test public void testReuseTaskAsRootTask() { final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); @@ -233,7 +295,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { homeActivity.mVisibleRequested = true; assertFalse(rootHomeTask.isVisible()); - assertEquals(rootWindowContainer.getOrientation(), rootHomeTask.getOrientation()); + assertEquals(defaultTaskDisplayArea.getOrientation(), rootHomeTask.getOrientation()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index f8a89c6f89aa..6d0e510ba626 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -20,7 +20,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.os.Process.INVALID_UID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; @@ -37,12 +36,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.content.pm.PackageManager; import android.os.IBinder; @@ -75,7 +72,7 @@ public class WindowManagerServiceTests extends WindowTestsBase { @Test public void testAddWindowToken() { IBinder token = mock(IBinder.class); - mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId()); + mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId(), null /* options */); WindowToken windowToken = mWm.mRoot.getWindowToken(token); assertFalse(windowToken.mRoundedCornerOverlay); @@ -83,32 +80,6 @@ public class WindowManagerServiceTests extends WindowTestsBase { } @Test - public void testAddWindowTokenWithOptions() { - IBinder token = mock(IBinder.class); - mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(), - null /* options */, null /* options */); - - WindowToken windowToken = mWm.mRoot.getWindowToken(token); - assertFalse(windowToken.mRoundedCornerOverlay); - assertTrue(windowToken.isFromClient()); - } - - @Test(expected = SecurityException.class) - public void testRemoveWindowToken_ownerUidNotMatch_throwException() { - IBinder token = mock(IBinder.class); - mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(), - null /* options */, null /* options */); - - spyOn(mWm); - when(mWm.checkCallingPermission(anyString(), anyString())).thenReturn(false); - WindowToken windowToken = mWm.mRoot.getWindowToken(token); - spyOn(windowToken); - when(windowToken.getOwnerUid()).thenReturn(INVALID_UID); - - mWm.removeWindowToken(token, mDisplayContent.getDisplayId()); - } - - @Test public void testTaskFocusChange_stackNotHomeType_focusChanges() throws RemoteException { DisplayContent display = createNewDisplay(); // Current focused window diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 3492d90cc0f3..eb6c6ed349de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -890,6 +890,7 @@ class WindowTestsBase extends SystemServiceTestsBase { mTask.moveToFront("createActivity"); } // Make visible by default... + activity.mVisibleRequested = true; activity.setVisible(true); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 2d273312ab81..e2585e5ebfba 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; @@ -201,7 +200,7 @@ public class WindowTokenTests extends WindowTestsBase { token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */, - INVALID_UID, true /* roundedCornerOverlay */, true /* fromClientToken */); + true /* roundedCornerOverlay */, true /* fromClientToken */, null /* options */); assertTrue(token.mRoundedCornerOverlay); assertTrue(token.isFromClient()); } @@ -215,8 +214,8 @@ public class WindowTokenTests extends WindowTestsBase { public void testSurfaceCreatedForWindowToken() { final WindowToken fromClientToken = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_APPLICATION_OVERLAY, true /* persistOnEmpty */, - mDisplayContent, true /* ownerCanManageAppTokens */, INVALID_UID, - true /* roundedCornerOverlay */, true /* fromClientToken */); + mDisplayContent, true /* ownerCanManageAppTokens */, + true /* roundedCornerOverlay */, true /* fromClientToken */, null /* options */); assertNull(fromClientToken.mSurfaceControl); createWindow(null, TYPE_APPLICATION_OVERLAY, fromClientToken, "window"); @@ -224,8 +223,8 @@ public class WindowTokenTests extends WindowTestsBase { final WindowToken nonClientToken = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, - false /* fromClientToken */); + true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */, + false /* fromClientToken */, null /* options */); assertNotNull(nonClientToken.mSurfaceControl); } @@ -238,7 +237,7 @@ public class WindowTokenTests extends WindowTestsBase { final WindowToken token1 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */); verify(selectFunc).apply(token1.windowType, null); @@ -246,7 +245,7 @@ public class WindowTokenTests extends WindowTestsBase { final Bundle options = new Bundle(); final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */, false /* fromClientToken */, options /* options */); verify(selectFunc).apply(token2.windowType, options); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java index cfdc5682a117..84f4f6a017d1 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java @@ -165,7 +165,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection, | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser)); if (mBound) { try { - mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY); + mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY, + null /* options */); } catch (RemoteException e) { Slog.w(TAG, "Failed adding window token", e); } diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index f48ddb0aaca6..bd4bf0740ca1 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -136,7 +136,7 @@ public final class DataCallResponse implements Parcelable { private final @HandoverFailureMode int mHandoverFailureMode; private final int mPduSessionId; private final Qos mDefaultQos; - private final List<QosSession> mQosSessions; + private final List<QosBearerSession> mQosBearerSessions; private final SliceInfo mSliceInfo; /** @@ -187,7 +187,7 @@ public final class DataCallResponse implements Parcelable { mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY; mPduSessionId = PDU_SESSION_ID_NOT_SET; mDefaultQos = null; - mQosSessions = new ArrayList<>(); + mQosBearerSessions = new ArrayList<>(); mSliceInfo = null; } @@ -197,7 +197,7 @@ public final class DataCallResponse implements Parcelable { @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses, @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6, @HandoverFailureMode int handoverFailureMode, int pduSessionId, - @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions, + @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions, @Nullable SliceInfo sliceInfo) { mCause = cause; mSuggestedRetryTime = suggestedRetryTime; @@ -219,7 +219,7 @@ public final class DataCallResponse implements Parcelable { mHandoverFailureMode = handoverFailureMode; mPduSessionId = pduSessionId; mDefaultQos = defaultQos; - mQosSessions = qosSessions; + mQosBearerSessions = qosBearerSessions; mSliceInfo = sliceInfo; } @@ -246,8 +246,8 @@ public final class DataCallResponse implements Parcelable { mHandoverFailureMode = source.readInt(); mPduSessionId = source.readInt(); mDefaultQos = source.readParcelable(Qos.class.getClassLoader()); - mQosSessions = new ArrayList<>(); - source.readList(mQosSessions, QosSession.class.getClassLoader()); + mQosBearerSessions = new ArrayList<>(); + source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader()); mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader()); } @@ -394,8 +394,8 @@ public final class DataCallResponse implements Parcelable { * @hide */ @NonNull - public List<QosSession> getQosSessions() { - return mQosSessions; + public List<QosBearerSession> getQosBearerSessions() { + return mQosBearerSessions; } /** @@ -427,7 +427,7 @@ public final class DataCallResponse implements Parcelable { .append(" handoverFailureMode=").append(failureModeToString(mHandoverFailureMode)) .append(" pduSessionId=").append(getPduSessionId()) .append(" defaultQos=").append(mDefaultQos) - .append(" qosSessions=").append(mQosSessions) + .append(" qosBearerSessions=").append(mQosBearerSessions) .append(" sliceInfo=").append(mSliceInfo) .append("}"); return sb.toString(); @@ -447,10 +447,10 @@ public final class DataCallResponse implements Parcelable { mDefaultQos == other.mDefaultQos : mDefaultQos.equals(other.mDefaultQos); - final boolean isQosSessionsSame = (mQosSessions == null || mQosSessions == null) ? - mQosSessions == other.mQosSessions : - mQosSessions.size() == other.mQosSessions.size() - && mQosSessions.containsAll(other.mQosSessions); + final boolean isQosBearerSessionsSame = (mQosBearerSessions == null || mQosBearerSessions == null) ? + mQosBearerSessions == other.mQosBearerSessions : + mQosBearerSessions.size() == other.mQosBearerSessions.size() + && mQosBearerSessions.containsAll(other.mQosBearerSessions); return mCause == other.mCause && mSuggestedRetryTime == other.mSuggestedRetryTime @@ -472,7 +472,7 @@ public final class DataCallResponse implements Parcelable { && mHandoverFailureMode == other.mHandoverFailureMode && mPduSessionId == other.mPduSessionId && isQosSame - && isQosSessionsSame + && isQosBearerSessionsSame && Objects.equals(mSliceInfo, other.mSliceInfo); } @@ -481,7 +481,7 @@ public final class DataCallResponse implements Parcelable { return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos, - mQosSessions, mSliceInfo); + mQosBearerSessions, mSliceInfo); } @Override @@ -515,7 +515,7 @@ public final class DataCallResponse implements Parcelable { } else { dest.writeParcelable(null, flags); } - dest.writeList(mQosSessions); + dest.writeList(mQosBearerSessions); dest.writeParcelable(mSliceInfo, flags); } @@ -598,7 +598,7 @@ public final class DataCallResponse implements Parcelable { private Qos mDefaultQos; - private List<QosSession> mQosSessions = new ArrayList<>(); + private List<QosBearerSession> mQosBearerSessions = new ArrayList<>(); private SliceInfo mSliceInfo; @@ -812,15 +812,16 @@ public final class DataCallResponse implements Parcelable { /** * Set the dedicated bearer QOS sessions for this data connection. * - * @param qosSessions Dedicated bearer QOS (Quality Of Service) sessions received + * @param qosBearerSessions Dedicated bearer QOS (Quality Of Service) sessions received * from network. * * @return The same instance of the builder. * * @hide */ - public @NonNull Builder setQosSessions(@NonNull List<QosSession> qosSessions) { - mQosSessions = qosSessions; + public @NonNull Builder setQosBearerSessions( + @NonNull List<QosBearerSession> qosBearerSessions) { + mQosBearerSessions = qosBearerSessions; return this; } @@ -848,7 +849,7 @@ public final class DataCallResponse implements Parcelable { return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, - mDefaultQos, mQosSessions, mSliceInfo); + mDefaultQos, mQosBearerSessions, mSliceInfo); } } } diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java index ad43068b2f11..22c8b0a0a74f 100644 --- a/telephony/java/android/telephony/data/EpsQos.java +++ b/telephony/java/android/telephony/data/EpsQos.java @@ -48,6 +48,10 @@ public final class EpsQos extends Qos implements Parcelable { qosClassId = source.readInt(); } + public int getQci() { + return qosClassId; + } + public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) { return new EpsQos(in); } diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java index c8bb91e28bf2..c286c6217450 100644 --- a/telephony/java/android/telephony/data/Qos.java +++ b/telephony/java/android/telephony/data/Qos.java @@ -56,7 +56,15 @@ public abstract class Qos { this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps); } - static class QosBandwidth implements Parcelable { + public QosBandwidth getDownlinkBandwidth() { + return downlink; + } + + public QosBandwidth getUplinkBandwidth() { + return uplink; + } + + public static class QosBandwidth implements Parcelable { int maxBitrateKbps; int guaranteedBitrateKbps; @@ -73,6 +81,14 @@ public abstract class Qos { guaranteedBitrateKbps = source.readInt(); } + public int getMaxBitrateKbps() { + return maxBitrateKbps; + } + + public int getGuaranteedBitrateKbps() { + return guaranteedBitrateKbps; + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(maxBitrateKbps); diff --git a/telephony/java/android/telephony/data/QosFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java index 69277445634d..6c1c653f13d0 100644 --- a/telephony/java/android/telephony/data/QosFilter.java +++ b/telephony/java/android/telephony/data/QosBearerFilter.java @@ -38,7 +38,7 @@ import java.util.Objects; * * @hide */ -public final class QosFilter implements Parcelable { +public final class QosBearerFilter implements Parcelable { private List<LinkAddress> localAddresses; private List<LinkAddress> remoteAddresses; @@ -74,7 +74,7 @@ public final class QosFilter implements Parcelable { @IntDef(prefix = "QOS_FILTER_DIRECTION_", value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK, QOS_FILTER_DIRECTION_BIDIRECTIONAL}) - public @interface QosFilterDirection {} + public @interface QosBearerFilterDirection {} public static final int QOS_FILTER_DIRECTION_DOWNLINK = android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK; @@ -83,7 +83,7 @@ public final class QosFilter implements Parcelable { public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL = android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL; - @QosFilterDirection + @QosBearerFilterDirection private int filterDirection; /** @@ -92,7 +92,7 @@ public final class QosFilter implements Parcelable { */ private int precedence; - QosFilter() { + QosBearerFilter() { localAddresses = new ArrayList<>(); remoteAddresses = new ArrayList<>(); localPort = new PortRange(); @@ -101,7 +101,7 @@ public final class QosFilter implements Parcelable { filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL; } - public QosFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses, + public QosBearerFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses, PortRange localPort, PortRange remotePort, int protocol, int tos, long flowLabel, long spi, int direction, int precedence) { this.localAddresses = localAddresses; @@ -116,10 +116,30 @@ public final class QosFilter implements Parcelable { this.precedence = precedence; } + public List<LinkAddress> getLocalAddresses() { + return localAddresses; + } + + public List<LinkAddress> getRemoteAddresses() { + return remoteAddresses; + } + + public PortRange getLocalPortRange() { + return localPort; + } + + public PortRange getRemotePortRange() { + return remotePort; + } + + public int getPrecedence() { + return precedence; + } + /** @hide */ - public static @NonNull QosFilter create( + public static @NonNull QosBearerFilter create( @NonNull android.hardware.radio.V1_6.QosFilter qosFilter) { - QosFilter ret = new QosFilter(); + QosBearerFilter ret = new QosBearerFilter(); String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new); if (localAddresses != null) { @@ -202,6 +222,14 @@ public final class QosFilter implements Parcelable { this.end = end; } + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } + @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(start); @@ -254,7 +282,7 @@ public final class QosFilter implements Parcelable { @Override public String toString() { - return "QosFilter {" + return "QosBearerFilter {" + " localAddresses=" + localAddresses + " remoteAddresses=" + remoteAddresses + " localPort=" + localPort @@ -278,11 +306,11 @@ public final class QosFilter implements Parcelable { public boolean equals(Object o) { if (this == o) return true; - if (o == null || !(o instanceof QosFilter)) { + if (o == null || !(o instanceof QosBearerFilter)) { return false; } - QosFilter other = (QosFilter) o; + QosBearerFilter other = (QosBearerFilter) o; return localAddresses.size() == other.localAddresses.size() && localAddresses.containsAll(other.localAddresses) @@ -324,7 +352,7 @@ public final class QosFilter implements Parcelable { LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN); } - private QosFilter(Parcel source) { + private QosBearerFilter(Parcel source) { localAddresses = new ArrayList<>(); source.readList(localAddresses, LinkAddress.class.getClassLoader()); remoteAddresses = new ArrayList<>(); @@ -358,16 +386,16 @@ public final class QosFilter implements Parcelable { return 0; } - public static final @NonNull Parcelable.Creator<QosFilter> CREATOR = - new Parcelable.Creator<QosFilter>() { + public static final @NonNull Parcelable.Creator<QosBearerFilter> CREATOR = + new Parcelable.Creator<QosBearerFilter>() { @Override - public QosFilter createFromParcel(Parcel source) { - return new QosFilter(source); + public QosBearerFilter createFromParcel(Parcel source) { + return new QosBearerFilter(source); } @Override - public QosFilter[] newArray(int size) { - return new QosFilter[size]; + public QosBearerFilter[] newArray(int size) { + return new QosBearerFilter[size]; } }; } diff --git a/telephony/java/android/telephony/data/QosBearerSession.java b/telephony/java/android/telephony/data/QosBearerSession.java new file mode 100644 index 000000000000..30effc9273d7 --- /dev/null +++ b/telephony/java/android/telephony/data/QosBearerSession.java @@ -0,0 +1,137 @@ +/** + * 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.telephony.data; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + + +/** + * Class that stores information specific to QOS session. + * + * @hide + */ +public final class QosBearerSession implements Parcelable{ + + final int qosBearerSessionId; + final Qos qos; + final List<QosBearerFilter> qosBearerFilterList; + + public QosBearerSession(int qosBearerSessionId, @NonNull Qos qos, @NonNull List<QosBearerFilter> qosBearerFilterList) { + this.qosBearerSessionId = qosBearerSessionId; + this.qos = qos; + this.qosBearerFilterList = qosBearerFilterList; + } + + private QosBearerSession(Parcel source) { + qosBearerSessionId = source.readInt(); + qos = source.readParcelable(Qos.class.getClassLoader()); + qosBearerFilterList = new ArrayList<>(); + source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader()); + } + + public int getQosBearerSessionId() { + return qosBearerSessionId; + } + + public Qos getQos() { + return qos; + } + + public List<QosBearerFilter> getQosBearerFilterList() { + return qosBearerFilterList; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(qosBearerSessionId); + if (qos.getType() == Qos.QOS_TYPE_EPS) { + dest.writeParcelable((EpsQos)qos, flags); + } else { + dest.writeParcelable((NrQos)qos, flags); + } + dest.writeList(qosBearerFilterList); + } + + public static @NonNull QosBearerSession create( + @NonNull android.hardware.radio.V1_6.QosSession qosSession) { + List<QosBearerFilter> qosBearerFilters = new ArrayList<>(); + + if (qosSession.qosFilters != null) { + for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) { + qosBearerFilters.add(QosBearerFilter.create(filter)); + } + } + + return new QosBearerSession( + qosSession.qosSessionId, + Qos.create(qosSession.qos), + qosBearerFilters); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "QosBearerSession {" + + " qosBearerSessionId=" + qosBearerSessionId + + " qos=" + qos + + " qosBearerFilterList=" + qosBearerFilterList + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(qosBearerSessionId, qos, qosBearerFilterList); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof QosBearerSession)) { + return false; + } + + QosBearerSession other = (QosBearerSession) o; + return this.qosBearerSessionId == other.qosBearerSessionId + && this.qos.equals(other.qos) + && this.qosBearerFilterList.size() == other.qosBearerFilterList.size() + && this.qosBearerFilterList.containsAll(other.qosBearerFilterList); + } + + + public static final @NonNull Parcelable.Creator<QosBearerSession> CREATOR = + new Parcelable.Creator<QosBearerSession>() { + @Override + public QosBearerSession createFromParcel(Parcel source) { + return new QosBearerSession(source); + } + + @Override + public QosBearerSession[] newArray(int size) { + return new QosBearerSession[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/QosSession.java b/telephony/java/android/telephony/data/QosSession.java deleted file mode 100644 index f07b6a9f6725..000000000000 --- a/telephony/java/android/telephony/data/QosSession.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * 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.telephony.data; - -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - - -/** - * Class that stores information specific to QOS session. - * - * @hide - */ -public final class QosSession implements Parcelable{ - - final int qosSessionId; - final Qos qos; - final List<QosFilter> qosFilterList; - - public QosSession(int qosSessionId, @NonNull Qos qos, @NonNull List<QosFilter> qosFilterList) { - this.qosSessionId = qosSessionId; - this.qos = qos; - this.qosFilterList = qosFilterList; - } - - private QosSession(Parcel source) { - qosSessionId = source.readInt(); - qos = source.readParcelable(Qos.class.getClassLoader()); - qosFilterList = new ArrayList<>(); - source.readList(qosFilterList, QosFilter.class.getClassLoader()); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(qosSessionId); - if (qos.getType() == Qos.QOS_TYPE_EPS) { - dest.writeParcelable((EpsQos)qos, flags); - } else { - dest.writeParcelable((NrQos)qos, flags); - } - dest.writeList(qosFilterList); - } - - public static @NonNull QosSession create( - @NonNull android.hardware.radio.V1_6.QosSession qosSession) { - List<QosFilter> qosFilters = new ArrayList<>(); - - if (qosSession.qosFilters != null) { - for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) { - qosFilters.add(QosFilter.create(filter)); - } - } - - return new QosSession( - qosSession.qosSessionId, - Qos.create(qosSession.qos), - qosFilters); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public String toString() { - return "QosSession {" - + " qosSessionId=" + qosSessionId - + " qos=" + qos - + " qosFilterList=" + qosFilterList + "}"; - } - - @Override - public int hashCode() { - return Objects.hash(qosSessionId, qos, qosFilterList); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || !(o instanceof QosSession)) { - return false; - } - - QosSession other = (QosSession) o; - return this.qosSessionId == other.qosSessionId - && this.qos.equals(other.qos) - && this.qosFilterList.size() == other.qosFilterList.size() - && this.qosFilterList.containsAll(other.qosFilterList); - } - - - public static final @NonNull Parcelable.Creator<QosSession> CREATOR = - new Parcelable.Creator<QosSession>() { - @Override - public QosSession createFromParcel(Parcel source) { - return new QosSession(source); - } - - @Override - public QosSession[] newArray(int size) { - return new QosSession[size]; - } - }; -} diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java index cade5ba3771f..d232a507454d 100644 --- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java +++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -20,22 +20,20 @@ import static com.android.testutils.MiscAsserts.assertThrows; import static com.android.testutils.ParcelUtils.assertParcelSane; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Build; -import android.util.SparseArray; import androidx.test.filters.SmallTest; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; import com.android.testutils.DevSdkIgnoreRunner; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.ArrayList; -import java.util.List; +import java.util.Map; @IgnoreUpTo(Build.VERSION_CODES.R) @RunWith(DevSdkIgnoreRunner.class) @@ -45,51 +43,51 @@ public class OemNetworkPreferencesTest { private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_DEFAULT; private static final String TEST_PACKAGE = "com.google.apps.contacts"; - private final List<String> mPackages = new ArrayList<>(); private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder(); - @Before - public void beforeEachTestMethod() { - mPackages.add(TEST_PACKAGE); + @Test + public void testBuilderAddNetworkPreferenceRequiresNonNullPackageName() { + assertThrows(NullPointerException.class, + () -> mBuilder.addNetworkPreference(null, TEST_PREF)); } @Test - public void builderAddNetworkPreferenceRequiresNonNullPackages() { + public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() { assertThrows(NullPointerException.class, - () -> mBuilder.addNetworkPreference(TEST_PREF, null)); + () -> mBuilder.removeNetworkPreference(null)); } @Test - public void getNetworkPreferencesReturnsCorrectValue() { + public void testGetNetworkPreferenceReturnsCorrectValue() { final int expectedNumberOfMappings = 1; - mBuilder.addNetworkPreference(TEST_PREF, mPackages); + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - final SparseArray<List<String>> networkPreferences = + final Map<String, Integer> networkPreferences = mBuilder.build().getNetworkPreferences(); assertEquals(expectedNumberOfMappings, networkPreferences.size()); - assertEquals(mPackages.size(), networkPreferences.get(TEST_PREF).size()); - assertEquals(mPackages.get(0), networkPreferences.get(TEST_PREF).get(0)); + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); } @Test - public void getNetworkPreferencesReturnsUnmodifiableValue() { + public void testGetNetworkPreferenceReturnsUnmodifiableValue() { final String newPackage = "new.com.google.apps.contacts"; - mBuilder.addNetworkPreference(TEST_PREF, mPackages); + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - final SparseArray<List<String>> networkPreferences = + final Map<String, Integer> networkPreferences = mBuilder.build().getNetworkPreferences(); assertThrows(UnsupportedOperationException.class, - () -> networkPreferences.get(TEST_PREF).set(mPackages.size() - 1, newPackage)); + () -> networkPreferences.put(newPackage, TEST_PREF)); assertThrows(UnsupportedOperationException.class, - () -> networkPreferences.get(TEST_PREF).add(newPackage)); + () -> networkPreferences.remove(TEST_PACKAGE)); + } @Test - public void toStringReturnsCorrectValue() { - mBuilder.addNetworkPreference(TEST_PREF, mPackages); + public void testToStringReturnsCorrectValue() { + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); final String networkPreferencesString = mBuilder.build().getNetworkPreferences().toString(); @@ -99,10 +97,56 @@ public class OemNetworkPreferencesTest { @Test public void testOemNetworkPreferencesParcelable() { - mBuilder.addNetworkPreference(TEST_PREF, mPackages); + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); final OemNetworkPreferences prefs = mBuilder.build(); assertParcelSane(prefs, 1 /* fieldCount */); } + + @Test + public void testAddNetworkPreferenceOverwritesPriorPreference() { + final int newPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + Map<String, Integer> networkPreferences = + mBuilder.build().getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF); + + mBuilder.addNetworkPreference(TEST_PACKAGE, newPref); + networkPreferences = mBuilder.build().getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), newPref); + } + + @Test + public void testRemoveNetworkPreferenceRemovesValue() { + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + Map<String, Integer> networkPreferences = + mBuilder.build().getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + + mBuilder.removeNetworkPreference(TEST_PACKAGE); + networkPreferences = mBuilder.build().getNetworkPreferences(); + + assertFalse(networkPreferences.containsKey(TEST_PACKAGE)); + } + + @Test + public void testConstructorByOemNetworkPreferencesSetsValue() { + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); + OemNetworkPreferences networkPreference = mBuilder.build(); + + final Map<String, Integer> networkPreferences = + new OemNetworkPreferences + .Builder(networkPreference) + .build() + .getNetworkPreferences(); + + assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); + assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF); + } } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 0674138044ff..bcbc9e0d6007 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -331,11 +331,12 @@ public class ConnectivityServiceTest { private static final String TAG = "ConnectivityServiceTest"; private static final int TIMEOUT_MS = 500; - private static final int TEST_LINGER_DELAY_MS = 300; - // Chosen to be less than the linger timeout. This ensures that we can distinguish between a - // LOST callback that arrives immediately and a LOST callback that arrives after the linger - // timeout. For this, our assertions should run fast enough to leave less than - // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are + private static final int TEST_LINGER_DELAY_MS = 400; + private static final int TEST_NASCENT_DELAY_MS = 300; + // Chosen to be less than the linger and nascent timeout. This ensures that we can distinguish + // between a LOST callback that arrives immediately and a LOST callback that arrives after + // the linger/nascent timeout. For this, our assertions should run fast enough to leave + // less than (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are // supposedly fired, and the time we call expectCallback. private static final int TEST_CALLBACK_TIMEOUT_MS = 250; // Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to @@ -1262,6 +1263,13 @@ public class ConnectivityServiceTest { } } + private void processBroadcastForVpn(Intent intent) { + // The BroadcastReceiver for this broadcast checks it is being run on the handler thread. + final Handler handler = new Handler(mCsHandlerThread.getLooper()); + handler.post(() -> mServiceContext.sendBroadcast(intent)); + waitForIdle(); + } + private void mockUidNetworkingBlocked() { doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class) .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules, @@ -1395,6 +1403,7 @@ public class ConnectivityServiceTest { mMockNetd, mDeps); mService.mLingerDelayMs = TEST_LINGER_DELAY_MS; + mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS; verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any()); final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor = @@ -1737,6 +1746,108 @@ public class ConnectivityServiceTest { verifyNoNetwork(); } + /** + * Verify a newly created network will be inactive instead of torn down even if no one is + * requesting. + */ + @Test + public void testNewNetworkInactive() throws Exception { + // Create a callback that monitoring the testing network. + final TestNetworkCallback listenCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), listenCallback); + + // 1. Create a network that is not requested by anyone, and does not satisfy any of the + // default requests. Verify that the network will be inactive instead of torn down. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithoutInternet(); + listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + listenCallback.assertNoCallback(); + + // Verify that the network will be torn down after nascent expiry. A small period of time + // is added in case of flakiness. + final int nascentTimeoutMs = + mService.mNascentDelayMs + mService.mNascentDelayMs / 4; + listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs); + + // 2. Create a network that is satisfied by a request comes later. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithoutInternet(); + listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + final TestNetworkCallback wifiCallback = new TestNetworkCallback(); + mCm.requestNetwork(wifiRequest, wifiCallback); + wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // Verify that the network will be kept since the request is still satisfied. And is able + // to get disconnected as usual if the request is released after the nascent timer expires. + listenCallback.assertNoCallback(nascentTimeoutMs); + mCm.unregisterNetworkCallback(wifiCallback); + listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // 3. Create a network that is satisfied by a request comes later. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connectWithoutInternet(); + listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + mCm.requestNetwork(wifiRequest, wifiCallback); + wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // Verify that the network will still be torn down after the request gets removed. + mCm.unregisterNetworkCallback(wifiCallback); + listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + + // There is no need to ensure that LOSING is never sent in the common case that the + // network immediately satisfies a request that was already present, because it is already + // verified anywhere whenever {@code TestNetworkCallback#expectAvailable*} is called. + + mCm.unregisterNetworkCallback(listenCallback); + } + + /** + * Verify a newly created network will be inactive and switch to background if only background + * request is satisfied. + */ + @Test + public void testNewNetworkInactive_bgNetwork() throws Exception { + // Create a callback that monitoring the wifi network. + final TestNetworkCallback wifiListenCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(), wifiListenCallback); + + // Create callbacks that can monitor background and foreground mobile networks. + // This is done by granting using background networks permission before registration. Thus, + // the service will not add {@code NET_CAPABILITY_FOREGROUND} by default. + grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); + final TestNetworkCallback bgMobileListenCallback = new TestNetworkCallback(); + final TestNetworkCallback fgMobileListenCallback = new TestNetworkCallback(); + mCm.registerNetworkCallback(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(), bgMobileListenCallback); + mCm.registerNetworkCallback(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_FOREGROUND).build(), fgMobileListenCallback); + + // Connect wifi, which satisfies default request. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + wifiListenCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); + + // Connect a cellular network, verify that satisfies only the background callback. + setAlwaysOnNetworks(true); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + bgMobileListenCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + fgMobileListenCallback.assertNoCallback(); + assertFalse(isForegroundNetwork(mCellNetworkAgent)); + + mCellNetworkAgent.disconnect(); + bgMobileListenCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + fgMobileListenCallback.assertNoCallback(); + + mCm.unregisterNetworkCallback(wifiListenCallback); + mCm.unregisterNetworkCallback(bgMobileListenCallback); + mCm.unregisterNetworkCallback(fgMobileListenCallback); + } + @Test public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception { // Test bringing up unvalidated WiFi @@ -3894,8 +4005,9 @@ public class ConnectivityServiceTest { setAlwaysOnNetworks(false); testFactory.expectRequestRemove(); - // ... and cell data to be torn down. - cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + // ... and cell data to be torn down after nascent network timeout. + cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, + mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS); assertLength(1, mCm.getAllNetworks()); } finally { testFactory.terminate(); @@ -5300,20 +5412,20 @@ public class ConnectivityServiceTest { // MOBILE_IFNAME even though the default network is wifi. // TODO: fix this to pass in the actual default network interface. Whether or not the VPN // applies to the system server UID should not have any bearing on network stats. - mService.setUnderlyingNetworksForVpn(onlyCell); + mMockVpn.setUnderlyingNetworks(onlyCell); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, new String[]{MOBILE_IFNAME}); reset(mStatsService); - mService.setUnderlyingNetworksForVpn(cellAndWifi); + mMockVpn.setUnderlyingNetworks(cellAndWifi); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, new String[]{MOBILE_IFNAME, WIFI_IFNAME}); reset(mStatsService); // Null underlying networks are ignored. - mService.setUnderlyingNetworksForVpn(cellNullAndWifi); + mMockVpn.setUnderlyingNetworks(cellNullAndWifi); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, new String[]{MOBILE_IFNAME, WIFI_IFNAME}); @@ -5362,25 +5474,25 @@ public class ConnectivityServiceTest { // is probably a performance improvement (though it's very unlikely that a VPN would declare // no underlying networks). // Also, for the same reason as above, the active interface passed in is null. - mService.setUnderlyingNetworksForVpn(new Network[0]); + mMockVpn.setUnderlyingNetworks(new Network[0]); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, null); reset(mStatsService); // Specifying only a null underlying network is the same as no networks. - mService.setUnderlyingNetworksForVpn(onlyNull); + mMockVpn.setUnderlyingNetworks(onlyNull); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, null); reset(mStatsService); // Specifying networks that are all disconnected is the same as specifying no networks. - mService.setUnderlyingNetworksForVpn(onlyCell); + mMockVpn.setUnderlyingNetworks(onlyCell); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, null); reset(mStatsService); // Passing in null again means follow the default network again. - mService.setUnderlyingNetworksForVpn(null); + mMockVpn.setUnderlyingNetworks(null); waitForIdle(); expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, new String[]{WIFI_IFNAME}); @@ -5855,7 +5967,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(false, true, false); assertUidRangesUpdatedForMyUid(true); final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId()); - mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork}); + mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork}); callback.expectAvailableCallbacksUnvalidated(mMockVpn); assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) .hasTransport(TRANSPORT_VPN)); @@ -6049,7 +6161,7 @@ public class ConnectivityServiceTest { final Set<UidRange> ranges = uidRangesForUid(uid); mMockVpn.registerAgent(ranges); - mService.setUnderlyingNetworksForVpn(new Network[0]); + mMockVpn.setUnderlyingNetworks(new Network[0]); // VPN networks do not satisfy the default request and are automatically validated // by NetworkMonitor @@ -6297,7 +6409,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); mCellNetworkAgent.connect(true); - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6312,7 +6424,7 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); mWiFiNetworkAgent.connect(true); - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6323,7 +6435,7 @@ public class ConnectivityServiceTest { assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); // Don't disconnect, but note the VPN is not using wifi any more. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6354,7 +6466,7 @@ public class ConnectivityServiceTest { vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn); // Use Wifi but not cell. Note the VPN is now unmetered and not suspended. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mWiFiNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6365,7 +6477,7 @@ public class ConnectivityServiceTest { assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent); // Use both again. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6380,7 +6492,7 @@ public class ConnectivityServiceTest { vpnNetworkCallback.assertNoCallback(); // Stop using WiFi. The VPN is suspended again. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) @@ -6391,7 +6503,7 @@ public class ConnectivityServiceTest { assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); // Use both again. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6526,9 +6638,7 @@ public class ConnectivityServiceTest { addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); // Send a USER_ADDED broadcast for it. - // The BroadcastReceiver for this broadcast checks that is being run on the handler thread. - final Handler handler = new Handler(mCsHandlerThread.getLooper()); - handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); + processBroadcastForVpn(addedIntent); // Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added // restricted user. @@ -6552,7 +6662,7 @@ public class ConnectivityServiceTest { // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. final Intent removedIntent = new Intent(ACTION_USER_REMOVED); removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); + processBroadcastForVpn(removedIntent); // Expect that the VPN gains the UID range for the restricted user, and that the capability // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved. @@ -6609,9 +6719,7 @@ public class ConnectivityServiceTest { // TODO: check that VPN app within restricted profile still has access, etc. final Intent addedIntent = new Intent(ACTION_USER_ADDED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - final Handler handler = new Handler(mCsHandlerThread.getLooper()); - handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); - waitForIdle(); + processBroadcastForVpn(addedIntent); assertNull(mCm.getActiveNetworkForUid(uid)); assertNull(mCm.getActiveNetworkForUid(restrictedUid)); @@ -6621,8 +6729,7 @@ public class ConnectivityServiceTest { // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. final Intent removedIntent = new Intent(ACTION_USER_REMOVED); removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); - handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); - waitForIdle(); + processBroadcastForVpn(removedIntent); assertNull(mCm.getActiveNetworkForUid(uid)); assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); @@ -6724,7 +6831,7 @@ public class ConnectivityServiceTest { // Ensure VPN is now the active network. assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // VPN is using Cell - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork() }); waitForIdle(); @@ -6732,7 +6839,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); // VPN is now using WiFi - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mWiFiNetworkAgent.getNetwork() }); waitForIdle(); @@ -6740,7 +6847,7 @@ public class ConnectivityServiceTest { assertFalse(mCm.isActiveNetworkMetered()); // VPN is using Cell | WiFi. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); waitForIdle(); @@ -6748,7 +6855,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); // VPN is using WiFi | Cell. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() }); waitForIdle(); @@ -6756,7 +6863,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); // VPN is not using any underlying networks. - mService.setUnderlyingNetworksForVpn(new Network[0]); + mMockVpn.setUnderlyingNetworks(new Network[0]); waitForIdle(); // VPN without underlying networks is treated as metered. @@ -6783,7 +6890,7 @@ public class ConnectivityServiceTest { assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // VPN is tracking current platform default (WiFi). - mService.setUnderlyingNetworksForVpn(null); + mMockVpn.setUnderlyingNetworks(null); waitForIdle(); // Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered. @@ -6791,7 +6898,7 @@ public class ConnectivityServiceTest { // VPN explicitly declares WiFi as its underlying network. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mWiFiNetworkAgent.getNetwork() }); waitForIdle(); @@ -7199,9 +7306,7 @@ public class ConnectivityServiceTest { final int userId = UserHandle.getUserId(Process.myUid()); final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - final Handler handler = new Handler(mCsHandlerThread.getLooper()); - handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); - waitForIdle(); + processBroadcastForVpn(addedIntent); // Lockdown VPN disables teardown and enables lockdown. assertFalse(mMockVpn.getEnableTeardown()); @@ -8643,7 +8748,7 @@ public class ConnectivityServiceTest { setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); - assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network})); + assertTrue(mMockVpn.setUnderlyingNetworks(new Network[] {naiWithoutUid.network})); waitForIdle(); assertTrue( "Active VPN permission not applied", @@ -8651,7 +8756,7 @@ public class ConnectivityServiceTest { Process.myPid(), Process.myUid(), naiWithoutUid, mContext.getOpPackageName())); - assertTrue(mService.setUnderlyingNetworksForVpn(null)); + assertTrue(mMockVpn.setUnderlyingNetworks(null)); waitForIdle(); assertFalse( "VPN shouldn't receive callback on non-underlying network", diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 32c6a75bd904..73cc9f129e79 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -953,14 +953,7 @@ public class VpnTest { } private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception { - // TODO(b/175883995): once these tests have been updated for the changes to the UserManager - // API, remove this ad-hoc setup code and use setMockedUsers(primaryUser) again. - // setMockedUsers(primaryUser); - final ArrayList<UserInfo> users = new ArrayList<>(); - users.add(primaryUser); - when(mUserManager.getAliveUsers()).thenReturn(users); - when(mUserManager.getUserInfo(primaryUser.id)).thenReturn(primaryUser); - when(mUserManager.canHaveRestrictedProfile()).thenReturn(false); + setMockedUsers(primaryUser); // Dummy egress interface final LinkProperties lp = new LinkProperties(); @@ -1159,10 +1152,6 @@ public class VpnTest { doReturn(UserHandle.of(userId)).when(asUserContext).getUser(); when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt())) .thenReturn(asUserContext); - when(asUserContext.getSystemServiceName(UserManager.class)) - .thenReturn(Context.USER_SERVICE); - when(asUserContext.getSystemService(UserManager.class)) - .thenReturn(mUserManager); final TestLooper testLooper = new TestLooper(); final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService, mNetd, userId, mKeyStore, mSystemServices, mIkev2SessionCreator); diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index aeb142b901d2..1fe13fe97fbe 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -28,8 +28,6 @@ import android.view.IWindowManager; import junit.framework.TestCase; -import org.junit.Test; - /** * TODO: Remove this. This is only a placeholder, need to implement this. */ @@ -56,7 +54,7 @@ public class WindowManagerPermissionTests extends TestCase { } try { - mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY); + mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null /* options */); fail("IWindowManager.addWindowToken did not throw SecurityException as" + " expected"); } catch (SecurityException e) { @@ -155,29 +153,4 @@ public class WindowManagerPermissionTests extends TestCase { fail("Unexpected remote exception"); } } - - @Test - public void testADD_WINDOW_TOKEN_WITH_OPTIONS() { - // Verify if addWindowTokenWithOptions throw SecurityException for privileged window type. - try { - mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, ""); - fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as" - + " expected"); - } catch (SecurityException e) { - // expected - } catch (RemoteException e) { - fail("Unexpected remote exception"); - } - - // Verify if addWindowTokenWithOptions throw SecurityException for null packageName. - try { - mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, null); - fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as" - + " expected"); - } catch (SecurityException e) { - // expected - } catch (RemoteException e) { - fail("Unexpected remote exception"); - } - } } |