Merge "[CSAI API] Unhide TvAdView" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index aea6781..04f907a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6880,6 +6880,7 @@
method public CharSequence getName();
method @Nullable public String getParentChannelId();
method public android.net.Uri getSound();
+ method @FlaggedApi("android.app.notification_channel_vibration_effect_api") @Nullable public android.os.VibrationEffect getVibrationEffect();
method public long[] getVibrationPattern();
method public boolean hasUserSetImportance();
method public boolean hasUserSetSound();
@@ -6899,6 +6900,7 @@
method public void setName(CharSequence);
method public void setShowBadge(boolean);
method public void setSound(android.net.Uri, android.media.AudioAttributes);
+ method @FlaggedApi("android.app.notification_channel_vibration_effect_api") public void setVibrationEffect(@Nullable android.os.VibrationEffect);
method public void setVibrationPattern(long[]);
method public boolean shouldShowLights();
method public boolean shouldVibrate();
@@ -20266,6 +20268,7 @@
method @Nullable public android.hardware.input.HostUsiVersion getHostUsiVersion(@NonNull android.view.Display);
method @Nullable public android.view.InputDevice getInputDevice(int);
method public int[] getInputDeviceIds();
+ method @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") @Nullable public android.view.InputDevice.ViewBehavior getInputDeviceViewBehavior(int);
method @FloatRange(from=0, to=1) public float getMaximumObscuringOpacityForTouch();
method public boolean isStylusPointerIconEnabled();
method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler);
@@ -27318,6 +27321,7 @@
field public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = 18; // 0x12
field public static final int VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 6; // 0x6
field public static final int VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 5; // 0x5
+ field @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19; // 0x13
field public static final int VIDEO_UNAVAILABLE_REASON_TUNING = 1; // 0x1
field public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; // 0x0
field public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2; // 0x2
@@ -27409,6 +27413,7 @@
method public void onRemoveBroadcastInfo(int);
method public void onRequestAd(@NonNull android.media.tv.AdRequest);
method public void onRequestBroadcastInfo(@NonNull android.media.tv.BroadcastInfoRequest);
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onResumePlayback();
method public boolean onSelectAudioPresentation(int, int);
method public boolean onSelectTrack(int, @Nullable String);
method public abstract void onSetCaptionEnabled(boolean);
@@ -27416,6 +27421,7 @@
method public abstract void onSetStreamVolume(@FloatRange(from=0.0, to=1.0) float);
method public abstract boolean onSetSurface(@Nullable android.view.Surface);
method public void onSetTvMessageEnabled(int, boolean);
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onStopPlayback(int);
method public void onSurfaceChanged(int, int, int);
method public long onTimeShiftGetCurrentPosition();
method public long onTimeShiftGetStartPosition();
@@ -27550,6 +27556,7 @@
method public boolean onUnhandledInputEvent(android.view.InputEvent);
method public void overrideTvAppAttributionSource(@NonNull android.content.AttributionSource);
method public void reset();
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void resumePlayback();
method public void selectAudioPresentation(int, int);
method public void selectTrack(int, String);
method public void sendAppPrivateCommand(@NonNull String, android.os.Bundle);
@@ -27562,6 +27569,7 @@
method public void setTvMessageEnabled(int, boolean);
method public void setZOrderMediaOverlay(boolean);
method public void setZOrderOnTop(boolean);
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void stopPlayback(int);
method public void timeShiftPause();
method public void timeShiftPlay(String, android.net.Uri);
method public void timeShiftResume();
@@ -50494,6 +50502,10 @@
method public boolean isFromSource(int);
}
+ @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") public static final class InputDevice.ViewBehavior {
+ method @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") public boolean shouldSmoothScroll(int, int);
+ }
+
public abstract class InputEvent implements android.os.Parcelable {
method public int describeContents();
method public final android.view.InputDevice getDevice();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 01f54ad..f026b57 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -13464,7 +13464,9 @@
method public void setTelecomCallId(@NonNull String);
field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000
field public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 262144; // 0x40000
+ field @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final String EVENT_CALL_QUALITY_REPORT = "android.telecom.event.CALL_QUALITY_REPORT";
field public static final String EVENT_DEVICE_TO_DEVICE_MESSAGE = "android.telecom.event.DEVICE_TO_DEVICE_MESSAGE";
+ field @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final String EXTRA_CALL_QUALITY_REPORT = "android.telecom.extra.CALL_QUALITY_REPORT";
field public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE = "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_TYPE";
field public static final String EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE = "android.telecom.extra.DEVICE_TO_DEVICE_MESSAGE_VALUE";
field public static final String EXTRA_DISABLE_ADD_CALL = "android.telecom.extra.DISABLE_ADD_CALL";
@@ -13732,6 +13734,7 @@
method @Deprecated public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
+ method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
@@ -15040,7 +15043,7 @@
@FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public class TelephonyRegistryManager {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
- method public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
}
public final class ThermalMitigationRequest implements android.os.Parcelable {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 15d692a..ab5395e 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,7 @@
*/
package android.app;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -30,6 +31,9 @@
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.VibrationEffect;
+import android.os.vibrator.persistence.VibrationXmlParser;
+import android.os.vibrator.persistence.VibrationXmlSerializer;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.text.TextUtils;
@@ -49,6 +53,8 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
import java.util.Arrays;
import java.util.Objects;
@@ -146,6 +152,7 @@
private static final String ATT_LIGHTS = "lights";
private static final String ATT_LIGHT_COLOR = "light_color";
private static final String ATT_VIBRATION = "vibration";
+ private static final String ATT_VIBRATION_EFFECT = "vibration_effect";
private static final String ATT_VIBRATION_ENABLED = "vibration_enabled";
private static final String ATT_SOUND = "sound";
private static final String ATT_USAGE = "usage";
@@ -253,7 +260,8 @@
private boolean mSoundRestored = false;
private boolean mLights;
private int mLightColor = DEFAULT_LIGHT_COLOR;
- private long[] mVibration;
+ private long[] mVibrationPattern;
+ private VibrationEffect mVibrationEffect;
// Bitwise representation of fields that have been changed by the user, preventing the app from
// making changes to these fields.
private int mUserLockedFields;
@@ -324,9 +332,13 @@
mSound = null;
}
mLights = in.readByte() != 0;
- mVibration = in.createLongArray();
- if (mVibration != null && mVibration.length > MAX_VIBRATION_LENGTH) {
- mVibration = Arrays.copyOf(mVibration, MAX_VIBRATION_LENGTH);
+ mVibrationPattern = in.createLongArray();
+ if (mVibrationPattern != null && mVibrationPattern.length > MAX_VIBRATION_LENGTH) {
+ mVibrationPattern = Arrays.copyOf(mVibrationPattern, MAX_VIBRATION_LENGTH);
+ }
+ if (Flags.notificationChannelVibrationEffectApi()) {
+ mVibrationEffect =
+ in.readInt() != 0 ? VibrationEffect.CREATOR.createFromParcel(in) : null;
}
mUserLockedFields = in.readInt();
mUserVisibleTaskShown = in.readByte() != 0;
@@ -381,7 +393,15 @@
dest.writeByte((byte) 0);
}
dest.writeByte(mLights ? (byte) 1 : (byte) 0);
- dest.writeLongArray(mVibration);
+ dest.writeLongArray(mVibrationPattern);
+ if (Flags.notificationChannelVibrationEffectApi()) {
+ if (mVibrationEffect != null) {
+ dest.writeInt(1);
+ mVibrationEffect.writeToParcel(dest, /* flags= */ 0);
+ } else {
+ dest.writeInt(0);
+ }
+ }
dest.writeInt(mUserLockedFields);
dest.writeByte(mUserVisibleTaskShown ? (byte) 1 : (byte) 0);
dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0);
@@ -585,8 +605,8 @@
/**
* Sets the vibration pattern for notifications posted to this channel. If the provided
- * pattern is valid (non-null, non-empty), will enable vibration on this channel
- * (equivalent to calling {@link #enableVibration(boolean)} with {@code true}).
+ * pattern is valid (non-null, non-empty with at least 1 non-zero value), will enable vibration
+ * on this channel (equivalent to calling {@link #enableVibration(boolean)} with {@code true}).
* Otherwise, vibration will be disabled unless {@link #enableVibration(boolean)} is
* used with {@code true}, in which case the default vibration will be used.
*
@@ -595,7 +615,56 @@
*/
public void setVibrationPattern(long[] vibrationPattern) {
this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
- this.mVibration = vibrationPattern;
+ this.mVibrationPattern = vibrationPattern;
+ if (Flags.notificationChannelVibrationEffectApi()) {
+ try {
+ this.mVibrationEffect =
+ VibrationEffect.createWaveform(vibrationPattern, /* repeat= */ -1);
+ } catch (IllegalArgumentException | NullPointerException e) {
+ this.mVibrationEffect = null;
+ }
+ }
+ }
+
+ /**
+ * Sets a {@link VibrationEffect} for notifications posted to this channel. If the
+ * provided effect is non-null, will enable vibration on this channel (equivalent
+ * to calling {@link #enableVibration(boolean)} with {@code true}). Otherwise
+ * vibration will be disabled unless {@link #enableVibration(boolean)} is used with
+ * {@code true}, in which case the default vibration will be used.
+ *
+ * <p>The effect passed here will be returned from {@link #getVibrationEffect()}.
+ * If the provided {@link VibrationEffect} is an equivalent to a wave-form
+ * vibration pattern, the equivalent wave-form pattern will be returned from
+ * {@link #getVibrationPattern()}.
+ *
+ * <p>Note that some {@link VibrationEffect}s may not be playable on some devices.
+ * In cases where such an effect is passed here, vibration will still be enabled
+ * for the channel, but the default vibration will be used. Nonetheless, the
+ * provided effect will be stored and be returned from {@link #getVibrationEffect}
+ * calls, and could be used by the same channel on a different device, for example,
+ * in cases the user backs up and restores to a device that does have the ability
+ * to play the effect, where that effect will be used instead of the default. To
+ * avoid such issues that could make the vibration behavior of your notification
+ * channel differ among different devices, it's recommended that you avoid
+ * vibration effect primitives, as the support for them differs widely among
+ * devices (read {@link VibrationEffect.Composition} for more on vibration
+ * primitives).
+ *
+ * <p>Only modifiable before the channel is submitted to
+ * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
+ *
+ * @see #getVibrationEffect()
+ * @see Vibrator#areEffectsSupported(int...)
+ * @see Vibrator#arePrimitivesSupported(int...)
+ */
+ @FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API)
+ public void setVibrationEffect(@Nullable VibrationEffect effect) {
+ this.mVibrationEnabled = effect != null;
+ this.mVibrationEffect = effect;
+ this.mVibrationPattern =
+ effect == null
+ ? null : effect.computeCreateWaveformOffOnTimingsOrNull();
}
/**
@@ -768,7 +837,35 @@
* vibration is not enabled ({@link #shouldVibrate()}).
*/
public long[] getVibrationPattern() {
- return mVibration;
+ return mVibrationPattern;
+ }
+
+ /**
+ * Returns the {@link VibrationEffect} for notifications posted to this channel.
+ * The returned effect is derived from either the effect provided in the
+ * {@link #setVibrationEffect(VibrationEffect)} method, or the equivalent vibration effect
+ * of the pattern set via the {@link #setVibrationPattern(long[])} method, based on setter
+ * method that was called last.
+ *
+ * The returned effect will be ignored in one of the following cases:
+ * <ul>
+ * <li> vibration is not enabled for the channel (i.e. {@link #shouldVibrate()}
+ * returns {@code false}).
+ * <li> the effect is not supported/playable by the device. In this case, if
+ * vibration is enabled for the channel, the default channel vibration will
+ * be used instead.
+ * </ul>
+ *
+ * @return the {@link VibrationEffect} set via {@link
+ * #setVibrationEffect(VibrationEffect)}, or the equivalent of the
+ * vibration set via {@link #setVibrationPattern(long[])}.
+ *
+ * @see VibrationEffect#createWaveform(long[], int)
+ */
+ @FlaggedApi(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API)
+ @Nullable
+ public VibrationEffect getVibrationEffect() {
+ return mVibrationEffect;
}
/**
@@ -991,7 +1088,19 @@
enableLights(safeBool(parser, ATT_LIGHTS, false));
setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
+ // Set the pattern before the effect, so that we can properly handle cases where the pattern
+ // is null, but the effect is not null (i.e. for non-waveform VibrationEffects - the ones
+ // which cannot be represented as a vibration pattern).
setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
+ if (Flags.notificationChannelVibrationEffectApi()) {
+ VibrationEffect vibrationEffect = safeVibrationEffect(parser, ATT_VIBRATION_EFFECT);
+ if (vibrationEffect != null) {
+ // Restore the effect only if it is not null. This allows to avoid undoing a
+ // `setVibrationPattern` call above, if that was done with a non-null pattern
+ // (e.g. back up from a version that did not support `setVibrationEffect`).
+ setVibrationEffect(vibrationEffect);
+ }
+ }
enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false));
setDeleted(safeBool(parser, ATT_DELETED, false));
@@ -1180,6 +1289,9 @@
if (getVibrationPattern() != null) {
out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern()));
}
+ if (getVibrationEffect() != null) {
+ out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+ }
if (getUserLockedFields() != 0) {
out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields());
}
@@ -1260,6 +1372,9 @@
record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isUserVisibleTaskShown()));
record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern()));
+ if (getVibrationEffect() != null) {
+ record.put(ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+ }
record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
record.put(ATT_DELETED, Boolean.toString(isDeleted()));
record.put(ATT_DELETED_TIME_MS, Long.toString(getDeletedTimeMs()));
@@ -1287,6 +1402,30 @@
return val == null ? null : Uri.parse(val);
}
+ private static String vibrationToString(VibrationEffect effect) {
+ StringWriter writer = new StringWriter();
+ try {
+ VibrationXmlSerializer.serialize(
+ effect, writer, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to serialize vibration: " + effect, e);
+ }
+ return writer.toString();
+ }
+
+ private static VibrationEffect safeVibrationEffect(TypedXmlPullParser parser, String att) {
+ final String val = parser.getAttributeValue(null, att);
+ if (val != null) {
+ try {
+ return VibrationXmlParser.parseVibrationEffect(
+ new StringReader(val), VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to read serialized vibration effect", e);
+ }
+ }
+ return null;
+ }
+
private static int safeInt(TypedXmlPullParser parser, String att, int defValue) {
return parser.getAttributeInt(null, att, defValue);
}
@@ -1361,7 +1500,8 @@
&& Objects.equals(getName(), that.getName())
&& Objects.equals(mDesc, that.mDesc)
&& Objects.equals(getSound(), that.getSound())
- && Arrays.equals(mVibration, that.mVibration)
+ && Arrays.equals(mVibrationPattern, that.mVibrationPattern)
+ && Objects.equals(getVibrationEffect(), that.getVibrationEffect())
&& Objects.equals(getGroup(), that.getGroup())
&& Objects.equals(getAudioAttributes(), that.getAudioAttributes())
&& mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
@@ -1379,9 +1519,9 @@
getUserLockedFields(), isUserVisibleTaskShown(),
mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(),
getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles,
- mImportanceLockedDefaultApp, mOriginalImportance,
+ mImportanceLockedDefaultApp, mOriginalImportance, getVibrationEffect(),
mParentId, mConversationId, mDemoted, mImportantConvo);
- result = 31 * result + Arrays.hashCode(mVibration);
+ result = 31 * result + Arrays.hashCode(mVibrationPattern);
return result;
}
@@ -1413,7 +1553,9 @@
+ ", mSound=" + mSound
+ ", mLights=" + mLights
+ ", mLightColor=" + mLightColor
- + ", mVibration=" + Arrays.toString(mVibration)
+ + ", mVibrationPattern=" + Arrays.toString(mVibrationPattern)
+ + ", mVibrationEffect="
+ + (mVibrationEffect == null ? "null" : mVibrationEffect.toString())
+ ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
+ ", mUserVisibleTaskShown=" + mUserVisibleTaskShown
+ ", mVibrationEnabled=" + mVibrationEnabled
@@ -1448,8 +1590,8 @@
}
proto.write(NotificationChannelProto.USE_LIGHTS, mLights);
proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor);
- if (mVibration != null) {
- for (long v : mVibration) {
+ if (mVibrationPattern != null) {
+ for (long v : mVibrationPattern) {
proto.write(NotificationChannelProto.VIBRATION, v);
}
}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index a5d4a14..c40b23e 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -50,3 +50,10 @@
description: "Adds a new voicemail category for notifications"
bug: "322806700"
}
+
+flag {
+ name: "notification_channel_vibration_effect_api"
+ namespace: "systemui"
+ description: "This flag enables the API to allow setting VibrationEffect for NotificationChannels"
+ bug: "241732519"
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 47f5b4c..451d6fb 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -5313,7 +5313,7 @@
* </code></pre>
* <ul>
* <li>VIDEO_STABILIZATION_MODES: {OFF, PREVIEW}</li>
- * <li>AE_TARGET_FPS_RANGE: {{<em>, 30}, {</em>, 60}}</li>
+ * <li>AE_TARGET_FPS_RANGE: { {<em>, 30}, {</em>, 60} }</li>
* <li>DYNAMIC_RANGE_PROFILE: {STANDARD, HLG10}</li>
* </ul>
* <p>This key is available on all devices.</p>
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 4ebbde7..db992cd 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,9 +16,11 @@
package android.hardware.input;
+import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -294,6 +296,23 @@
}
/**
+ * Gets the {@link InputDevice.ViewBehavior} of the input device with a given {@code id}.
+ *
+ * <p>Use this API to query a fresh view behavior instance whenever the input device
+ * changes.
+ *
+ * @param deviceId the id of the input device whose view behavior is being requested.
+ * @return the view behavior of the input device with the provided id, or {@code null} if there
+ * is not input device with the provided id.
+ */
+ @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+ @Nullable
+ public InputDevice.ViewBehavior getInputDeviceViewBehavior(int deviceId) {
+ InputDevice device = getInputDevice(deviceId);
+ return device == null ? null : device.getViewBehavior();
+ }
+
+ /**
* Gets information about the input device with the specified descriptor.
* @param descriptor The input device descriptor.
* @return The input device or null if not found.
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 0a813a3..d39c4ce 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -19,12 +19,15 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.SystemService;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
@@ -73,9 +76,15 @@
* Limit API access to only carrier apps with certain permissions or apps running on
* privileged UID.
*
+ * TelephonyRegistryManager is intended for use on devices that implement
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. On devices
+ * that do not implement this feature, the behavior is not reliable.
+ *
* @hide
*/
@SystemApi
+@SystemService(Context.TELEPHONY_REGISTRY_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TELEPHONY)
@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
public class TelephonyRegistryManager {
@@ -389,10 +398,11 @@
}
/**
- * Notify call state changed on all subscriptions.
+ * Notify call state changed on all subscriptions, excluding over-the-top VOIP calls (otherwise
+ * known as self-managed calls in the Android Platform).
*
* @param state latest call state. e.g, offhook, ringing
- * @param incomingNumber incoming phone number.
+ * @param incomingNumber incoming phone number or null in the case for OTT VOIP calls
* @hide
*/
@SystemApi
@@ -627,17 +637,20 @@
}
/**
- * Notify outgoing emergency call.
+ * Notify outgoing emergency call to all applications that have registered a listener
+ * ({@link PhoneStateListener}) or a callback ({@link TelephonyCallback}) to monitor changes in
+ * telephony states.
* @param simSlotIndex Sender phone ID.
- * @param subId Sender subscription ID.
+ * @param subscriptionId Sender subscription ID.
* @param emergencyNumber Emergency number.
* @hide
*/
@SystemApi
- public void notifyOutgoingEmergencyCall(int simSlotIndex, int subId,
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void notifyOutgoingEmergencyCall(int simSlotIndex, int subscriptionId,
@NonNull EmergencyNumber emergencyNumber) {
try {
- sRegistry.notifyOutgoingEmergencyCall(simSlotIndex, subId, emergencyNumber);
+ sRegistry.notifyOutgoingEmergencyCall(simSlotIndex, subscriptionId, emergencyNumber);
} catch (RemoteException ex) {
// system process is dead
throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/tracing/perfetto/DataSourceInstance.java b/core/java/android/tracing/perfetto/DataSourceInstance.java
index 4994501..3710b4d 100644
--- a/core/java/android/tracing/perfetto/DataSourceInstance.java
+++ b/core/java/android/tracing/perfetto/DataSourceInstance.java
@@ -69,4 +69,8 @@
public final void release() {
mDataSource.releaseDataSourceInstance(mInstanceIndex);
}
+
+ public final int getInstanceIndex() {
+ return mInstanceIndex;
+ }
}
diff --git a/core/java/android/tracing/transition/TransitionDataSource.java b/core/java/android/tracing/transition/TransitionDataSource.java
index baece75..82559da 100644
--- a/core/java/android/tracing/transition/TransitionDataSource.java
+++ b/core/java/android/tracing/transition/TransitionDataSource.java
@@ -24,8 +24,8 @@
import android.tracing.perfetto.StopCallbackArguments;
import android.util.proto.ProtoInputStream;
-import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* @hide
@@ -38,6 +38,9 @@
private final Runnable mOnFlushStaticCallback;
private final Runnable mOnStopStaticCallback;
+ private final ConcurrentHashMap<Integer, ConcurrentHashMap<String, Integer>> mHandlerMappings =
+ new ConcurrentHashMap<>();
+
public TransitionDataSource(Runnable onStart, Runnable onFlush, Runnable onStop) {
super(DATA_SOURCE_NAME);
this.mOnStartStaticCallback = onStart;
@@ -47,11 +50,16 @@
@Override
protected TlsState createTlsState(CreateTlsStateArgs<DataSourceInstance> args) {
- return new TlsState();
+ return new TlsState(args.getDataSourceInstanceLocked().getInstanceIndex());
}
public class TlsState {
- public final Map<String, Integer> handlerMapping = new HashMap<>();
+ public final Map<String, Integer> handlerMapping;
+
+ public TlsState(int instanceIndex) {
+ handlerMapping = mHandlerMappings
+ .computeIfAbsent(instanceIndex, index -> new ConcurrentHashMap<>());
+ }
}
@Override
@@ -70,6 +78,7 @@
@Override
protected void onStop(StopCallbackArguments args) {
mOnStopStaticCallback.run();
+ mHandlerMappings.remove(instanceIndex);
}
};
}
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index f2c3abc..891e2a2 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,7 +16,10 @@
package android.view;
+import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
+
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +31,7 @@
import android.hardware.SensorManager;
import android.hardware.input.HostUsiVersion;
import android.hardware.input.InputDeviceIdentifier;
+import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
import android.hardware.lights.LightsManager;
import android.icu.util.ULocale;
@@ -90,6 +94,8 @@
private final int mAssociatedDisplayId;
private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
+ private final ViewBehavior mViewBehavior = new ViewBehavior(this);
+
@GuardedBy("mMotionRanges")
private Vibrator mVibrator; // guarded by mMotionRanges during initialization
@@ -539,6 +545,8 @@
addMotionRange(in.readInt(), in.readInt(), in.readFloat(), in.readFloat(),
in.readFloat(), in.readFloat(), in.readFloat());
}
+
+ mViewBehavior.mShouldSmoothScroll = in.readBoolean();
}
/**
@@ -571,6 +579,7 @@
private int mUsiVersionMinor = -1;
private int mAssociatedDisplayId = Display.INVALID_DISPLAY;
private List<MotionRange> mMotionRanges = new ArrayList<>();
+ private boolean mShouldSmoothScroll;
/** @see InputDevice#getId() */
public Builder setId(int id) {
@@ -706,6 +715,16 @@
return this;
}
+ /**
+ * Sets the view behavior for smooth scrolling ({@code false} by default).
+ *
+ * @see ViewBehavior#shouldSmoothScroll(int, int)
+ */
+ public Builder setShouldSmoothScroll(boolean shouldSmoothScroll) {
+ mShouldSmoothScroll = shouldSmoothScroll;
+ return this;
+ }
+
/** Build {@link InputDevice}. */
public InputDevice build() {
InputDevice device = new InputDevice(
@@ -745,6 +764,8 @@
range.getResolution());
}
+ device.setShouldSmoothScroll(mShouldSmoothScroll);
+
return device;
}
}
@@ -1123,6 +1144,22 @@
return mMotionRanges;
}
+ /**
+ * Provides the {@link ViewBehavior} for the device.
+ *
+ * <p>This behavior is designed to be obtained using the
+ * {@link InputManager#getInputDeviceViewBehavior(int)} API, to allow associating the behavior
+ * with a {@link Context} (since input device is not associated with a context).
+ * The ability to associate the behavior with a context opens capabilities like linking the
+ * behavior to user settings, for example.
+ *
+ * @hide
+ */
+ @NonNull
+ public ViewBehavior getViewBehavior() {
+ return mViewBehavior;
+ }
+
// Called from native code.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void addMotionRange(int axis, int source,
@@ -1130,6 +1167,11 @@
mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz, resolution));
}
+ // Called from native code.
+ private void setShouldSmoothScroll(boolean shouldSmoothScroll) {
+ mViewBehavior.mShouldSmoothScroll = shouldSmoothScroll;
+ }
+
/**
* Returns the Bluetooth address of this input device, if known.
*
@@ -1447,6 +1489,82 @@
}
}
+ /**
+ * Provides information on how views processing {@link MotionEvent}s generated by this input
+ * device should respond to the events. Use {@link InputManager#getInputDeviceViewBehavior(int)}
+ * to get an instance of the view behavior for an input device.
+ *
+ * <p>See an example below how a {@link View} can use this class to determine and apply the
+ * scrolling behavior for a generic {@link MotionEvent}.
+ *
+ * <pre>{@code
+ * public boolean onGenericMotionEvent(MotionEvent event) {
+ * InputManager manager = context.getSystemService(InputManager.class);
+ * ViewBehavior viewBehavior = manager.getInputDeviceViewBehavior(event.getDeviceId());
+ * // Assume a helper function that tells us which axis to use for scrolling purpose.
+ * int axis = getScrollAxisForGenericMotionEvent(event);
+ * int source = event.getSource();
+ *
+ * boolean shouldSmoothScroll =
+ * viewBehavior != null && viewBehavior.shouldSmoothScroll(axis, source);
+ * // Proceed to running the scrolling logic...
+ * }
+ * }</pre>
+ *
+ * @see InputManager#getInputDeviceViewBehavior(int)
+ */
+ @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+ public static final class ViewBehavior {
+ private static final boolean DEFAULT_SHOULD_SMOOTH_SCROLL = false;
+
+ private final InputDevice mInputDevice;
+
+ // TODO(b/246946631): implement support for InputDevices to adjust this configuration
+ // by axis and source. When implemented, the axis/source specific config will take
+ // precedence over this global config.
+ /** A global smooth scroll configuration applying to all motion axis and input source. */
+ private boolean mShouldSmoothScroll = DEFAULT_SHOULD_SMOOTH_SCROLL;
+
+ /** @hide */
+ public ViewBehavior(@NonNull InputDevice inputDevice) {
+ mInputDevice = inputDevice;
+ }
+
+ /**
+ * Returns whether a view should smooth scroll when scrolling due to a {@link MotionEvent}
+ * generated by the input device.
+ *
+ * <p>Smooth scroll in this case refers to a scroll that animates the transition between
+ * the starting and ending positions of the scroll. When this method returns {@code true},
+ * views should try to animate a scroll generated by this device at the given axis and with
+ * the given source to produce a good scroll user experience. If this method returns
+ * {@code false}, animating scrolls is not necessary.
+ *
+ * <p>If the input device does not have a {@link MotionRange} with the provided axis and
+ * source, this method returns {@code false}.
+ *
+ * @param axis the {@link MotionEvent} axis whose value is used to get the scroll extent.
+ * @param source the {link InputDevice} source from which the {@link MotionEvent} that
+ * triggers the scroll came.
+ * @return {@code true} if smooth scrolling should be used for the scroll, or {@code false}
+ * if smooth scrolling is not necessary, or if the provided axis and source combination
+ * is not available for the input device.
+ */
+ @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+ public boolean shouldSmoothScroll(int axis, int source) {
+ // Note: although we currently do not use axis and source in computing the return value,
+ // we will keep the API params to avoid further public API changes when we start
+ // supporting axis/source configuration. Also, having these params lets OEMs provide
+ // their custom implementation of the API that depends on axis and source.
+
+ // TODO(b/246946631): speed up computation using caching of results.
+ if (mInputDevice.getMotionRange(axis, source) == null) {
+ return false;
+ }
+ return mShouldSmoothScroll;
+ }
+ }
+
@Override
public void writeToParcel(Parcel out, int flags) {
mKeyCharacterMap.writeToParcel(out, flags);
@@ -1484,6 +1602,8 @@
out.writeFloat(range.mFuzz);
out.writeFloat(range.mResolution);
}
+
+ out.writeBoolean(mViewBehavior.mShouldSmoothScroll);
}
@Override
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 239c626..aae0da9 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-#include <input/Input.h>
+#include "android_view_InputDevice.h"
#include <android_runtime/AndroidRuntime.h>
+#include <com_android_input_flags.h>
+#include <input/Input.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
-
#include <nativehelper/ScopedLocalRef.h>
-#include "android_view_InputDevice.h"
#include "android_view_KeyCharacterMap.h"
-
#include "core_jni_helpers.h"
namespace android {
@@ -34,6 +33,7 @@
jmethodID ctor;
jmethodID addMotionRange;
+ jmethodID setShouldSmoothScroll;
} gInputDeviceClassInfo;
jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& deviceInfo) {
@@ -103,6 +103,18 @@
}
}
+ if (com::android::input::flags::input_device_view_behavior_api()) {
+ const InputDeviceViewBehavior& viewBehavior = deviceInfo.getViewBehavior();
+ std::optional<bool> defaultSmoothScroll = viewBehavior.shouldSmoothScroll;
+ if (defaultSmoothScroll.has_value()) {
+ env->CallVoidMethod(inputDeviceObj.get(), gInputDeviceClassInfo.setShouldSmoothScroll,
+ *defaultSmoothScroll);
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ }
+ }
+
return env->NewLocalRef(inputDeviceObj.get());
}
@@ -118,6 +130,8 @@
gInputDeviceClassInfo.addMotionRange =
GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "addMotionRange", "(IIFFFFF)V");
+ gInputDeviceClassInfo.setShouldSmoothScroll =
+ GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "setShouldSmoothScroll", "(Z)V");
return 0;
}
diff --git a/core/tests/coretests/src/android/app/NotificationChannelTest.java b/core/tests/coretests/src/android/app/NotificationChannelTest.java
index d8305f0..56ab034 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelTest.java
@@ -19,6 +19,9 @@
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -42,6 +45,8 @@
import android.os.Parcel;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.VibrationEffect;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.MediaStore.Audio.AudioColumns;
import android.test.mock.MockContentResolver;
import android.util.Xml;
@@ -55,6 +60,7 @@
import com.google.common.base.Strings;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -63,10 +69,15 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.function.Consumer;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NotificationChannelTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private final String CLASS = "android.app.NotificationChannel";
Context mContext;
@@ -294,28 +305,12 @@
NotificationChannel channel = new NotificationChannel("id", "name", 3);
channel.setSound(uriToBeRestoredCanonicalized, mAudioAttributes);
- TypedXmlSerializer serializer = Xml.newFastSerializer();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
- serializer.startDocument(null, true);
-
// mock the canonicalize in writeXmlForBackup -> getSoundForBackup
when(mIContentProvider.canonicalize(any(), eq(uriToBeRestoredUncanonicalized)))
.thenReturn(uriToBeRestoredCanonicalized);
when(mIContentProvider.canonicalize(any(), eq(uriToBeRestoredCanonicalized)))
.thenReturn(uriToBeRestoredCanonicalized);
- channel.writeXmlForBackup(serializer, mContext);
- serializer.endDocument();
- serializer.flush();
-
- TypedXmlPullParser parser = Xml.newFastPullParser();
- byte[] byteArray = baos.toByteArray();
- parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
- parser.nextTag();
-
- NotificationChannel targetChannel = new NotificationChannel("id", "name", 3);
-
MatrixCursor cursor = new MatrixCursor(new String[] {"_id"});
cursor.addRow(new Object[] {100L});
@@ -350,7 +345,263 @@
when(mIContentProvider.canonicalize(any(), eq(uriAfterRestoredUncanonicalized)))
.thenReturn(uriAfterRestoredCanonicalized);
- targetChannel.populateFromXmlForRestore(parser, true, mContext);
- assertThat(targetChannel.getSound()).isEqualTo(uriAfterRestoredCanonicalized);
+ NotificationChannel restoredChannel = backUpAndRestore(channel);
+ assertThat(restoredChannel.getSound()).isEqualTo(uriAfterRestoredCanonicalized);
+ }
+
+ @Test
+ public void testVibrationGetters_nonPatternBasedVibrationEffect_waveform() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+ // Note that the amplitude used (1) is not the default amplitude, meaning that this effect
+ // does not have an equivalent pattern based effect.
+ VibrationEffect effect = VibrationEffect.createOneShot(123, 1);
+
+ channel.setVibrationEffect(effect);
+
+ Consumer<NotificationChannel> assertions = (testedChannel) -> {
+ assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+ assertNull(testedChannel.getVibrationPattern());
+ assertTrue(testedChannel.shouldVibrate());
+ };
+ assertions.accept(channel);
+ assertions.accept(writeToAndReadFromParcel(channel));
+ assertions.accept(backUpAndRestore(channel));
+ }
+
+ @Test
+ public void testVibrationGetters_nonPatternBasedVibrationEffect_nonWaveform() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+ VibrationEffect effect =
+ VibrationEffect
+ .startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose();
+
+ channel.setVibrationEffect(effect);
+
+ Consumer<NotificationChannel> assertions = (testedChannel) -> {
+ assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+ assertNull(testedChannel.getVibrationPattern()); // amplitude not default.
+ assertTrue(testedChannel.shouldVibrate());
+ };
+ assertions.accept(channel);
+ assertions.accept(writeToAndReadFromParcel(channel));
+ assertions.accept(backUpAndRestore(channel));
+ }
+
+ @Test
+ public void testVibrationGetters_patternBasedVibrationEffect_nonRepeating() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+ long[] pattern = new long[] {1, 2};
+ VibrationEffect effect = VibrationEffect.createWaveform(pattern, /* repeatIndex= */ -1);
+
+ channel.setVibrationEffect(effect);
+
+ Consumer<NotificationChannel> assertions = (testedChannel) -> {
+ assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+ assertTrue(Arrays.equals(pattern, testedChannel.getVibrationPattern()));
+ assertTrue(testedChannel.shouldVibrate());
+ };
+ assertions.accept(channel);
+ assertions.accept(writeToAndReadFromParcel(channel));
+ assertions.accept(backUpAndRestore(channel));
+ }
+
+ @Test
+ public void testVibrationGetters_patternBasedVibrationEffect_wholeRepeating() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+ long[] pattern = new long[] {1, 2};
+ VibrationEffect effect = VibrationEffect.createWaveform(pattern, /* repeatIndex= */ 0);
+
+ channel.setVibrationEffect(effect);
+
+ Consumer<NotificationChannel> assertions = (testedChannel) -> {
+ assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+ assertNull(testedChannel.getVibrationPattern());
+ assertTrue(testedChannel.shouldVibrate());
+ };
+ assertions.accept(channel);
+ assertions.accept(writeToAndReadFromParcel(channel));
+ assertions.accept(backUpAndRestore(channel));
+ }
+
+ @Test
+ public void testVibrationGetters_patternBasedVibrationEffect_partialRepeating()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+ long[] pattern = new long[] {1, 2, 3, 4};
+ VibrationEffect effect = VibrationEffect.createWaveform(pattern, /* repeatIndex= */ 2);
+
+ channel.setVibrationEffect(effect);
+
+ Consumer<NotificationChannel> assertions = (testedChannel) -> {
+ assertThat(testedChannel.getVibrationEffect()).isEqualTo(effect);
+ assertNull(testedChannel.getVibrationPattern());
+ assertTrue(testedChannel.shouldVibrate());
+ };
+ assertions.accept(channel);
+ assertions.accept(writeToAndReadFromParcel(channel));
+ assertions.accept(backUpAndRestore(channel));
+ }
+
+ @Test
+ public void testVibrationGetters_nullVibrationEffect() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+
+ channel.setVibrationEffect(null);
+
+ Consumer<NotificationChannel> assertions = (testedChannel) -> {
+ assertNull(channel.getVibrationEffect());
+ assertNull(channel.getVibrationPattern());
+ assertFalse(channel.shouldVibrate());
+ };
+ assertions.accept(channel);
+ assertions.accept(writeToAndReadFromParcel(channel));
+ assertions.accept(backUpAndRestore(channel));
+ }
+
+ @Test
+ public void testVibrationGetters_nullPattern() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+
+ channel.setVibrationPattern(null);
+
+ Consumer<NotificationChannel> assertions = (testedChannel) -> {
+ assertThat(testedChannel.getVibrationEffect()).isNull();
+ assertNull(testedChannel.getVibrationPattern());
+ assertFalse(testedChannel.shouldVibrate());
+ };
+ assertions.accept(channel);
+ assertions.accept(writeToAndReadFromParcel(channel));
+ assertions.accept(backUpAndRestore(channel));
+ }
+
+ @Test
+ public void testVibrationGetters_setEffectOverridesSetPattern() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+ VibrationEffect effect =
+ VibrationEffect.createOneShot(123, VibrationEffect.DEFAULT_AMPLITUDE);
+
+ channel.setVibrationPattern(new long[] {60, 80});
+ channel.setVibrationEffect(effect);
+
+ assertThat(channel.getVibrationEffect()).isEqualTo(effect);
+ assertTrue(Arrays.equals(new long[] {0, 123}, channel.getVibrationPattern()));
+ assertTrue(channel.shouldVibrate());
+
+ channel.setVibrationEffect(null);
+
+ assertNull(channel.getVibrationEffect());
+ assertNull(channel.getVibrationPattern());
+ assertFalse(channel.shouldVibrate());
+ }
+
+ @Test
+ public void testVibrationGetters_setPatternOverridesSetEffect() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+ long[] pattern = new long[] {0, 123};
+
+ channel.setVibrationEffect(
+ VibrationEffect.createOneShot(123, VibrationEffect.DEFAULT_AMPLITUDE));
+ channel.setVibrationPattern(pattern);
+
+ assertThat(channel.getVibrationEffect())
+ .isEqualTo(VibrationEffect.createWaveform(pattern, -1));
+ assertTrue(Arrays.equals(pattern, channel.getVibrationPattern()));
+ assertTrue(channel.shouldVibrate());
+
+ channel.setVibrationPattern(null);
+
+ assertNull(channel.getVibrationEffect());
+ assertNull(channel.getVibrationPattern());
+ assertFalse(channel.shouldVibrate());
+ }
+
+ @Test
+ public void testEqualityDependsOnVibration() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel1 = new NotificationChannel("id", "name", 3);
+ NotificationChannel channel2 = new NotificationChannel("id", "name", 3);
+ assertThat(channel1).isEqualTo(channel2);
+
+ VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP);
+ long[] pattern = new long[] {1, 2, 3};
+ channel1.setVibrationEffect(effect);
+ channel2.setVibrationEffect(effect);
+ assertThat(channel1).isEqualTo(channel2);
+
+ channel1.setVibrationPattern(pattern);
+ channel2.setVibrationPattern(pattern);
+ assertThat(channel1).isEqualTo(channel2);
+
+ channel1.setVibrationPattern(pattern);
+ channel2.setVibrationEffect(VibrationEffect.createWaveform(pattern, /* repeat= */ -1));
+ // Channels should still be equal, because the pattern and the effect set are equivalent.
+ assertThat(channel1).isEqualTo(channel2);
+
+ channel1.setVibrationEffect(effect);
+ channel2.setVibrationPattern(pattern);
+ assertThat(channel1).isNotEqualTo(channel2);
+ }
+
+ @Test
+ public void testSetVibrationPattern_flagOn_setsEffect() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+ long[] pattern = new long[] {1, 2, 3};
+
+ channel.setVibrationPattern(pattern);
+
+ assertThat(channel.getVibrationEffect())
+ .isEqualTo(VibrationEffect.createWaveform(pattern, -1));
+ }
+
+ @Test
+ public void testSetVibrationPattern_flagNotOn_doesNotSetEffect() throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ NotificationChannel channel = new NotificationChannel("id", "name", 3);
+
+ channel.setVibrationPattern(new long[] {1, 2, 3});
+
+ assertNull(channel.getVibrationEffect());
+ }
+
+ /** Backs up a given channel to an XML, and returns the channel read from the XML. */
+ private NotificationChannel backUpAndRestore(NotificationChannel channel) throws Exception {
+ TypedXmlSerializer serializer = Xml.newFastSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+
+ channel.writeXmlForBackup(serializer, mContext);
+ serializer.endDocument();
+ serializer.flush();
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ byte[] byteArray = baos.toByteArray();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
+ parser.nextTag();
+
+ NotificationChannel restoredChannel =
+ new NotificationChannel("default_id", "default_name", 3);
+ restoredChannel.populateFromXmlForRestore(parser, true, mContext);
+
+ return restoredChannel;
+ }
+
+ private NotificationChannel writeToAndReadFromParcel(NotificationChannel channel) {
+ Parcel parcel = Parcel.obtain();
+ channel.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return NotificationChannel.CREATOR.createFromParcel(parcel);
}
}
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 84c197d..be93abb 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -111,7 +111,7 @@
void resumeRecording(in IBinder sessionToken, in Bundle params, int userId);
// For playback control
- void startPlayback(in IBinder sessionToken, int userId);
+ void resumePlayback(in IBinder sessionToken, int userId);
void stopPlayback(in IBinder sessionToken, int mode, int userId);
// For broadcast info
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 7b20c39..6b247cc 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -63,7 +63,7 @@
void timeShiftSetMode(int mode);
void timeShiftEnablePositionTracking(boolean enable);
- void startPlayback();
+ void resumePlayback();
void stopPlayback(int mode);
// For the recording session
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 999e2cf..d145397 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -80,7 +80,7 @@
private static final int DO_SET_TV_MESSAGE_ENABLED = 31;
private static final int DO_NOTIFY_TV_MESSAGE = 32;
private static final int DO_STOP_PLAYBACK = 33;
- private static final int DO_START_PLAYBACK = 34;
+ private static final int DO_RESUME_PLAYBACK = 34;
private static final int DO_SET_VIDEO_FROZEN = 35;
private static final int DO_NOTIFY_AD_SESSION_DATA = 36;
@@ -295,8 +295,8 @@
mTvInputSessionImpl.stopPlayback(msg.arg1);
break;
}
- case DO_START_PLAYBACK: {
- mTvInputSessionImpl.startPlayback();
+ case DO_RESUME_PLAYBACK: {
+ mTvInputSessionImpl.resumePlayback();
break;
}
case DO_SET_VIDEO_FROZEN: {
@@ -523,8 +523,8 @@
}
@Override
- public void startPlayback() {
- mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_PLAYBACK));
+ public void resumePlayback() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RESUME_PLAYBACK));
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 8720bfe..672f58b 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -17,6 +17,7 @@
package android.media.tv;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -35,6 +36,7 @@
import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.ad.TvAdManager;
+import android.media.tv.flags.Flags;
import android.media.tv.interactive.TvInteractiveAppManager;
import android.net.Uri;
import android.os.Binder;
@@ -131,7 +133,8 @@
VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION, VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING,
VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD, VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE,
VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID, VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT,
- VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING, VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN})
+ VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING, VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN,
+ VIDEO_UNAVAILABLE_REASON_STOPPED})
public @interface VideoUnavailableReason {}
/** Indicates that this TV message contains watermarking data */
@@ -344,9 +347,9 @@
/**
* Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
* {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
- * it has been stopped by stopPlayback.
- * @hide
+ * it has been stopped by {@link TvView#stopPlayback(int)}.
*/
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19;
/** @hide */
@@ -3616,13 +3619,13 @@
}
}
- void startPlayback() {
+ void resumePlayback() {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
- mService.startPlayback(mToken, mUserId);
+ mService.resumePlayback(mToken, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index a022b1c..6b03041 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.MainThread;
@@ -35,6 +36,7 @@
import android.media.AudioPresentation;
import android.media.PlaybackParams;
import android.media.tv.ad.TvAdManager;
+import android.media.tv.flags.Flags;
import android.media.tv.interactive.TvInteractiveAppService;
import android.net.Uri;
import android.os.AsyncTask;
@@ -1615,20 +1617,20 @@
* <p> Note that this is different form {@link #timeShiftPause()} as should release the
* stream, making it impossible to resume from this position again.
* @param mode
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
public void onStopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
}
/**
- * Starts playback of the Audio, Video, and CC streams.
+ * Resumes playback of the Audio, Video, and CC streams.
*
* <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
- * used after stopping playback. This is used to restart playback from the current position
+ * used after stopping playback. This is used to resume playback from the current position
* in the live broadcast.
- * @hide
*/
- public void onStartPlayback() {
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+ public void onResumePlayback() {
}
/**
@@ -2112,10 +2114,10 @@
}
/**
- * Calls {@link #onStartPlayback()}.
+ * Calls {@link #onResumePlayback()}.
*/
- void startPlayback() {
- onStartPlayback();
+ void resumePlayback() {
+ onResumePlayback();
}
/**
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index cb45661..ffc121e 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,6 +38,7 @@
import android.media.tv.TvInputManager.Session;
import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
import android.media.tv.TvInputManager.SessionCallback;
+import android.media.tv.flags.Flags;
import android.media.tv.interactive.TvInteractiveAppService;
import android.net.Uri;
import android.os.Bundle;
@@ -650,10 +652,10 @@
* <p>The metadata that will continue to be filtered includes the PSI
* (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1.
*
- * <p> Note that this is different form {@link #timeShiftPause()} as this completely drops
+ * <p> Note that this is different from {@link #timeShiftPause()} as this completely drops
* the stream, making it impossible to resume from this position again.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
public void stopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) {
if (mSession != null) {
mSession.stopPlayback(mode);
@@ -661,16 +663,19 @@
}
/**
- * Starts playback of the Audio, Video, and CC streams.
+ * Resumes playback of the Audio, Video, and CC streams.
*
- * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be
- * used after stopping playback. This is used to restart playback from the current position
- * in the live broadcast.
- * @hide
+ * <p> Note that this is different from {@link #timeShiftResume()} as this is intended to
+ * be used after {@link #stopPlayback(int)} has been called. This is used to resume
+ * playback from the current position in the live broadcast.
+
+ * <p> If this is the first time playback should begin, you will need to use
+ * {@link #tune(String, Uri, Bundle)} to begin playback.
*/
- public void startPlayback() {
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+ public void resumePlayback() {
if (mSession != null) {
- mSession.startPlayback();
+ mSession.resumePlayback();
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 31c1d76..638382e 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7955,8 +7955,8 @@
&& mTelecomManager != null) {
try {
return mTelecomManager.isInManagedCall()
- || mTelecomManager.isInSelfManagedCall(
- pkg, UserHandle.getUserHandleForUid(uid));
+ || mTelecomManager.isInSelfManagedCall(pkg,
+ UserHandle.getUserHandleForUid(uid), /* hasCrossUserAccess */ true);
} catch (IllegalStateException ise) {
// Telecom is not ready (this is likely early boot), so there are no calls.
return false;
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1786ac5..6ab4b99 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -308,31 +308,43 @@
return light;
}
+ private VibrationEffect getVibrationForChannel(
+ NotificationChannel channel, VibratorHelper helper, boolean insistent) {
+ if (!channel.shouldVibrate()) {
+ return null;
+ }
+
+ if (Flags.notificationChannelVibrationEffectApi()) {
+ final VibrationEffect vibration = channel.getVibrationEffect();
+ if (vibration != null && helper.areEffectComponentsSupported(vibration)) {
+ // Adjust the vibration's repeat behavior based on the `insistent` property.
+ return vibration.applyRepeatingIndefinitely(insistent, /* loopDelayMs= */ 0);
+ }
+ }
+
+ final long[] vibrationPattern = channel.getVibrationPattern();
+ if (vibrationPattern == null) {
+ return helper.createDefaultVibration(insistent);
+ }
+ return helper.createWaveformVibration(vibrationPattern, insistent);
+ }
+
private VibrationEffect calculateVibration() {
VibratorHelper helper = new VibratorHelper(mContext);
final Notification notification = getSbn().getNotification();
final boolean insistent = (notification.flags & Notification.FLAG_INSISTENT) != 0;
- VibrationEffect defaultVibration = helper.createDefaultVibration(insistent);
- VibrationEffect vibration;
- if (getChannel().shouldVibrate()) {
- vibration = getChannel().getVibrationPattern() == null
- ? defaultVibration
- : helper.createWaveformVibration(getChannel().getVibrationPattern(), insistent);
- } else {
- vibration = null;
- }
+
if (mPreChannelsNotification
&& (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
final boolean useDefaultVibrate =
(notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
if (useDefaultVibrate) {
- vibration = defaultVibration;
- } else {
- vibration = helper.createWaveformVibration(notification.vibrate, insistent);
+ return helper.createDefaultVibration(insistent);
}
+ return helper.createWaveformVibration(notification.vibrate, insistent);
}
- return vibration;
+ return getVibrationForChannel(getChannel(), helper, insistent);
}
private @NonNull AudioAttributes calculateAttributes() {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 1bafcfe..4f3cdbc 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1212,6 +1212,8 @@
if ((conversation.getUserLockedFields() & NotificationChannel.USER_LOCKED_VIBRATION) == 0
&& (!Arrays.equals(oldParent.getVibrationPattern(),
updatedParent.getVibrationPattern())
+ || !Objects.equals(
+ oldParent.getVibrationEffect(), updatedParent.getVibrationEffect())
|| oldParent.shouldVibrate() != updatedParent.shouldVibrate())) {
// enableVibration must be 2nd because setVibrationPattern may toggle it.
conversation.setVibrationPattern(updatedParent.getVibrationPattern());
@@ -1972,6 +1974,7 @@
update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
}
if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
+ || !Objects.equals(original.getVibrationEffect(), update.getVibrationEffect())
|| original.shouldVibrate() != update.shouldVibrate()) {
update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
}
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index 7204d05..8a0e595 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -193,6 +193,11 @@
return createWaveformVibration(mDefaultPattern, insistent);
}
+ /** Returns if a given vibration can be played by the vibrator that does notification buzz. */
+ public boolean areEffectComponentsSupported(VibrationEffect effect) {
+ return mVibrator.areVibrationFeaturesSupported(effect);
+ }
+
@Nullable
private static float[] getFloatArray(Resources resources, int resId) {
TypedArray array = resources.obtainTypedArray(resId);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 089a886..e0a8226 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2095,17 +2095,17 @@
}
@Override
- public void startPlayback(IBinder sessionToken, int userId) {
+ public void resumePlayback(IBinder sessionToken, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
- userId, "stopPlayback");
+ userId, "resumePlayback");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
try {
- getSessionLocked(sessionToken, callingUid, resolvedUserId).startPlayback();
+ getSessionLocked(sessionToken, callingUid, resolvedUserId).resumePlayback();
} catch (RemoteException | SessionNotFoundException e) {
- Slog.e(TAG, "error in startPlayback()", e);
+ Slog.e(TAG, "error in resumePlayback()", e);
}
}
} finally {
diff --git a/services/core/jni/com_android_server_am_OomConnection.cpp b/services/core/jni/com_android_server_am_OomConnection.cpp
index e892d23..49a3ad3 100644
--- a/services/core/jni/com_android_server_am_OomConnection.cpp
+++ b/services/core/jni/com_android_server_am_OomConnection.cpp
@@ -22,13 +22,15 @@
namespace android {
+using namespace ::android::bpf::memevents;
+
// Used to cache the results of the JNI name lookup
static struct {
jclass clazz;
jmethodID ctor;
} sOomKillRecordInfo;
-static memevents::MemEventListener memevent_listener;
+static MemEventListener memevent_listener(MemEventClient::AMS);
/**
* Initialize listening and waiting for new out-of-memory (OOM) events to occur.
@@ -42,25 +44,20 @@
* @throws java.lang.RuntimeException
*/
static jobjectArray android_server_am_OomConnection_waitOom(JNIEnv* env, jobject) {
- const memevents::MemEvent oom_event = memevents::MemEvent::OOM_KILL;
- if (!memevent_listener.registerEvent(oom_event)) {
+ if (!memevent_listener.registerEvent(MEM_EVENT_OOM_KILL)) {
memevent_listener.deregisterAllEvents();
jniThrowRuntimeException(env, "listener failed to register to OOM events");
return nullptr;
}
- memevents::MemEvent event_received;
- do {
- event_received = memevent_listener.listen();
- if (event_received == memevents::MemEvent::ERROR) {
- memevent_listener.deregisterAllEvents();
- jniThrowRuntimeException(env, "listener received error event");
- return nullptr;
- }
- } while (event_received != oom_event);
+ if (!memevent_listener.listen()) {
+ memevent_listener.deregisterAllEvents();
+ jniThrowRuntimeException(env, "listener failed waiting for OOM event");
+ return nullptr;
+ }
- std::vector<memevents::OomKill> oom_events;
- if (!memevent_listener.getOomEvents(oom_events)) {
+ std::vector<mem_event_t> oom_events;
+ if (!memevent_listener.getMemEvents(oom_events)) {
memevent_listener.deregisterAllEvents();
jniThrowRuntimeException(env, "Failed to get OOM events");
return nullptr;
@@ -75,15 +72,23 @@
}
for (int i = 0; i < oom_events.size(); i++) {
- const memevents::OomKill oom_event = oom_events[i];
- jstring process_name = env->NewStringUTF(oom_event.process_name);
+ const mem_event_t mem_event = oom_events[i];
+ if (mem_event.type != MEM_EVENT_OOM_KILL) {
+ memevent_listener.deregisterAllEvents();
+ jniThrowRuntimeException(env, "Received invalid memory event");
+ return java_oom_array;
+ }
+
+ const auto oom_kill = mem_event.event_data.oom_kill;
+
+ jstring process_name = env->NewStringUTF(oom_kill.process_name);
if (process_name == NULL) {
memevent_listener.deregisterAllEvents();
jniThrowRuntimeException(env, "Failed creating java string for process name");
}
jobject java_oom_kill = env->NewObject(sOomKillRecordInfo.clazz, sOomKillRecordInfo.ctor,
- oom_event.timestamp_ms, oom_event.pid, oom_event.uid,
- process_name, oom_event.oom_score_adj);
+ oom_kill.timestamp_ms, oom_kill.pid, oom_kill.uid,
+ process_name, oom_kill.oom_score_adj);
if (java_oom_kill == NULL) {
memevent_listener.deregisterAllEvents();
jniThrowRuntimeException(env, "Failed to create OomKillRecord object");
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2b8bcc7..b79d20a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -269,6 +269,8 @@
"com.android.server.backup.BackupManagerService$Lifecycle";
private static final String APPWIDGET_SERVICE_CLASS =
"com.android.server.appwidget.AppWidgetService";
+ private static final String ARC_NETWORK_SERVICE_CLASS =
+ "com.android.server.arc.net.ArcNetworkService";
private static final String ARC_PERSISTENT_DATA_BLOCK_SERVICE_CLASS =
"com.android.server.arc.persistent_data_block.ArcPersistentDataBlockService";
private static final String ARC_SYSTEM_HEALTH_SERVICE =
@@ -2069,13 +2071,24 @@
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI)) {
// Wifi Service must be started first for wifi-related services.
- t.traceBegin("StartWifi");
- mSystemServiceManager.startServiceFromJar(
- WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
- t.traceEnd();
- t.traceBegin("StartWifiScanning");
- mSystemServiceManager.startServiceFromJar(
- WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+ if (!isArc) {
+ t.traceBegin("StartWifi");
+ mSystemServiceManager.startServiceFromJar(
+ WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+ t.traceEnd();
+ t.traceBegin("StartWifiScanning");
+ mSystemServiceManager.startServiceFromJar(
+ WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+ t.traceEnd();
+ }
+ }
+
+ // ARC - ArcNetworkService registers the ARC network stack and replaces the
+ // stock WiFi service in both ARC++ container and ARCVM. Always starts the ARC network
+ // stack regardless of whether FEATURE_WIFI is enabled/disabled (b/254755875).
+ if (isArc) {
+ t.traceBegin("StartArcNetworking");
+ mSystemServiceManager.startService(ARC_NETWORK_SERVICE_CLASS);
t.traceEnd();
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index df314c2..8261dee 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -11686,7 +11686,7 @@
// style + self managed call - bypasses block
when(mTelecomManager.isInSelfManagedCall(
- r.getSbn().getPackageName(), r.getUser())).thenReturn(true);
+ r.getSbn().getPackageName(), r.getUser(), true)).thenReturn(true);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
@@ -11769,7 +11769,7 @@
// style + self managed call - bypasses block
mService.clearNotifications();
reset(mUsageStats);
- when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser()))
+ when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser(), true))
.thenReturn(true);
mService.addEnqueuedNotification(r);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 670d097..130a8ca 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -66,6 +66,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
+import android.os.VibrationEffect;
import android.os.Vibrator;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -114,11 +115,13 @@
NotificationManager.IMPORTANCE_UNSPECIFIED);
private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
- private static final long[] CUSTOM_VIBRATION = new long[] {
+ private static final long[] CUSTOM_NOTIFICATION_VIBRATION = new long[] {
300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400,
300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400 };
- private static final long[] CUSTOM_CHANNEL_VIBRATION = new long[] {300, 400, 300, 400 };
+ private static final long[] CUSTOM_CHANNEL_VIBRATION_PATTERN = new long[] {300, 400, 300, 400 };
+ private static final VibrationEffect CUSTOM_CHANNEL_VIBRATION_EFFECT =
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
private static final Uri CUSTOM_SOUND = Settings.System.DEFAULT_ALARM_ALERT_URI;
private static final AudioAttributes CUSTOM_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
@@ -135,6 +138,7 @@
MockitoAnnotations.initMocks(this);
when(mMockContext.getSystemService(eq(Vibrator.class))).thenReturn(mVibrator);
+ when(mVibrator.areVibrationFeaturesSupported(any())).thenReturn(true);
final Resources res = mContext.getResources();
when(mMockContext.getResources()).thenReturn(res);
when(mMockContext.getPackageManager()).thenReturn(mPm);
@@ -168,8 +172,8 @@
if (defaultVibration) {
defaults |= Notification.DEFAULT_VIBRATE;
} else {
- builder.setVibrate(CUSTOM_VIBRATION);
- channel.setVibrationPattern(CUSTOM_CHANNEL_VIBRATION);
+ builder.setVibrate(CUSTOM_NOTIFICATION_VIBRATION);
+ channel.setVibrationPattern(CUSTOM_CHANNEL_VIBRATION_PATTERN);
}
}
if (lights) {
@@ -217,26 +221,39 @@
return new StatusBarNotification(mPkg, mPkg, id1, tag1, uid, uid, n, mUser, null, uid);
}
+ private StatusBarNotification getNotification(
+ long[] channelVibrationPattern,
+ VibrationEffect channelVibrationEffect,
+ boolean insistent) {
+ if (channelVibrationPattern != null) {
+ channel.setVibrationPattern(channelVibrationPattern);
+ } else if (channelVibrationEffect != null) {
+ channel.setVibrationEffect(channelVibrationEffect);
+ }
- private StatusBarNotification getInsistentNotification(boolean defaultVibration) {
final Builder builder = new Builder(mMockContext)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
- .setPriority(Notification.PRIORITY_HIGH);
- int defaults = 0;
- if (defaultVibration) {
- defaults |= Notification.DEFAULT_VIBRATE;
- } else {
- builder.setVibrate(CUSTOM_VIBRATION);
- channel.setVibrationPattern(CUSTOM_CHANNEL_VIBRATION);
- }
- builder.setDefaults(defaults);
- builder.setFlag(Notification.FLAG_INSISTENT, true);
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setVibrate(CUSTOM_NOTIFICATION_VIBRATION)
+ .setFlag(Notification.FLAG_INSISTENT, insistent);
Notification n = builder.build();
return new StatusBarNotification(mPkg, mPkg, id1, tag1, uid, uid, n, mUser, null, uid);
}
+ private StatusBarNotification getNotification(
+ VibrationEffect channelVibrationEffect, boolean insistent) {
+ return getNotification(
+ /* channelVibrationPattern= */ null, channelVibrationEffect, insistent);
+ }
+
+ private StatusBarNotification getNotification(
+ long[] channelVibrationPattern, boolean insistent) {
+ return getNotification(
+ channelVibrationPattern, /* channelVibrationEffect= */ null, insistent);
+ }
+
private StatusBarNotification getMessagingStyleNotification() {
return getMessagingStyleNotification(mPkg);
}
@@ -344,7 +361,7 @@
NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(VibratorHelper.createWaveformVibration(
- CUSTOM_VIBRATION, /* insistent= */ false), record.getVibration());
+ CUSTOM_NOTIFICATION_VIBRATION, /* insistent= */ false), record.getVibration());
}
@Test
@@ -358,30 +375,137 @@
NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertNotEquals(VibratorHelper.createWaveformVibration(
- CUSTOM_VIBRATION, /* insistent= */ false), record.getVibration());
+ CUSTOM_NOTIFICATION_VIBRATION, /* insistent= */ false), record.getVibration());
}
@Test
- public void testVibration_custom_upgradeUsesChannel() {
+ public void testVibration_customPattern_nonInsistent_usesCustomPattern() {
channel.enableVibration(true);
- // post upgrade, custom vibration.
- StatusBarNotification sbn = getNotification(PKG_O, false /* noisy */,
- false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
- false /* lights */, false /* defaultLights */, null /* group */);
+ StatusBarNotification sbn = getNotification(
+ CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ false);
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(VibratorHelper.createWaveformVibration(
- CUSTOM_CHANNEL_VIBRATION, /* insistent= */ false), record.getVibration());
+ CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ false), record.getVibration());
}
@Test
- public void testVibration_insistent_createsInsistentVibrationEffect() {
+ public void testVibration_customPattern_insistent_createsInsistentEffect() {
channel.enableVibration(true);
- StatusBarNotification sbn = getInsistentNotification(false /* defaultBuzz */);
+ StatusBarNotification sbn = getNotification(
+ CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ true);
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(VibratorHelper.createWaveformVibration(
- CUSTOM_CHANNEL_VIBRATION, /* insistent= */ true), record.getVibration());
+ CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ true), record.getVibration());
+ }
+
+ @Test
+ public void testVibration_customEffect_flagNotEnabled_usesDefaultEffect() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ channel.enableVibration(true);
+ StatusBarNotification sbn = getNotification(
+ CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ false);
+
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ VibrationEffect effect = record.getVibration();
+ assertNotEquals(effect, CUSTOM_CHANNEL_VIBRATION_EFFECT);
+ assertNotNull(effect);
+ }
+
+ @Test
+ public void testVibration_customEffect_effectNotSupported_usesDefaultEffect() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ when(mVibrator.areVibrationFeaturesSupported(any())).thenReturn(false);
+ StatusBarNotification sbn = getNotification(
+ CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ false);
+
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ VibrationEffect effect = record.getVibration();
+ assertNotEquals(effect, CUSTOM_CHANNEL_VIBRATION_EFFECT);
+ assertNotNull(effect);
+ }
+
+ @Test
+ public void testVibration_customNonRepeatingEffect_nonInsistent_usesCustomEffect() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ StatusBarNotification sbn = getNotification(
+ CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ false);
+
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ assertEquals(CUSTOM_CHANNEL_VIBRATION_EFFECT, record.getVibration());
+ }
+
+ @Test
+ public void testVibration_customNonRepeatingEffect_insistent_createsInsistentEffect() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ StatusBarNotification sbn = getNotification(
+ CUSTOM_CHANNEL_VIBRATION_EFFECT, /* insistent= */ true);
+
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ VibrationEffect repeatingEffect =
+ CUSTOM_CHANNEL_VIBRATION_EFFECT
+ .applyRepeatingIndefinitely(true, /* loopDelayMs= */ 0);
+ assertEquals(repeatingEffect, record.getVibration());
+ }
+
+ @Test
+ public void testVibration_customRepeatingEffect_nonInsistent_createsNonRepeatingCustomEffect() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ VibrationEffect repeatingEffect =
+ CUSTOM_CHANNEL_VIBRATION_EFFECT
+ .applyRepeatingIndefinitely(true, /* loopDelayMs= */ 0);
+ StatusBarNotification sbn = getNotification(repeatingEffect, /* insistent= */ false);
+
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ assertEquals(CUSTOM_CHANNEL_VIBRATION_EFFECT, record.getVibration());
+ }
+
+ @Test
+ public void testVibration_customRepeatingEffect_insistent_usesCustomEffect() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ VibrationEffect repeatingEffect =
+ CUSTOM_CHANNEL_VIBRATION_EFFECT
+ .applyRepeatingIndefinitely(true, /* loopDelayMs= */ 0);
+ StatusBarNotification sbn = getNotification(repeatingEffect, /* insistent= */ true);
+
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ assertEquals(repeatingEffect, record.getVibration());
+ }
+
+ @Test
+ public void testVibration_noCustomVibration_vibrationEnabled_usesDefaultVibration() {
+ channel.enableVibration(true);
+ StatusBarNotification sbn = getNotification(
+ /* channelVibrationPattern= */ null,
+ /* channelVibrationEffect= */ null,
+ /* insistent= */ false);
+
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ assertNotNull(record.getVibration());
+ }
+
+ @Test
+ public void testVibration_noCustomVibration_vibrationNotEnabled_usesNoVibration() {
+ channel.enableVibration(false);
+ StatusBarNotification sbn = getNotification(
+ /* channelVibrationPattern= */ null,
+ /* channelVibrationEffect= */ null,
+ /* insistent= */ false);
+
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ assertNull(record.getVibration());
+ }
+
+ @Test
+ public void testVibration_customVibration_vibrationNotEnabled_usesNoVibration() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API);
+ StatusBarNotification sbn = getNotification(
+ CUSTOM_CHANNEL_VIBRATION_PATTERN, /* insistent= */ false);
+ channel.enableVibration(false);
+
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ assertNull(record.getVibration());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 5ddac03..8b55778 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -391,6 +391,7 @@
assertEquals(expected.getSound(), actual.getSound());
assertEquals(expected.canBypassDnd(), actual.canBypassDnd());
assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
+ assertEquals(expected.getVibrationEffect(), actual.getVibrationEffect());
assertEquals(expected.getGroup(), actual.getGroup());
assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
assertEquals(expected.getLightColor(), actual.getLightColor());
@@ -410,6 +411,7 @@
assertEquals(parent.getSound(), actual.getSound());
assertEquals(parent.canBypassDnd(), actual.canBypassDnd());
assertTrue(Arrays.equals(parent.getVibrationPattern(), actual.getVibrationPattern()));
+ assertEquals(parent.getVibrationEffect(), actual.getVibrationEffect());
assertEquals(parent.getGroup(), actual.getGroup());
assertEquals(parent.getAudioAttributes(), actual.getAudioAttributes());
assertEquals(parent.getLightColor(), actual.getLightColor());
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 9ee48d8..1df6cf7 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -1017,9 +1017,11 @@
/**
* Connection event used to communicate a {@link android.telephony.CallQuality} report from
* telephony to Telecom for relaying to
- * {@link DiagnosticCall#onCallQualityReceived(CallQuality)}.
+ * {@link CallDiagnostics#onCallQualityReceived(CallQuality)}.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
public static final String EVENT_CALL_QUALITY_REPORT =
"android.telecom.event.CALL_QUALITY_REPORT";
@@ -1028,6 +1030,8 @@
* {@link android.telephony.CallQuality} data.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
public static final String EXTRA_CALL_QUALITY_REPORT =
"android.telecom.extra.CALL_QUALITY_REPORT";
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 2c6e1e4..f8e8529 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2752,17 +2752,23 @@
*
* @param packageName the package name of the app to check calls for.
* @param userHandle the user handle on which to check for calls.
+ * @param hasCrossUserAccess indicates if calls should be detected across all users.
* @return {@code true} if there are ongoing calls, {@code false} otherwise.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.INTERACT_ACROSS_USERS
+ })
public boolean isInSelfManagedCall(@NonNull String packageName,
- @NonNull UserHandle userHandle) {
+ @NonNull UserHandle userHandle, boolean hasCrossUserAccess) {
ITelecomService service = getTelecomService();
if (service != null) {
try {
return service.isInSelfManagedCall(packageName, userHandle,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), hasCrossUserAccess);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException isInSelfManagedCall: " + e);
e.rethrowFromSystemServer();
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index f1bfd22..412e827 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -395,7 +395,7 @@
* @see TelecomServiceImpl#isInSelfManagedCall
*/
boolean isInSelfManagedCall(String packageName, in UserHandle userHandle,
- String callingPackage);
+ String callingPackage, boolean hasCrossUserAccess);
/**
* @see TelecomServiceImpl#addCall
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 287aa65..7607c64 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -15,4 +15,4 @@
per-file CarrierConfigManager.java=amruthr@google.com,tgunn@google.com,rgreenwalt@google.com,satk@google.com
#Domain Selection is jointly owned, add additional owners for domain selection specific files
-per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com
+per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com,jdyou@google.com
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index 5434c82..5f1bc87 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -67,8 +67,14 @@
assertEquals("keyCharacterMap not equal", keyCharacterMap, outKeyCharacterMap);
for (int j = 0; j < device.getMotionRanges().size(); j++) {
- assertMotionRangeEquals(device.getMotionRanges().get(j),
- outDevice.getMotionRanges().get(j));
+ InputDevice.MotionRange motionRange = device.getMotionRanges().get(j);
+ assertMotionRangeEquals(motionRange, outDevice.getMotionRanges().get(j));
+
+ int axis = motionRange.getAxis();
+ int source = motionRange.getSource();
+ assertEquals(
+ device.getViewBehavior().shouldSmoothScroll(axis, source),
+ outDevice.getViewBehavior().shouldSmoothScroll(axis, source));
}
}
@@ -93,7 +99,8 @@
.setHasBattery(true)
.setKeyboardLanguageTag("en-US")
.setKeyboardLayoutType("qwerty")
- .setUsiVersion(new HostUsiVersion(2, 0));
+ .setUsiVersion(new HostUsiVersion(2, 0))
+ .setShouldSmoothScroll(true);
for (int i = 0; i < 30; i++) {
deviceBuilder.addMotionRange(