Merge "Camera: Fix HTML doc issue" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index b17e3343..8fd50cc 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();
@@ -27318,6 +27320,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 +27412,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 +27420,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 +27555,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 +27568,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();
@@ -27748,6 +27755,7 @@
method public void onRecordingTuned(@NonNull String, @NonNull android.net.Uri);
method public abstract void onRelease();
method public void onResetInteractiveApp();
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onSelectedTrackInfo(@NonNull java.util.List<android.media.tv.TvTrackInfo>);
method public abstract boolean onSetSurface(@Nullable android.view.Surface);
method public void onSetTeletextAppEnabled(boolean);
method public void onSignalStrength(int);
@@ -27782,6 +27790,7 @@
method @CallSuper public void requestCurrentVideoBounds();
method @CallSuper public void requestScheduleRecording(@NonNull String, @NonNull String, @NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.os.Bundle);
method @CallSuper public void requestScheduleRecording(@NonNull String, @NonNull String, @NonNull android.net.Uri, long, long, int, @NonNull android.os.Bundle);
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") @CallSuper public void requestSelectedTrackInfo();
method @CallSuper public void requestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
method @CallSuper public void requestStartRecording(@NonNull String, @Nullable android.net.Uri);
method @CallSuper public void requestStopRecording(@NonNull String);
@@ -27846,6 +27855,7 @@
method public void sendCurrentChannelUri(@Nullable android.net.Uri);
method public void sendCurrentTvInputId(@Nullable String);
method public void sendCurrentVideoBounds(@NonNull android.graphics.Rect);
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void sendSelectedTrackInfo(@Nullable java.util.List<android.media.tv.TvTrackInfo>);
method public void sendSigningResult(@NonNull String, @NonNull byte[]);
method public void sendStreamVolume(float);
method public void sendTimeShiftMode(int);
@@ -27881,6 +27891,7 @@
method public void onRequestCurrentVideoBounds(@NonNull String);
method public void onRequestScheduleRecording(@NonNull String, @NonNull String, @NonNull String, @NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.os.Bundle);
method public void onRequestScheduleRecording(@NonNull String, @NonNull String, @NonNull String, @NonNull android.net.Uri, long, long, int, @NonNull android.os.Bundle);
+ method @FlaggedApi("android.media.tv.flags.tiaf_v_apis") public void onRequestSelectedTrackInfo(@NonNull String);
method public void onRequestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
method public void onRequestStartRecording(@NonNull String, @NonNull String, @Nullable android.net.Uri);
method public void onRequestStopRecording(@NonNull String, @NonNull String);
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/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 8b8576a..b5e5074 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -721,28 +721,28 @@
/**
* Returns whether the device is currently locked for the user.
* <p>
- * This returns the device locked state for the {@link Context}'s user. If this user is the
- * current user, then the device is considered "locked" when the lock screen is showing (i.e.
- * {@link #isKeyguardLocked()} returns {@code true}) and is not trivially dismissible (e.g. with
- * swipe), and the user has a PIN, pattern, or password.
+ * This method returns the device locked state for the {@link Context}'s user. The device is
+ * considered to be locked for a user when the user's apps are currently inaccessible and some
+ * form of lock screen authentication is required to regain access to them. The lock screen
+ * authentication typically uses PIN, pattern, password, or biometric. Some devices may support
+ * additional methods, such as unlock using a paired smartwatch. "Swipe" does not count as
+ * authentication; if the lock screen is dismissible with swipe, for example due to the lock
+ * screen being set to Swipe or due to the device being kept unlocked by being near a trusted
+ * bluetooth device or in a trusted location, the device is considered unlocked.
+ * <div class="note">
* <p>
- * Note: the above definition implies that a user with no PIN, pattern, or password is never
- * considered locked, even if the lock screen is showing and requesting a SIM card PIN. The
- * device PIN and SIM PIN are separate. Also, the user is not considered locked if face
- * authentication has just completed or a trust agent is keeping the device unlocked, since in
- * these cases the lock screen is dismissible with swipe.
+ * <b>Note:</b> In the case of multiple full users, each user can have their own lock screen
+ * authentication configured. The device-locked state may differ between different users. For
+ * example, the device may be unlocked for the current user, but locked for a non-current user
+ * if lock screen authentication would be required to access that user's apps after switching to
+ * that user.
* <p>
- * For a user that is not the current user but can be switched to (usually this means "another
- * full user"), and that has a PIN, pattern, or password, the device is always considered
- * locked.
- * <p>
- * For a profile with a unified challenge, the device locked state is the same as that of the
- * parent user.
- * <p>
- * For a profile with a separate challenge, the device becomes unlocked when the profile's PIN,
- * pattern, password, or biometric is verified. It becomes locked when the parent user becomes
- * locked, the screen turns off, the device reboots, the device policy controller locks the
- * profile, or the timeout set by the device policy controller expires.
+ * In the case of a profile, when the device goes to the main lock screen, up to two layers of
+ * authentication may be required to regain access to the profile's apps: one to unlock the main
+ * lock screen, and one to unlock the profile (when a separate profile challenge is required).
+ * For a profile, the device is considered to be locked as long as any challenge remains, either
+ * the parent user's challenge (when applicable) or the profile's challenge (when applicable).
+ * </div>
*
* @return {@code true} if the device is currently locked for the user
* @see #isKeyguardLocked()
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/contextualsearch/OWNERS b/core/java/android/app/contextualsearch/OWNERS
new file mode 100644
index 0000000..0c2612c
--- /dev/null
+++ b/core/java/android/app/contextualsearch/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/contextualsearch/OWNERS
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/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index e9b94c9..87fb843 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -185,7 +185,7 @@
*
* @param context A Context object which should be some mock instance (like the
* instance of {@link android.test.mock.MockContext}).
- * @param readPermission The read permision you want this instance should have in the
+ * @param readPermission The read permission you want this instance should have in the
* test, which is available via {@link #getReadPermission()}.
* @param writePermission The write permission you want this instance should have
* in the test, which is available via {@link #getWritePermission()}.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9253998..a126363 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -387,7 +387,7 @@
* {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
* <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in
* the arguments {@link Bundle}, the Content framework will attempt to
- * synthesize an QUERY_ARG_SQL* argument using the corresponding
+ * synthesize a QUERY_ARG_SQL* argument using the corresponding
* QUERY_ARG_SORT* values.
*/
public static final String QUERY_ARG_SORT_COLUMNS = "android:query-arg-sort-columns";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8744eae..bc29f8b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4479,6 +4479,10 @@
* the Android Keystore backed by an isolated execution environment. The version indicates
* which features are implemented in the isolated execution environment:
* <ul>
+ * <li>300: Ability to include a second IMEI in the ID attestation record, see
+ * {@link android.app.admin.DevicePolicyManager#ID_TYPE_IMEI}.
+ * <li>200: Hardware support for Curve 25519 (including both Ed25519 signature generation and
+ * X25519 key agreement).
* <li>100: Hardware support for ECDH (see {@link javax.crypto.KeyAgreement}) and support
* for app-generated attestation keys (see {@link
* android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias(String)}).
@@ -4508,6 +4512,11 @@
* StrongBox</a>. If this feature has a version, the version number indicates which features are
* implemented in StrongBox:
* <ul>
+ * <li>300: Ability to include a second IMEI in the ID attestation record, see
+ * {@link android.app.admin.DevicePolicyManager#ID_TYPE_IMEI}.
+ * <li>200: No new features for StrongBox (the Android Keystore environment backed by an
+ * isolated execution environment has gained support for Curve 25519 in this version, but
+ * the implementation backed by a dedicated secure processor is not expected to implement it).
* <li>100: Hardware support for ECDH (see {@link javax.crypto.KeyAgreement}) and support
* for app-generated attestation keys (see {@link
* android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias(String)}).
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index c3a09ae..e8d5d37 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -20,6 +20,7 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.content.ComponentName;
@@ -49,7 +50,7 @@
public static Intent createCredentialSelectorIntent(
@NonNull RequestInfo requestInfo,
@SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
- @NonNull
+ @Nullable
ArrayList<ProviderData> enabledProviderDataList,
@SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
@NonNull
@@ -57,23 +58,30 @@
@NonNull ResultReceiver resultReceiver,
boolean isRequestForAllOptions) {
- Intent intent = createCredentialSelectorIntent(requestInfo, enabledProviderDataList,
- disabledProviderDataList, resultReceiver);
+ Intent intent;
+ if (enabledProviderDataList != null) {
+ intent = createCredentialSelectorIntent(requestInfo, enabledProviderDataList,
+ disabledProviderDataList, resultReceiver);
+ } else {
+ intent = createCredentialSelectorIntent(requestInfo,
+ disabledProviderDataList, resultReceiver);
+ }
intent.putExtra(Constants.EXTRA_REQ_FOR_ALL_OPTIONS, isRequestForAllOptions);
return intent;
}
- /** Generate a new launch intent to the Credential Selector UI. */
+ /**
+ * Generate a new launch intent to the Credential Selector UI.
+ *
+ * @hide
+ */
@NonNull
public static Intent createCredentialSelectorIntent(
@NonNull RequestInfo requestInfo,
@SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
- @NonNull
- ArrayList<ProviderData> enabledProviderDataList,
- @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
- @NonNull
- ArrayList<DisabledProviderData> disabledProviderDataList,
+ @NonNull
+ ArrayList<DisabledProviderData> disabledProviderDataList,
@NonNull ResultReceiver resultReceiver) {
Intent intent = new Intent();
ComponentName componentName =
@@ -83,9 +91,6 @@
com.android.internal.R.string
.config_credentialManagerDialogComponent));
intent.setComponent(componentName);
-
- intent.putParcelableArrayListExtra(
- ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, enabledProviderDataList);
intent.putParcelableArrayListExtra(
ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST, disabledProviderDataList);
intent.putExtra(RequestInfo.EXTRA_REQUEST_INFO, requestInfo);
@@ -95,6 +100,24 @@
return intent;
}
+ /** Generate a new launch intent to the Credential Selector UI. */
+ @NonNull
+ public static Intent createCredentialSelectorIntent(
+ @NonNull RequestInfo requestInfo,
+ @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
+ @NonNull
+ ArrayList<ProviderData> enabledProviderDataList,
+ @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
+ @NonNull
+ ArrayList<DisabledProviderData> disabledProviderDataList,
+ @NonNull ResultReceiver resultReceiver) {
+ Intent intent = createCredentialSelectorIntent(requestInfo,
+ disabledProviderDataList, resultReceiver);
+ intent.putParcelableArrayListExtra(
+ ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, enabledProviderDataList);
+ return intent;
+ }
+
/**
* Creates an Intent that cancels any UI matching the given request token id.
*
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index cb1d3f5..3b7ade2 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -511,7 +511,7 @@
Bundle getExtras();
/**
- * This is an out-of-band way for the the user of a cursor to communicate with the cursor. The
+ * This is an out-of-band way for the user of a cursor to communicate with the cursor. The
* structure of each bundle is entirely defined by the cursor.
*
* <p>One use of this is to tell a cursor that it should retry its network request after it
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index f3496e7..b1ef05a 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -71,7 +71,7 @@
*/
public class ZygoteProcess {
- private static final int ZYGOTE_CONNECT_TIMEOUT_MS = 20000;
+ private static final int ZYGOTE_CONNECT_TIMEOUT_MS = 60000;
/**
* Use a relatively short delay, because for app zygote, this is in the critical path of
diff --git a/core/java/android/service/contextualsearch/OWNERS b/core/java/android/service/contextualsearch/OWNERS
new file mode 100644
index 0000000..463adf4
--- /dev/null
+++ b/core/java/android/service/contextualsearch/OWNERS
@@ -0,0 +1,3 @@
+srazdan@google.com
+volnov@google.com
+hackz@google.com
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/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 59ec605..9db1060 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -162,6 +162,12 @@
public float alpha;
/**
+ * Sets a property on this window indicating that its visible region should be considered when
+ * computing TrustedPresentation Thresholds.
+ */
+ public boolean canOccludePresentation;
+
+ /**
* The input token for the window to which focus should be transferred when this input window
* can be successfully focused. If null, this input window will not transfer its focus to
* any other window.
@@ -205,6 +211,7 @@
focusTransferTarget = other.focusTransferTarget;
contentSize = new Size(other.contentSize.getWidth(), other.contentSize.getHeight());
alpha = other.alpha;
+ canOccludePresentation = other.canOccludePresentation;
}
@Override
@@ -219,6 +226,7 @@
.append(", isClone=").append((inputConfig & InputConfig.CLONE) != 0)
.append(", contentSize=").append(contentSize)
.append(", alpha=").append(alpha)
+ .append(", canOccludePresentation=").append(canOccludePresentation)
.toString();
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3ed0385..3c0ac06 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -168,6 +168,8 @@
boolean isTrustedOverlay);
private static native void nativeSetDropInputMode(
long transactionObj, long nativeObject, int flags);
+ private static native void nativeSetCanOccludePresentation(long transactionObj,
+ long nativeObject, boolean canOccludePresentation);
private static native void nativeSurfaceFlushJankData(long nativeSurfaceObject);
private static native boolean nativeClearContentFrameStats(long nativeObject);
private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats);
@@ -589,6 +591,28 @@
public static final int DISPLAY_DECORATION = 0x00000200;
/**
+ * Ignore any destination frame set on the layer. This is used when the buffer scaling mode
+ * is freeze and the destination frame is applied asynchronously with the buffer submission.
+ * This is needed to maintain compatibility for SurfaceView scaling behavior.
+ * See SurfaceView scaling behavior for more details.
+ * @hide
+ */
+ public static final int IGNORE_DESTINATION_FRAME = 0x00000400;
+
+ /**
+ * Special casing for layer that is a refresh rate indicator
+ * @hide
+ */
+ public static final int LAYER_IS_REFRESH_RATE_INDICATOR = 0x00000800;
+
+ /**
+ * Sets a property on this layer indicating that its visible region should be considered when
+ * computing TrustedPresentation Thresholds
+ * @hide
+ */
+ public static final int CAN_OCCLUDE_PRESENTATION = 0x00001000;
+
+ /**
* Surface creation flag: Creates a surface where color components are interpreted
* as "non pre-multiplied" by their alpha channel. Of course this flag is
* meaningless for surfaces without an alpha channel. By default
@@ -4163,6 +4187,29 @@
}
/**
+ * Sets a property on this SurfaceControl and all its children indicating that the visible
+ * region of this SurfaceControl should be considered when computing TrustedPresentation
+ * Thresholds.
+ * <p>
+ * API Guidance:
+ * The goal of this API is to identify windows that can be used to occlude content on
+ * another window. This includes windows controlled by the user or the system. If the window
+ * is transient, like Toast or notification shade, the window should not set this flag since
+ * the user or the app cannot use the window to occlude content in a persistent manner. All
+ * apps should have this flag set.
+ * <p>
+ * The caller must hold the ACCESS_SURFACE_FLINGER permission.
+ * @hide
+ */
+ public Transaction setCanOccludePresentation(SurfaceControl sc,
+ boolean canOccludePresentation) {
+ checkPreconditions(sc);
+ final int value = (canOccludePresentation) ? CAN_OCCLUDE_PRESENTATION : 0;
+ nativeSetFlags(mNativeObject, sc.mNativeObject, value, CAN_OCCLUDE_PRESENTATION);
+ return this;
+ }
+
+ /**
* Sends a flush jank data transaction for the given surface.
* @hide
*/
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1fdd1a5..f54ef38 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -425,12 +425,12 @@
int mMotionViewNewTop;
/**
- * The X value associated with the the down motion event
+ * The X value associated with the down motion event
*/
int mMotionX;
/**
- * The Y value associated with the the down motion event
+ * The Y value associated with the down motion event
*/
@UnsupportedAppUsage
int mMotionY;
@@ -7381,7 +7381,7 @@
scrap.dispatchStartTemporaryDetach();
- // The the accessibility state of the view may change while temporary
+ // the accessibility state of the view may change while temporary
// detached and we do not allow detached views to fire accessibility
// events. So we are announcing that the subtree changed giving a chance
// to clients holding on to a view in this subtree to refresh it.
@@ -7750,7 +7750,7 @@
}
/**
- * Abstract positon scroller used to handle smooth scrolling.
+ * Abstract position scroller used to handle smooth scrolling.
*/
static abstract class AbsPositionScroller {
public abstract void start(int position);
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index 76e97ad..3b7e1e9 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -170,7 +170,7 @@
* @see android.view.View#measure(int, int)
*
* Figure out the dimensions of this Spinner. The width comes from
- * the widthMeasureSpec as Spinnners can't have their width set to
+ * the widthMeasureSpec as Spinners can't have their width set to
* UNSPECIFIED. The height is based on the height of the selected item
* plus padding.
*/
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index e20357fa..1dc90ed 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -15,3 +15,5 @@
per-file Remote* = file:../appwidget/OWNERS
per-file Toast.java = juliacr@google.com, jeffdq@google.com
+
+per-file flags/notification_widget_flags.aconfig = juliacr@google.com, jeffdq@google.com
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index f234637..14fb17c 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -84,6 +84,14 @@
}
flag {
+ name: "delegate_unhandled_drags"
+ namespace: "multitasking"
+ description: "Enables delegating unhandled drags to SystemUI"
+ bug: "320797628"
+ is_fixed_read_only: true
+}
+
+flag {
name: "insets_decoupled_configuration"
namespace: "windowing_frontend"
description: "Configuration decoupled from insets"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 4a6e8d7..2e20cce 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -16,7 +16,7 @@
namespace: "windowing_sdk"
name: "activity_embedding_overlay_presentation_flag"
description: "Whether the overlay presentation feature is enabled"
- bug: "243518738"
+ bug: "293370683"
}
flag {
@@ -30,7 +30,7 @@
namespace: "windowing_sdk"
name: "fullscreen_dim_flag"
description: "Whether to allow showing fullscreen dim on ActivityEmbedding split"
- bug: "253533308"
+ bug: "293797706"
}
flag {
@@ -44,7 +44,7 @@
namespace: "windowing_sdk"
name: "untrusted_embedding_any_app_permission"
description: "Feature flag to enable the permission to embed any app in untrusted mode."
- bug: "289199433"
+ bug: "293647332"
is_fixed_read_only: true
}
@@ -60,5 +60,5 @@
namespace: "windowing_sdk"
name: "embedded_activity_back_nav_flag"
description: "Refines embedded activity back navigation behavior"
- bug: "240575809"
+ bug: "293642394"
}
\ No newline at end of file
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index ae23942..bed7768 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -75,6 +75,7 @@
jfieldID windowToken;
jfieldID focusTransferTarget;
jfieldID alpha;
+ jfieldID canOccludePresentation;
} gInputWindowHandleClassInfo;
static struct {
@@ -327,6 +328,8 @@
javaObjectForIBinder(env, windowInfo.windowToken));
env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.alpha, windowInfo.alpha);
+ env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.canOccludePresentation,
+ windowInfo.canOccludePresentation);
return inputWindowHandle;
}
@@ -451,6 +454,9 @@
GET_FIELD_ID(gInputWindowHandleClassInfo.alpha, clazz, "alpha", "F");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.canOccludePresentation, clazz,
+ "canOccludePresentation", "Z");
+
jclass surfaceControlClazz;
FIND_CLASS(surfaceControlClazz, "android/view/SurfaceControl");
GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject,
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 2f29cae..917d283 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -17,6 +17,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp
index b9c36b9..447b8ec 100644
--- a/core/jni/android_opengl_EGL15.cpp
+++ b/core/jni/android_opengl_EGL15.cpp
@@ -17,6 +17,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp
index cdc9852..ffd75ea 100644
--- a/core/jni/android_opengl_EGLExt.cpp
+++ b/core/jni/android_opengl_EGLExt.cpp
@@ -17,6 +17,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
@@ -54,11 +55,11 @@
jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface");
eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal);
jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync");
- eglsyncClass = (jclass)_env->NewGlobalRef(eglsyncClassLocal);
+ eglsyncClass = (jclass) _env->NewGlobalRef(eglsyncClassLocal);
egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
- eglsyncGetHandleID = _env->GetMethodID(eglsyncClassLocal, "getNativeHandle", "()J");
+ eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
}
static void *
@@ -72,6 +73,14 @@
return reinterpret_cast<void*>(_env->CallLongMethod(obj, mid));
}
+// TODO: this should be generated from the .spec file, but needs to be renamed and made private
+static jint android_eglDupNativeFenceFDANDROID(JNIEnv *env, jobject, jobject dpy, jobject sync) {
+ EGLDisplay dpy_native = (EGLDisplay)fromEGLHandle(env, egldisplayGetHandleID, dpy);
+ EGLSync sync_native = (EGLSync)fromEGLHandle(env, eglsyncGetHandleID, sync);
+
+ return eglDupNativeFenceFDANDROID(dpy_native, sync_native);
+}
+
// --------------------------------------------------------------------------
/* EGLBoolean eglPresentationTimeANDROID ( EGLDisplay dpy, EGLSurface sur, EGLnsecsANDROID time ) */
static jboolean
@@ -89,21 +98,12 @@
return (jboolean)_returnValue;
}
-static jint android_eglDupNativeFenceFDANDROID(JNIEnv *env, jobject, jobject dpy, jobject sync) {
- EGLDisplay dpy_native = (EGLDisplay)fromEGLHandle(env, egldisplayGetHandleID, dpy);
- EGLSync sync_native = (EGLSync)fromEGLHandle(env, eglsyncGetHandleID, sync);
-
- return eglDupNativeFenceFDANDROID(dpy_native, sync_native);
-}
-
static const char *classPathName = "android/opengl/EGLExt";
static const JNINativeMethod methods[] = {
- {"_nativeClassInit", "()V", (void *)nativeClassInit},
- {"eglPresentationTimeANDROID", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;J)Z",
- (void *)android_eglPresentationTimeANDROID},
- {"eglDupNativeFenceFDANDROIDImpl", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;)I",
- (void *)android_eglDupNativeFenceFDANDROID},
+{"_nativeClassInit", "()V", (void*)nativeClassInit },
+{"eglPresentationTimeANDROID", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSurface;J)Z", (void *) android_eglPresentationTimeANDROID },
+{"eglDupNativeFenceFDANDROIDImpl", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;)I", (void *)android_eglDupNativeFenceFDANDROID },
};
int register_android_opengl_jni_EGLExt(JNIEnv *_env)
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index d65b498..2d921ad 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -18,6 +18,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index 3638b87..35a9a68 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -18,6 +18,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index 9724e6c..e04b56e 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -18,6 +18,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 1ffa4ec..bccbda6 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -18,6 +18,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index d832558..165262e 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -18,6 +18,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES2/gl2.h>
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index 719c6b3..d3fe439 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -18,6 +18,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES3/gl3.h>
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index afe7c63..b123f9d 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -17,6 +17,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <stdint.h>
diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp
index 8127433..1e4049b 100644
--- a/core/jni/android_opengl_GLES31Ext.cpp
+++ b/core/jni/android_opengl_GLES31Ext.cpp
@@ -17,6 +17,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES3/gl31.h>
diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp
index 7ed7548..e0175f0 100644
--- a/core/jni/android_opengl_GLES32.cpp
+++ b/core/jni/android_opengl_GLES32.cpp
@@ -17,6 +17,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <stdint.h>
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index 21de723..ef29c88 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -18,6 +18,7 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
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/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
index bd2f36f..d57f1fc 100644
--- a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
+++ b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
@@ -658,6 +658,52 @@
Truth.assertThat(matchingPackets).hasSize(1);
}
+ @Test
+ public void canTraceOnFlush() throws InvalidProtocolBufferException, InterruptedException {
+ final int singleIntValue = 101;
+ sInstanceProvider = (ds, idx, config) ->
+ new TestDataSource.TestDataSourceInstance(
+ ds,
+ idx,
+ (args) -> {},
+ (args) -> sTestDataSource.trace(ctx -> {
+ final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
+ long forTestingToken = protoOutputStream.start(FOR_TESTING);
+ long payloadToken = protoOutputStream.start(PAYLOAD);
+ protoOutputStream.write(SINGLE_INT, singleIntValue);
+ protoOutputStream.end(payloadToken);
+ protoOutputStream.end(forTestingToken);
+
+ ctx.flush();
+ }),
+ (args) -> {}
+ );
+
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ try {
+ traceMonitor.start();
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
+ assert rawProtoFromFile != null;
+ final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace
+ .parseFrom(rawProtoFromFile);
+
+ Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
+ final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
+ .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
+ final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream()
+ .filter(it -> it.getForTesting().getPayload().getSingleInt()
+ == singleIntValue).toList();
+ Truth.assertThat(matchingPackets).hasSize(1);
+ }
+
interface RunnableCreator {
Runnable create(int state, AtomicInteger stateOut);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index d0db708..a43a951 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -259,7 +259,7 @@
/** One handed mode controller to register transition listener. */
private final Optional<OneHandedController> mOneHandedOptional;
/** Drag and drop controller to register listener for onDragStarted. */
- private final Optional<DragAndDropController> mDragAndDropController;
+ private final DragAndDropController mDragAndDropController;
/** Used to send bubble events to launcher. */
private Bubbles.BubbleStateListener mBubbleStateListener;
@@ -285,7 +285,7 @@
BubblePositioner positioner,
DisplayController displayController,
Optional<OneHandedController> oneHandedOptional,
- Optional<DragAndDropController> dragAndDropController,
+ DragAndDropController dragAndDropController,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellBackgroundThread ShellExecutor bgExecutor,
@@ -463,7 +463,7 @@
});
mOneHandedOptional.ifPresent(this::registerOneHandedState);
- mDragAndDropController.ifPresent(controller -> controller.addListener(this::collapseStack));
+ mDragAndDropController.addListener(this::collapseStack);
// Clear out any persisted bubbles on disk that no longer have a valid user.
List<UserInfo> users = mUserManager.getAliveUsers();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index b52a118..d4ed017 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -83,7 +83,6 @@
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- Optional<DragAndDropController> dragAndDropController,
Transitions transitions,
TransactionPool transactionPool,
IconProvider iconProvider,
@@ -94,8 +93,8 @@
SystemWindows systemWindows) {
return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
- displayImeController, displayInsetsController, dragAndDropController, transitions,
- transactionPool, iconProvider, recentTasks, launchAdjacentController, mainExecutor,
- mainHandler, systemWindows);
+ displayImeController, displayInsetsController, transitions, transactionPool,
+ iconProvider, recentTasks, launchAdjacentController, mainExecutor, mainHandler,
+ systemWindows);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index fc97c798..0d6a852 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -78,7 +78,6 @@
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
-import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
@@ -203,20 +202,6 @@
@WMSingleton
@Provides
- static Optional<DragAndDropController> provideDragAndDropController(Context context,
- ShellInit shellInit,
- ShellController shellController,
- ShellCommandHandler shellCommandHandler,
- DisplayController displayController,
- UiEventLogger uiEventLogger,
- IconProvider iconProvider,
- @ShellMainThread ShellExecutor mainExecutor) {
- return Optional.ofNullable(DragAndDropController.create(context, shellInit, shellController,
- shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor));
- }
-
- @WMSingleton
- @Provides
static ShellTaskOrganizer provideShellTaskOrganizer(
Context context,
ShellInit shellInit,
@@ -911,7 +896,6 @@
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- Optional<DragAndDropController> dragAndDropControllerOptional,
ShellTaskOrganizer shellTaskOrganizer,
Optional<BubbleController> bubblesOptional,
Optional<SplitScreenController> splitScreenOptional,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 36f06e8..ead5ad2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -172,7 +172,7 @@
BubblePositioner positioner,
DisplayController displayController,
@DynamicOverride Optional<OneHandedController> oneHandedOptional,
- Optional<DragAndDropController> dragAndDropController,
+ DragAndDropController dragAndDropController,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellBackgroundThread ShellExecutor bgExecutor,
@@ -338,7 +338,7 @@
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- Optional<DragAndDropController> dragAndDropController,
+ DragAndDropController dragAndDropController,
Transitions transitions,
TransactionPool transactionPool,
IconProvider iconProvider,
@@ -553,6 +553,24 @@
}
//
+ // Drag and drop
+ //
+
+ @WMSingleton
+ @Provides
+ static DragAndDropController provideDragAndDropController(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ ShellCommandHandler shellCommandHandler,
+ DisplayController displayController,
+ UiEventLogger uiEventLogger,
+ IconProvider iconProvider,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new DragAndDropController(context, shellInit, shellController,
+ shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor);
+ }
+
+ //
// Misc
//
@@ -562,6 +580,7 @@
@ShellCreateTriggerOverride
@Provides
static Object provideIndependentShellComponentsToCreate(
+ DragAndDropController dragAndDropController,
DefaultMixedHandler defaultMixedHandler) {
return new Object();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index fdfb6f3..269c369 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -105,25 +105,7 @@
void onDragStarted();
}
- /**
- * Creates {@link DragAndDropController}. Returns {@code null} if the feature is disabled.
- */
- public static DragAndDropController create(Context context,
- ShellInit shellInit,
- ShellController shellController,
- ShellCommandHandler shellCommandHandler,
- DisplayController displayController,
- UiEventLogger uiEventLogger,
- IconProvider iconProvider,
- ShellExecutor mainExecutor) {
- if (!context.getResources().getBoolean(R.bool.config_enableShellDragDrop)) {
- return null;
- }
- return new DragAndDropController(context, shellInit, shellController, shellCommandHandler,
- displayController, uiEventLogger, iconProvider, mainExecutor);
- }
-
- DragAndDropController(Context context,
+ public DragAndDropController(Context context,
ShellInit shellInit,
ShellController shellController,
ShellCommandHandler shellCommandHandler,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 70cb2fc..1b124c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -184,7 +184,7 @@
private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
private final DisplayInsetsController mDisplayInsetsController;
- private final Optional<DragAndDropController> mDragAndDropController;
+ private final DragAndDropController mDragAndDropController;
private final Transitions mTransitions;
private final TransactionPool mTransactionPool;
private final IconProvider mIconProvider;
@@ -214,7 +214,7 @@
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- Optional<DragAndDropController> dragAndDropController,
+ DragAndDropController dragAndDropController,
Transitions transitions,
TransactionPool transactionPool,
IconProvider iconProvider,
@@ -290,7 +290,7 @@
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
- mDragAndDropController = Optional.of(dragAndDropController);
+ mDragAndDropController = dragAndDropController;
mTransitions = transitions;
mTransactionPool = transactionPool;
mIconProvider = iconProvider;
@@ -328,7 +328,9 @@
// TODO: Multi-display
mStageCoordinator = createStageCoordinator();
}
- mDragAndDropController.ifPresent(controller -> controller.setSplitScreenController(this));
+ if (mDragAndDropController != null) {
+ mDragAndDropController.setSplitScreenController(this);
+ }
mWindowDecorViewModel.ifPresent(viewModel -> viewModel.setSplitScreenController(this));
mDesktopTasksController.ifPresent(controller -> controller.setSplitScreenController(this));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index c101425..aec4d11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -74,7 +74,6 @@
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- Optional<DragAndDropController> dragAndDropController,
Transitions transitions,
TransactionPool transactionPool,
IconProvider iconProvider,
@@ -85,7 +84,7 @@
SystemWindows systemWindows) {
super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer,
syncQueue, rootTDAOrganizer, displayController, displayImeController,
- displayInsetsController, dragAndDropController, transitions, transactionPool,
+ displayInsetsController, null, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
Optional.empty(), mainExecutor);
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/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 7b6dc38..6b0620c 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -44,6 +45,7 @@
import android.media.tv.TvRecordingInfo;
import android.media.tv.TvTrackInfo;
import android.media.tv.TvView;
+import android.media.tv.flags.Flags;
import android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback;
import android.net.Uri;
import android.net.http.SslCertificate;
@@ -959,12 +961,12 @@
/**
* Called when the TV App sends the selected track info as a response to
- * requestSelectedTrackInfo.
+ * {@link #requestSelectedTrackInfo()}
*
- * @param tracks
- * @hide
+ * @param tracks A list of {@link TvTrackInfo} that are currently selected
*/
- public void onSelectedTrackInfo(List<TvTrackInfo> tracks) {
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
+ public void onSelectedTrackInfo(@NonNull List<TvTrackInfo> tracks) {
}
@Override
@@ -1373,13 +1375,13 @@
}
/**
- * Requests the currently selected {@link TvTrackInfo} from the TV App.
+ * Requests a list of the currently selected {@link TvTrackInfo} from the TV App.
*
* <p> Normally, track info cannot be synchronized until the channel has
- * been changed. This is used when the session of the TIAS is newly
- * created and the normal synchronization has not happened yet.
- * @hide
+ * been changed. This is used when the session of the {@link TvInteractiveAppService}
+ * is newly created and the normal synchronization has not happened yet.
*/
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
@CallSuper
public void requestSelectedTrackInfo() {
executeOrPostRunnableOnMainThread(() -> {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 3b29574..584ea84 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -30,6 +31,7 @@
import android.media.tv.TvRecordingInfo;
import android.media.tv.TvTrackInfo;
import android.media.tv.TvView;
+import android.media.tv.flags.Flags;
import android.media.tv.interactive.TvInteractiveAppManager.Session;
import android.media.tv.interactive.TvInteractiveAppManager.Session.FinishedInputEventCallback;
import android.media.tv.interactive.TvInteractiveAppManager.SessionCallback;
@@ -585,8 +587,9 @@
/**
* Sends the currently selected track info to the TV Interactive App.
*
- * @hide
+ * @param tracks list of {@link TvTrackInfo} of the currently selected track(s)
*/
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
public void sendSelectedTrackInfo(@Nullable List<TvTrackInfo> tracks) {
if (DEBUG) {
Log.d(TAG, "sendSelectedTrackInfo");
@@ -1248,8 +1251,8 @@
* called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
public void onRequestSelectedTrackInfo(@NonNull String iAppServiceId) {
}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
index 70202463..c18a2de 100644
--- a/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
@@ -41,7 +41,7 @@
public EffectsTest() {
- Log.d(TAG, "contructor");
+ Log.d(TAG, "constructor");
}
@Override
diff --git a/opengl/java/android/opengl/EGLExt.java b/opengl/java/android/opengl/EGLExt.java
index 1570e0e..31104a0 100644
--- a/opengl/java/android/opengl/EGLExt.java
+++ b/opengl/java/android/opengl/EGLExt.java
@@ -46,14 +46,6 @@
_nativeClassInit();
}
- // C function EGLBoolean eglPresentationTimeANDROID ( EGLDisplay dpy, EGLSurface sur, EGLnsecsANDROID time )
-
- public static native boolean eglPresentationTimeANDROID(
- EGLDisplay dpy,
- EGLSurface sur,
- long time
- );
-
/**
* Retrieves the SyncFence for an EGLSync created with EGL_SYNC_NATIVE_FENCE_ANDROID
*
@@ -83,4 +75,13 @@
}
private static native int eglDupNativeFenceFDANDROIDImpl(EGLDisplay display, EGLSync sync);
+
+ // C function EGLBoolean eglPresentationTimeANDROID ( EGLDisplay dpy, EGLSurface sur, EGLnsecsANDROID time )
+
+ public static native boolean eglPresentationTimeANDROID(
+ EGLDisplay dpy,
+ EGLSurface sur,
+ long time
+ );
+
}
diff --git a/packages/CrashRecovery/services/Android.bp b/packages/CrashRecovery/services/Android.bp
index 27ddff9..63e6c50 100644
--- a/packages/CrashRecovery/services/Android.bp
+++ b/packages/CrashRecovery/services/Android.bp
@@ -3,7 +3,15 @@
srcs: [
"java/**/*.java",
"java/**/*.aidl",
+ ":statslog-crashrecovery-java-gen",
],
- path: "java",
visibility: ["//frameworks/base:__subpackages__"],
}
+
+genrule {
+ name: "statslog-crashrecovery-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module crashrecovery " +
+ "--javaPackage com.android.server.crashrecovery.proto --javaClass CrashRecoveryStatsLog --worksource",
+ out: ["com/android/server/crashrecovery/proto/CrashRecoveryStatsLog.java"],
+}
diff --git a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
index dd54334..dffe4e2 100644
--- a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
@@ -46,11 +46,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.PackageWatchdog.FailureReasons;
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
import com.android.server.am.SettingsToPropertiesMapper;
+import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
import java.io.File;
import java.util.ArrayList;
@@ -390,7 +390,7 @@
return;
}
- FrameworkStatsLog.write(FrameworkStatsLog.RESCUE_PARTY_RESET_REPORTED, level);
+ CrashRecoveryStatsLog.write(CrashRecoveryStatsLog.RESCUE_PARTY_RESET_REPORTED, level);
// Try our best to reset all settings possible, and once finished
// rethrow any exception that we encountered
Exception res = null;
diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 2007079..50322f0 100644
--- a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -39,13 +39,13 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.server.PackageWatchdog;
import com.android.server.PackageWatchdog.FailureReasons;
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
import com.android.server.SystemConfig;
+import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
import com.android.server.pm.ApexManager;
import java.io.BufferedReader;
@@ -418,7 +418,7 @@
final VersionedPackage logPackage = logPackageTemp;
WatchdogRollbackLogger.logEvent(logPackage,
- FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
+ CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
reasonToLog, failedPackageToLog);
Consumer<Intent> onResult = result -> {
@@ -430,19 +430,19 @@
int rollbackId = rollback.getRollbackId();
saveStagedRollbackId(rollbackId, logPackage);
WatchdogRollbackLogger.logEvent(logPackage,
- FrameworkStatsLog
+ CrashRecoveryStatsLog
.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
reasonToLog, failedPackageToLog);
} else {
WatchdogRollbackLogger.logEvent(logPackage,
- FrameworkStatsLog
+ CrashRecoveryStatsLog
.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
reasonToLog, failedPackageToLog);
}
} else {
WatchdogRollbackLogger.logEvent(logPackage,
- FrameworkStatsLog
+ CrashRecoveryStatsLog
.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
reasonToLog, failedPackageToLog);
}
diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
index f9ef994..898c543 100644
--- a/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -16,16 +16,16 @@
package com.android.server.rollback;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE;
-import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -42,8 +42,8 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.PackageWatchdog;
+import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
import java.util.List;
import java.util.Set;
@@ -197,8 +197,8 @@
+ " rollbackReason: " + rollbackReasonToString(rollbackReason)
+ " failedPackageName: " + failingPackageName);
if (logPackage != null) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+ CrashRecoveryStatsLog.write(
+ CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
type,
logPackage.getPackageName(),
logPackage.getVersionCode(),
@@ -208,8 +208,8 @@
} else {
// In the case that the log package is null, still log an empty string as an
// indication that retrieving the logging parent failed.
- FrameworkStatsLog.write(
- FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+ CrashRecoveryStatsLog.write(
+ CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
type,
"",
0,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 6cafcf7..bd9d2e6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -32,6 +32,7 @@
import android.os.Bundle
import android.os.ResultReceiver
import android.util.Log
+import android.view.autofill.AutofillManager
import com.android.credentialmanager.createflow.DisabledProviderInfo
import com.android.credentialmanager.createflow.EnabledProviderInfo
import com.android.credentialmanager.createflow.RequestDisplayInfo
@@ -80,9 +81,10 @@
CreateCredentialProviderData::class.java
) ?: emptyList()
RequestInfo.TYPE_GET ->
- intent.extras?.getParcelableArrayList(
- ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
- GetCredentialProviderData::class.java
+ getEnabledProviderDataList(
+ intent
+ ) ?: getEnabledProviderDataListFromAuthExtras(
+ intent
) ?: emptyList()
else -> {
Log.d(
@@ -238,6 +240,24 @@
)
}
+ private fun getEnabledProviderDataList(intent: Intent): List<GetCredentialProviderData>? {
+ return intent.extras?.getParcelableArrayList(
+ ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
+ GetCredentialProviderData::class.java
+ )
+ }
+
+ private fun getEnabledProviderDataListFromAuthExtras(
+ intent: Intent
+ ): List<GetCredentialProviderData>? {
+ return intent.getBundleExtra(
+ AutofillManager.EXTRA_AUTH_STATE
+ ) ?.getParcelableArrayList(
+ ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
+ GetCredentialProviderData::class.java
+ )
+ }
+
// IMPORTANT: new invocation should be mindful that this method can throw.
private fun getCreateProviderEnableListInitialUiState(): List<EnabledProviderInfo> {
return CreateFlowUtils.toEnabledProviderList(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 1f1d236..07f1fa3 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -16,22 +16,24 @@
package com.android.credentialmanager.autofill
-import android.R
+import android.app.PendingIntent
import android.app.assist.AssistStructure
import android.content.Context
-import android.app.PendingIntent
-import android.credentials.GetCredentialResponse
-import android.credentials.GetCredentialRequest
-import android.credentials.GetCandidateCredentialsResponse
-import android.credentials.GetCandidateCredentialsException
+import android.credentials.Credential
+import android.credentials.CredentialManager
import android.credentials.CredentialOption
+import android.credentials.GetCandidateCredentialsException
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.GetCredentialRequest
+import android.credentials.GetCredentialResponse
+import android.credentials.selection.Entry
import android.credentials.selection.GetCredentialProviderData
+import android.credentials.selection.ProviderData
import android.graphics.drawable.Icon
import android.os.Bundle
import android.os.CancellationSignal
import android.os.OutcomeReceiver
import android.provider.Settings
-import android.credentials.Credential
import android.service.autofill.AutofillService
import android.service.autofill.Dataset
import android.service.autofill.Field
@@ -44,12 +46,11 @@
import android.service.autofill.SaveRequest
import android.service.credentials.CredentialProviderService
import android.util.Log
+import android.view.autofill.AutofillId
import android.view.autofill.AutofillValue
import android.view.autofill.IAutoFillManagerClient
-import android.view.autofill.AutofillId
-import android.widget.inline.InlinePresentationSpec
-import android.credentials.CredentialManager
import android.widget.RemoteViews
+import android.widget.inline.InlinePresentationSpec
import androidx.autofill.inline.v1.InlineSuggestionUi
import androidx.credentials.provider.CustomCredentialEntry
import androidx.credentials.provider.PasswordCredentialEntry
@@ -61,10 +62,9 @@
import com.android.credentialmanager.ktx.credentialEntry
import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.model.get.CredentialEntryInfo
-import com.android.credentialmanager.model.get.ProviderInfo
+import java.util.concurrent.Executors
import org.json.JSONException
import org.json.JSONObject
-import java.util.concurrent.Executors
class CredentialAutofillService : AutofillService() {
@@ -153,8 +153,7 @@
return
}
- val fillResponse = convertToFillResponse(result, request,
- this@CredentialAutofillService)
+ val fillResponse = convertToFillResponse(result, request)
if (fillResponse != null) {
callback.onSuccess(fillResponse)
} else {
@@ -231,7 +230,7 @@
}
private fun getEntryToIconMap(
- candidateProviderDataList: MutableList<GetCredentialProviderData>
+ candidateProviderDataList: List<GetCredentialProviderData>
): Map<String, Icon> {
val entryIconMap: MutableMap<String, Icon> = mutableMapOf()
candidateProviderDataList.forEach { provider ->
@@ -261,20 +260,16 @@
private fun convertToFillResponse(
getCredResponse: GetCandidateCredentialsResponse,
- filLRequest: FillRequest,
- context: Context
+ filLRequest: FillRequest
): FillResponse? {
- val providerList = GetFlowUtils.toProviderList(
- getCredResponse.candidateProviderDataList,
- context)
- if (providerList.isEmpty()) {
+ val candidateProviders = getCredResponse.candidateProviderDataList
+ if (candidateProviders.isEmpty()) {
return null
}
- val entryIconMap: Map<String, Icon> =
- getEntryToIconMap(getCredResponse.candidateProviderDataList)
- val autofillIdToProvidersMap: Map<AutofillId, List<ProviderInfo>> =
- mapAutofillIdToProviders(providerList)
+ val entryIconMap: Map<String, Icon> = getEntryToIconMap(candidateProviders)
+ val autofillIdToProvidersMap: Map<AutofillId, ArrayList<GetCredentialProviderData>> =
+ mapAutofillIdToProviders(candidateProviders)
val fillResponseBuilder = FillResponse.Builder()
var validFillResponse = false
autofillIdToProvidersMap.forEach { (autofillId, providers) ->
@@ -292,11 +287,14 @@
private fun processProvidersForAutofillId(
filLRequest: FillRequest,
autofillId: AutofillId,
- providerList: List<ProviderInfo>,
+ providerDataList: ArrayList<GetCredentialProviderData>,
entryIconMap: Map<String, Icon>,
fillResponseBuilder: FillResponse.Builder,
bottomSheetPendingIntent: PendingIntent?
): Boolean {
+ val providerList = GetFlowUtils.toProviderList(
+ providerDataList,
+ this@CredentialAutofillService)
if (providerList.isEmpty()) {
return false
}
@@ -340,7 +338,7 @@
return@usernameLoop
}
if (i >= maxInlineItemCount && i >= lastDropdownDatasetIndex) {
- return@usernameLoop;
+ return@usernameLoop
}
val icon: Icon = if (primaryEntry.icon == null) {
// The empty entry icon has non-null icon reference but null drawable reference.
@@ -398,19 +396,20 @@
inlinePresentationSpecsCount)
if (datasetAdded && bottomSheetPendingIntent != null && pinnedSpec != null) {
addPinnedInlineSuggestion(bottomSheetPendingIntent, pinnedSpec, autofillId,
- fillResponseBuilder)
+ fillResponseBuilder, providerDataList)
}
return datasetAdded
}
- private fun createInlinePresentation(primaryEntry: CredentialEntryInfo,
- pendingIntent: PendingIntent,
- icon: Icon,
- spec: InlinePresentationSpec,
- duplicateDisplayNameForPasskeys: MutableMap<String, Boolean>):
- InlinePresentation {
- val displayName: String = if (primaryEntry.credentialType == CredentialType.PASSKEY
- && primaryEntry.displayName != null) {
+ private fun createInlinePresentation(
+ primaryEntry: CredentialEntryInfo,
+ pendingIntent: PendingIntent,
+ icon: Icon,
+ spec: InlinePresentationSpec,
+ duplicateDisplayNameForPasskeys: MutableMap<String, Boolean>
+ ): InlinePresentation {
+ val displayName: String = if (primaryEntry.credentialType == CredentialType.PASSKEY &&
+ primaryEntry.displayName != null) {
primaryEntry.displayName!!
} else {
primaryEntry.userName
@@ -430,7 +429,8 @@
private fun addDropdownMoreOptionsPresentation(
bottomSheetPendingIntent: PendingIntent,
autofillId: AutofillId,
- fillResponseBuilder: FillResponse.Builder) {
+ fillResponseBuilder: FillResponse.Builder
+ ) {
val presentationBuilder = Presentations.Builder()
.setMenuPresentation(RemoteViewsFactory.createMoreSignInOptionsPresentation(this))
@@ -460,7 +460,8 @@
bottomSheetPendingIntent: PendingIntent,
spec: InlinePresentationSpec,
autofillId: AutofillId,
- fillResponseBuilder: FillResponse.Builder
+ fillResponseBuilder: FillResponse.Builder,
+ providerDataList: ArrayList<GetCredentialProviderData>
) {
val dataSetBuilder = Dataset.Builder()
val sliceBuilder = InlineSuggestionUi
@@ -471,6 +472,10 @@
.setInlinePresentation(InlinePresentation(
sliceBuilder.build().slice, spec, /* pinned= */ true))
+ val extraBundle = Bundle()
+ extraBundle.putParcelableArrayList(
+ ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, providerDataList)
+
fillResponseBuilder.addDataset(
dataSetBuilder
.setField(
@@ -479,6 +484,7 @@
presentationBuilder.build())
.build())
.setAuthentication(bottomSheetPendingIntent.intentSender)
+ .setAuthenticationExtras(extraBundle)
.build()
)
}
@@ -514,16 +520,16 @@
* }
*/
private fun mapAutofillIdToProviders(
- providerList: List<ProviderInfo>
- ): Map<AutofillId, List<ProviderInfo>> {
- val autofillIdToProviders: MutableMap<AutofillId, MutableList<ProviderInfo>> =
- mutableMapOf()
+ providerList: List<GetCredentialProviderData>
+ ): Map<AutofillId, ArrayList<GetCredentialProviderData>> {
+ val autofillIdToProviders: MutableMap<AutofillId, ArrayList<GetCredentialProviderData>> =
+ mutableMapOf()
providerList.forEach { provider ->
val autofillIdToCredentialEntries:
- MutableMap<AutofillId, MutableList<CredentialEntryInfo>> =
- mapAutofillIdToCredentialEntries(provider.credentialEntryList)
+ MutableMap<AutofillId, ArrayList<Entry>> =
+ mapAutofillIdToCredentialEntries(provider.credentialEntries)
autofillIdToCredentialEntries.forEach { (autofillId, entries) ->
- autofillIdToProviders.getOrPut(autofillId) { mutableListOf() }
+ autofillIdToProviders.getOrPut(autofillId) { ArrayList() }
.add(copyProviderInfo(provider, entries))
}
}
@@ -531,13 +537,13 @@
}
private fun mapAutofillIdToCredentialEntries(
- credentialEntryList: List<CredentialEntryInfo>
- ): MutableMap<AutofillId, MutableList<CredentialEntryInfo>> {
+ credentialEntryList: List<Entry>
+ ): MutableMap<AutofillId, ArrayList<Entry>> {
val autofillIdToCredentialEntries:
- MutableMap<AutofillId, MutableList<CredentialEntryInfo>> = mutableMapOf()
+ MutableMap<AutofillId, ArrayList<Entry>> = mutableMapOf()
credentialEntryList.forEach entryLoop@{ credentialEntry ->
val autofillId: AutofillId? = credentialEntry
- .fillInIntent
+ .frameworkExtrasIntent
?.getParcelableExtra(
CredentialProviderService.EXTRA_AUTOFILL_ID,
AutofillId::class.java)
@@ -546,24 +552,22 @@
" Integration might be disabled.")
return@entryLoop
}
- autofillIdToCredentialEntries.getOrPut(autofillId) { mutableListOf() }
+ autofillIdToCredentialEntries.getOrPut(autofillId) { ArrayList() }
.add(credentialEntry)
}
return autofillIdToCredentialEntries
}
private fun copyProviderInfo(
- providerInfo: ProviderInfo,
- credentialList: List<CredentialEntryInfo>
- ): ProviderInfo {
- return ProviderInfo(
- providerInfo.id,
- providerInfo.icon,
- providerInfo.displayName,
- credentialList,
- providerInfo.authenticationEntryList,
- providerInfo.remoteEntry,
- providerInfo.actionEntryList
+ providerInfo: GetCredentialProviderData,
+ credentialList: List<Entry>
+ ): GetCredentialProviderData {
+ return GetCredentialProviderData(
+ providerInfo.providerFlattenedComponentName,
+ credentialList,
+ providerInfo.actionChips,
+ providerInfo.authenticationEntries,
+ providerInfo.remoteEntry
)
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index dfeb1f3..d821f19 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -33,7 +33,7 @@
public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
protected View mEcaView;
- // To avoid accidental lockout due to events while the device in in the pocket, ignore
+ // To avoid accidental lockout due to events while the device in the pocket, ignore
// any passwords with length less than or equal to this length.
protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
private KeyDownListener mKeyDownListener;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index bc12aee..ce03072 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -132,7 +132,7 @@
boolean shouldSubtleWindowAnimationsForUnlock();
/**
- * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
+ * Starts the animation before we dismiss Keyguard, i.e. a disappearing animation on the
* security view of the bouncer.
*
* @param finishRunnable the runnable to be run after the animation finished, or {@code null} if
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
index 9b83b75..ee3706a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
@@ -80,7 +80,7 @@
}
/**
- * Click listener for messsage.
+ * Click listener for message.
*/
public @Nullable View.OnClickListener getClickListener() {
return mOnClickListener;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index f085e88..4766a84 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -444,7 +444,7 @@
/**
* Whether a hide is pending and we are just waiting for #startKeyguardExitAnimation to be
* called.
- * */
+ */
private boolean mHiding;
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 0831971..25a7eb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -16,12 +16,14 @@
package com.android.systemui.shade
+import android.content.Context
import android.os.PowerManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
import android.view.MotionEvent
import android.view.View
+import android.view.WindowManager
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -33,18 +35,21 @@
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.Assert.assertThrows
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.BeforeClass
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -52,12 +57,17 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@Ignore("b/323053208")
+@ExperimentalCoroutinesApi
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class GlanceableHubContainerControllerTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+ private val kosmos: Kosmos =
+ testKosmos().apply {
+ // UnconfinedTestDispatcher makes testing simpler due to CommunalInteractor flows using
+ // SharedFlow
+ testDispatcher = UnconfinedTestDispatcher()
+ }
@Mock private lateinit var communalViewModel: CommunalViewModel
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@@ -104,17 +114,19 @@
R.dimen.communal_bottom_edge_swipe_region_height,
BOTTOM_SWIPE_REGION_WIDTH
)
+
+ initAndAttachContainerView()
}
@Test
- fun isEnabled_interactorEnabled_interceptsTouches() {
+ fun isEnabled_communalEnabled_returnsTrue() {
communalRepository.setIsCommunalEnabled(true)
assertThat(underTest.isEnabled()).isTrue()
}
@Test
- fun isEnabled_interactorDisabled_doesNotIntercept() {
+ fun isEnabled_communalDisabled_returnsFalse() {
communalRepository.setIsCommunalEnabled(false)
assertThat(underTest.isEnabled()).isFalse()
@@ -124,11 +136,29 @@
fun initView_notEnabled_throwsException() {
communalRepository.setIsCommunalEnabled(false)
+ underTest =
+ GlanceableHubContainerController(
+ communalInteractor,
+ communalViewModel,
+ keyguardTransitionInteractor,
+ shadeInteractor,
+ powerManager,
+ )
+
assertThrows(RuntimeException::class.java) { underTest.initView(context) }
}
@Test
fun initView_calledTwice_throwsException() {
+ underTest =
+ GlanceableHubContainerController(
+ communalInteractor,
+ communalViewModel,
+ keyguardTransitionInteractor,
+ shadeInteractor,
+ powerManager,
+ )
+
// First call succeeds.
underTest.initView(context)
@@ -137,25 +167,20 @@
}
@Test
- fun onTouchEvent_touchInsideGestureRegion_interceptsTouches() {
- // Communal is open.
- communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+ fun onTouchEvent_communalClosed_doesNotIntercept() {
+ // Communal is closed.
+ goToScene(CommunalSceneKey.Blank)
- initAndAttachContainerView()
-
- // Touch events are intercepted.
- assertThat(underTest.onTouchEvent(DOWN_IN_RIGHT_SWIPE_REGION_EVENT)).isTrue()
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
}
@Test
- fun onTouchEvent_subsequentTouchesAfterGestureStart_interceptsTouches() {
- // Communal is open.
- communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+ fun onTouchEvent_openGesture_interceptsTouches() {
+ // Communal is closed.
+ goToScene(CommunalSceneKey.Blank)
- initAndAttachContainerView()
-
- // Initial touch down is intercepted, and so are touches outside of the region, until an up
- // event is received.
+ // Initial touch down is intercepted, and so are touches outside of the region, until an
+ // up event is received.
assertThat(underTest.onTouchEvent(DOWN_IN_RIGHT_SWIPE_REGION_EVENT)).isTrue()
assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
@@ -165,34 +190,27 @@
@Test
fun onTouchEvent_communalOpen_interceptsTouches() {
// Communal is open.
- communalRepository.setDesiredScene(CommunalSceneKey.Communal)
+ goToScene(CommunalSceneKey.Communal)
- initAndAttachContainerView()
- testableLooper.processAllMessages()
-
- // Touch events are intercepted.
+ // Touch events are intercepted outside of any gesture areas.
assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
// User activity sent to PowerManager.
verify(powerManager).userActivity(any(), any(), any())
}
@Test
- fun onTouchEvent_topSwipeWhenHubOpen_returnsFalse() {
+ fun onTouchEvent_topSwipeWhenCommunalOpen_doesNotIntercept() {
// Communal is open.
- communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
- initAndAttachContainerView()
+ goToScene(CommunalSceneKey.Communal)
// Touch event in the top swipe reqgion is not intercepted.
assertThat(underTest.onTouchEvent(DOWN_IN_TOP_SWIPE_REGION_EVENT)).isFalse()
}
@Test
- fun onTouchEvent_bottomSwipeWhenHubOpen_returnsFalse() {
+ fun onTouchEvent_bottomSwipeWhenCommunalOpen_doesNotIntercept() {
// Communal is open.
- communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
- initAndAttachContainerView()
+ goToScene(CommunalSceneKey.Communal)
// Touch event in the bottom swipe reqgion is not intercepted.
assertThat(underTest.onTouchEvent(DOWN_IN_BOTTOM_SWIPE_REGION_EVENT)).isFalse()
@@ -201,9 +219,7 @@
@Test
fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() {
// Communal is open.
- communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
- initAndAttachContainerView()
+ goToScene(CommunalSceneKey.Communal)
// Bouncer is visible.
bouncerShowingFlow.value = true
@@ -218,9 +234,7 @@
@Test
fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() {
// Communal is open.
- communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
- initAndAttachContainerView()
+ goToScene(CommunalSceneKey.Communal)
shadeShowingFlow.value = true
testableLooper.processAllMessages()
@@ -232,10 +246,7 @@
@Test
fun onTouchEvent_containerViewDisposed_doesNotIntercept() {
// Communal is open.
- communalRepository.setDesiredScene(CommunalSceneKey.Communal)
-
- initAndAttachContainerView()
- testableLooper.processAllMessages()
+ goToScene(CommunalSceneKey.Communal)
// Touch events are intercepted.
assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
@@ -253,15 +264,24 @@
parentView = FrameLayout(context)
parentView.addView(containerView)
- // Make view clickable so that dispatchTouchEvent returns true.
- containerView.isClickable = true
-
underTest.initView(containerView)
+
// Attach the view so that flows start collecting.
ViewUtils.attachView(parentView)
- // Give the view a size so that determining if a touch starts at the right edge works.
- parentView.layout(0, 0, CONTAINER_WIDTH, CONTAINER_HEIGHT)
- containerView.layout(0, 0, CONTAINER_WIDTH, CONTAINER_HEIGHT)
+
+ // Give the view a fixed size to simplify testing for edge swipes.
+ val lp =
+ parentView.layoutParams.apply {
+ width = CONTAINER_WIDTH
+ height = CONTAINER_HEIGHT
+ }
+ val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ wm.updateViewLayout(parentView, lp)
+ }
+
+ private fun goToScene(scene: CommunalSceneKey) {
+ communalRepository.setDesiredScene(scene)
+ testableLooper.processAllMessages()
}
companion object {
@@ -271,13 +291,17 @@
private const val TOP_SWIPE_REGION_WIDTH = 20
private const val BOTTOM_SWIPE_REGION_WIDTH = 20
+ /**
+ * A touch down event right in the middle of the screen, to avoid being in any of the swipe
+ * regions.
+ */
private val DOWN_EVENT =
MotionEvent.obtain(
0L,
0L,
MotionEvent.ACTION_DOWN,
- CONTAINER_WIDTH.toFloat(),
- CONTAINER_HEIGHT.toFloat(),
+ CONTAINER_WIDTH.toFloat() / 2,
+ CONTAINER_HEIGHT.toFloat() / 2,
0
)
private val DOWN_IN_RIGHT_SWIPE_REGION_EVENT =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 9ea4142..f3e9203 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -616,7 +616,7 @@
mPositioner,
mock(DisplayController.class),
mOneHandedOptional,
- Optional.of(mock(DragAndDropController.class)),
+ mock(DragAndDropController.class),
syncExecutor,
mock(Handler.class),
mTaskViewTransitions,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 9ad234e1..4a5ebd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -71,7 +71,7 @@
BubblePositioner positioner,
DisplayController displayController,
Optional<OneHandedController> oneHandedOptional,
- Optional<DragAndDropController> dragAndDropController,
+ DragAndDropController dragAndDropController,
ShellExecutor shellMainExecutor,
Handler shellMainHandler,
TaskViewTransitions taskViewTransitions,
diff --git a/ravenwood/bulk_enable.py b/ravenwood/bulk_enable.py
index 83fda9e..aafaaff 100644
--- a/ravenwood/bulk_enable.py
+++ b/ravenwood/bulk_enable.py
@@ -21,7 +21,7 @@
classes that have partial success.
Typical usage:
-$ ENABLE_PROBE_IGNORED=1 atest MyTestsRavenwood
+$ RAVENWOOD_RUN_DISABLED_TESTS=1 atest MyTestsRavenwood
$ cd /path/to/tests/root
$ python bulk_enable.py /path/to/atest/output/host_log.txt
"""
diff --git a/ravenwood/coretest/Android.bp b/ravenwood/coretest/Android.bp
new file mode 100644
index 0000000..9b7f8f7
--- /dev/null
+++ b/ravenwood/coretest/Android.bp
@@ -0,0 +1,23 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_ravenwood_test {
+ name: "RavenwoodCoreTest",
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.test.rules",
+ ],
+
+ srcs: [
+ "test/**/*.java",
+ ],
+ sdk_version: "test_current",
+ auto_gen_config: true,
+}
diff --git a/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
new file mode 100644
index 0000000..e58c282
--- /dev/null
+++ b/ravenwood/coretest/test/com/android/platform/test/ravenwood/coretest/RavenwoodTestRunnerValidationTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 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.platform.test.ravenwood.coretest;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for the test runner validator in RavenwoodRule.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodTestRunnerValidationTest {
+ // Note the following rules don't have a @Rule, because they need to be applied in a specific
+ // order. So we use a RuleChain instead.
+ private ExpectedException mThrown = ExpectedException.none();
+ private final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Rule
+ public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
+
+ public RavenwoodTestRunnerValidationTest() {
+ Assume.assumeTrue(mRavenwood._ravenwood_private$isOptionalValidationEnabled());
+ // Because RavenwoodRule will throw this error before executing the test method,
+ // we can't do it in the test method itself.
+ // So instead, we initialize it here.
+ mThrown.expectMessage("Switch to androidx.test.ext.junit.runners.AndroidJUnit4");
+ }
+
+ @Test
+ public void testValidateTestRunner() {
+ }
+}
diff --git a/ravenwood/fix_test_runner.py b/ravenwood/fix_test_runner.py
new file mode 100755
index 0000000..99b7a1f
--- /dev/null
+++ b/ravenwood/fix_test_runner.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 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.
+
+"""
+Tool switch the deprecated jetpack test runner to the correct one.
+
+Typical usage:
+$ RAVENWOOD_OPTIONAL_VALIDATION=1 atest MyTestsRavenwood # Prepend RAVENWOOD_RUN_DISABLED_TESTS=1 as needed
+$ cd /path/to/tests/root
+$ python bulk_enable.py /path/to/atest/output/host_log.txt
+"""
+
+import collections
+import os
+import re
+import subprocess
+import sys
+
+re_result = re.compile("I/ModuleListener.+?null-device-0 (.+?)#(.+?) ([A-Z_]+)(.*)$")
+
+OLD_RUNNER = "androidx.test.runner.AndroidJUnit4"
+NEW_RUNNER = "androidx.test.ext.junit.runners.AndroidJUnit4"
+SED_ARG = r"s/%s/%s/g" % (OLD_RUNNER, NEW_RUNNER)
+
+target = collections.defaultdict()
+
+with open(sys.argv[1]) as f:
+ for line in f.readlines():
+ result = re_result.search(line)
+ if result:
+ clazz, method, state, msg = result.groups()
+ if NEW_RUNNER in msg:
+ target[clazz] = 1
+
+if len(target) == 0:
+ print("No tests need updating.")
+ sys.exit(0)
+
+num_fixed = 0
+for clazz in target.keys():
+ print("Fixing test runner", clazz)
+ clazz_match = re.compile("%s\.(kt|java)" % (clazz.split(".")[-1]))
+ found = False
+ for root, dirs, files in os.walk("."):
+ for f in files:
+ if clazz_match.match(f):
+ found = True
+ num_fixed += 1
+ path = os.path.join(root, f)
+ subprocess.run(["sed", "-i", "-E", SED_ARG, path])
+ if not found:
+ print(f" Warining: tests {clazz} not found")
+
+
+print("Tests fixed", num_fixed)
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 8af561f..85fa65a 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -28,7 +28,10 @@
import com.android.internal.os.RuntimeInit;
+import org.junit.Assert;
import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
import java.io.PrintStream;
import java.util.Map;
@@ -167,4 +170,31 @@
}
}
}
+
+ public static void validate(Statement base, Description description,
+ boolean enableOptionalValidation) {
+ validateTestRunner(base, description, enableOptionalValidation);
+ }
+
+ private static void validateTestRunner(Statement base, Description description,
+ boolean shouldFail) {
+ final var testClass = description.getTestClass();
+ final var runWith = testClass.getAnnotation(RunWith.class);
+ if (runWith == null) {
+ return;
+ }
+
+ // Due to build dependencies, we can't directly refer to androidx classes here,
+ // so just check the class name instead.
+ if (runWith.value().getCanonicalName().equals("androidx.test.runner.AndroidJUnit4")) {
+ var message = "Test " + testClass.getCanonicalName() + " uses deprecated"
+ + " test runner androidx.test.runner.AndroidJUnit4."
+ + " Switch to androidx.test.ext.junit.runners.AndroidJUnit4.";
+ if (shouldFail) {
+ Assert.fail(message);
+ } else {
+ System.err.println("Warning: " + message);
+ }
+ }
+ }
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index daed457..764573d 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -89,6 +89,12 @@
private static final boolean ENABLE_REALLY_DISABLE_PATTERN =
!REALLY_DISABLE_PATTERN.pattern().isEmpty();
+ /**
+ * If true, enable optional validation on running tests.
+ */
+ private static final boolean ENABLE_OPTIONAL_VALIDATION = "1".equals(
+ System.getenv("RAVENWOOD_OPTIONAL_VALIDATION"));
+
static {
if (ENABLE_PROBE_IGNORED) {
System.out.println("$RAVENWOOD_RUN_DISABLED_TESTS enabled: force running all tests");
@@ -275,6 +281,12 @@
}
}
+ private void commonPrologue(Statement base, Description description) {
+ RavenwoodRuleImpl.logTestRunner("started", description);
+ RavenwoodRuleImpl.validate(base, description, ENABLE_OPTIONAL_VALIDATION);
+ RavenwoodRuleImpl.init(RavenwoodRule.this);
+ }
+
/**
* Run the given {@link Statement} with no special treatment.
*/
@@ -284,8 +296,7 @@
public void evaluate() throws Throwable {
Assume.assumeTrue(shouldEnableOnRavenwood(description));
- RavenwoodRuleImpl.logTestRunner("started", description);
- RavenwoodRuleImpl.init(RavenwoodRule.this);
+ commonPrologue(base, description);
try {
base.evaluate();
RavenwoodRuleImpl.logTestRunner("finished", description);
@@ -310,8 +321,7 @@
public void evaluate() throws Throwable {
Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description));
- RavenwoodRuleImpl.logTestRunner("started", description);
- RavenwoodRuleImpl.init(RavenwoodRule.this);
+ commonPrologue(base, description);
try {
base.evaluate();
} catch (Throwable t) {
@@ -331,4 +341,11 @@
}
};
}
+
+ /**
+ * Do not use it outside ravenwood core classes.
+ */
+ public boolean _ravenwood_private$isOptionalValidationEnabled() {
+ return ENABLE_OPTIONAL_VALIDATION;
+ }
}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 7d172f2..e951351b 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -17,6 +17,7 @@
package android.platform.test.ravenwood;
import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
public class RavenwoodRuleImpl {
public static boolean isOnRavenwood() {
@@ -34,4 +35,8 @@
public static void logTestRunner(String label, Description description) {
// No-op when running on a real device
}
+
+ public static void validate(Statement base, Description description,
+ boolean enableOptionalValidation) {
+ }
}
diff --git a/ravenwood/minimum-test/Android.bp b/ravenwood/minimum-test/Android.bp
index bf3583c..e4ed3d5 100644
--- a/ravenwood/minimum-test/Android.bp
+++ b/ravenwood/minimum-test/Android.bp
@@ -13,6 +13,7 @@
static_libs: [
"androidx.annotation_annotation",
+ "androidx.test.ext.junit",
"androidx.test.rules",
],
diff --git a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java b/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
index 7abfecf..03cfad5 100644
--- a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
+++ b/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
@@ -18,7 +18,7 @@
import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Rule;
diff --git a/services/contextualsearch/OWNERS b/services/contextualsearch/OWNERS
new file mode 100644
index 0000000..0c2612c
--- /dev/null
+++ b/services/contextualsearch/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/contextualsearch/OWNERS
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index a15cb10..a23c08a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -716,7 +716,10 @@
// Programmed
int programedInfo = params[offset] & 0x0F;
if (isValidProgrammedInfo(programedInfo)) {
- if (programedInfo == 0x09 || programedInfo == 0x0B) {
+ offset = offset + 1;
+ // Duration Available (2 bytes)
+ if ((programedInfo == 0x09 || programedInfo == 0x0B)
+ && params.length - offset >= 2) {
durationAvailable = true;
} else {
return true;
@@ -726,16 +729,17 @@
// Non programmed
int nonProgramedErrorInfo = params[offset] & 0x0F;
if (isValidNotProgrammedErrorInfo(nonProgramedErrorInfo)) {
- if (nonProgramedErrorInfo == 0x0E) {
+ offset = offset + 1;
+ // Duration Available (2 bytes)
+ if (nonProgramedErrorInfo == 0x0E && params.length - offset >= 2) {
durationAvailable = true;
} else {
return true;
}
}
}
- offset = offset + 1;
// Duration Available (2 bytes)
- if (durationAvailable && params.length - offset >= 2) {
+ if (durationAvailable) {
return (isValidDurationHours(params[offset]) && isValidMinute(params[offset + 1]));
}
return false;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1c9bbab..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;
@@ -8844,19 +8844,26 @@
}
}
+ private PendingIntent getNotificationTimeoutPendingIntent(NotificationRecord record,
+ int flags) {
+ flags |= PendingIntent.FLAG_IMMUTABLE;
+ return PendingIntent.getBroadcast(getContext(),
+ REQUEST_CODE_TIMEOUT,
+ new Intent(ACTION_NOTIFICATION_TIMEOUT)
+ .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
+ .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
+ .appendPath(record.getKey()).build())
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .putExtra(EXTRA_KEY, record.getKey()),
+ flags);
+ }
+
@VisibleForTesting
@GuardedBy("mNotificationLock")
void scheduleTimeoutLocked(NotificationRecord record) {
if (record.getNotification().getTimeoutAfter() > 0) {
- final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
- REQUEST_CODE_TIMEOUT,
- new Intent(ACTION_NOTIFICATION_TIMEOUT)
- .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
- .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
- .appendPath(record.getKey()).build())
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(EXTRA_KEY, record.getKey()),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ final PendingIntent pi = getNotificationTimeoutPendingIntent(
+ record, PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
}
@@ -8864,6 +8871,16 @@
@VisibleForTesting
@GuardedBy("mNotificationLock")
+ void cancelScheduledTimeoutLocked(NotificationRecord record) {
+ final PendingIntent pi = getNotificationTimeoutPendingIntent(
+ record, PendingIntent.FLAG_CANCEL_CURRENT);
+ if (pi != null) {
+ mAlarmManager.cancel(pi);
+ }
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mNotificationLock")
/**
* Determine whether this notification should attempt to make noise, vibrate, or flash the LED
* @return buzzBeepBlink - bitfield (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)
@@ -9894,21 +9911,7 @@
int rank, int count, boolean wasPosted, String listenerName,
@ElapsedRealtimeLong long cancellationElapsedTimeMs) {
final String canceledKey = r.getKey();
-
- // Get pending intent used to create alarm, use FLAG_NO_CREATE if PendingIntent
- // does not already exist, then null will be returned.
- final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
- REQUEST_CODE_TIMEOUT,
- new Intent(ACTION_NOTIFICATION_TIMEOUT)
- .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
- .appendPath(r.getKey()).build())
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
- PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE);
-
- // Cancel alarm corresponding to pi.
- if (pi != null) {
- mAlarmManager.cancel(pi);
- }
+ cancelScheduledTimeoutLocked(r);
// Record caller.
recordCallerLocked(r);
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/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
index 1688a1a..817901f 100644
--- a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
+++ b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
@@ -32,7 +32,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.IntArray;
import android.util.Pair;
import android.util.Size;
@@ -159,10 +158,6 @@
private InputWindowHandle[] mLastWindowHandles;
- private final Object mIgnoredWindowTokensLock = new Object();
-
- private final ArraySet<IBinder> mIgnoredWindowTokens = new ArraySet<>();
-
private void startHandlerThreadIfNeeded() {
synchronized (mHandlerThreadLock) {
if (mHandler == null) {
@@ -173,18 +168,6 @@
}
}
- void addIgnoredWindowTokens(IBinder token) {
- synchronized (mIgnoredWindowTokensLock) {
- mIgnoredWindowTokens.add(token);
- }
- }
-
- void removeIgnoredWindowTokens(IBinder token) {
- synchronized (mIgnoredWindowTokensLock) {
- mIgnoredWindowTokens.remove(token);
- }
- }
-
void registerListener(IBinder window, ITrustedPresentationListener listener,
TrustedPresentationThresholds thresholds, int id) {
startHandlerThreadIfNeeded();
@@ -271,12 +254,8 @@
ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates =
new ArrayMap<>();
- ArraySet<IBinder> ignoredWindowTokens;
- synchronized (mIgnoredWindowTokensLock) {
- ignoredWindowTokens = new ArraySet<>(mIgnoredWindowTokens);
- }
for (var windowHandle : mLastWindowHandles) {
- if (ignoredWindowTokens.contains(windowHandle.getWindowToken())) {
+ if (!windowHandle.canOccludePresentation) {
ProtoLog.v(WM_DEBUG_TPL, "Skipping %s", windowHandle.name);
continue;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 24e50c5..f5806c0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1148,10 +1148,6 @@
parentWindow.addChild(this, sWindowSubLayerComparator);
}
- if (token.mRoundedCornerOverlay) {
- mWmService.mTrustedPresentationListenerController.addIgnoredWindowTokens(
- getWindowToken());
- }
}
@Override
@@ -1163,6 +1159,9 @@
if (secureWindowState()) {
getPendingTransaction().setSecure(mSurfaceControl, isSecureLocked());
}
+ // All apps should be considered as occluding when computing TrustedPresentation Thresholds.
+ final boolean canOccludePresentation = !mSession.mCanAddInternalSystemWindow;
+ getPendingTransaction().setCanOccludePresentation(mSurfaceControl, canOccludePresentation);
}
void updateTrustedOverlay() {
@@ -2344,9 +2343,6 @@
mSession.onWindowRemoved(this);
mWmService.postWindowRemoveCleanupLocked(this);
- mWmService.mTrustedPresentationListenerController.removeIgnoredWindowTokens(
- getWindowToken());
-
consumeInsetsChange();
}
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/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 4203576..e8b32cd 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -178,7 +178,7 @@
// intents
return PendingIntent.getActivityAsUser(
mContext, /*requestCode=*/0, intent,
- PendingIntent.FLAG_IMMUTABLE, /*options=*/null,
+ PendingIntent.FLAG_MUTABLE, /*options=*/null,
UserHandle.of(mUserId));
}
}
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index 7e709fe..1a9a0e6 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -116,7 +116,7 @@
mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
- providerDataList,
+ /*providerDataList=*/ null,
/*isRequestForAllOptions=*/ true);
List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index fcaef9f..ca23d62 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -30,6 +30,7 @@
import android.credentials.selection.Entry;
import android.credentials.selection.GetCredentialProviderData;
import android.credentials.selection.ProviderPendingIntentResponse;
+import android.os.Bundle;
import android.os.ICancellationSignal;
import android.service.autofill.Flags;
import android.service.credentials.Action;
@@ -76,6 +77,9 @@
@NonNull
private final Map<String, CredentialOption> mBeginGetOptionToCredentialOptionMap;
+ @NonNull
+ private final Map<String, AutofillId> mCredentialEntryKeyToAutofilLIdMap;
+
/** The complete request to be used in the second round. */
private final android.credentials.GetCredentialRequest mCompleteRequest;
@@ -245,6 +249,7 @@
mBeginGetOptionToCredentialOptionMap = new HashMap<>(beginGetOptionToCredentialOptionMap);
mProviderResponseDataHandler = new ProviderResponseDataHandler(
ComponentName.unflattenFromString(hybridService));
+ mCredentialEntryKeyToAutofilLIdMap = new HashMap<>();
}
/** Called when the provider response has been updated by an external source. */
@@ -298,7 +303,7 @@
invokeCallbackOnInternalInvalidState();
return;
}
- onCredentialEntrySelected(providerPendingIntentResponse);
+ onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
break;
case ACTION_ENTRY_KEY:
Action actionEntry = mProviderResponseDataHandler.getActionEntry(entryKey);
@@ -307,7 +312,7 @@
invokeCallbackOnInternalInvalidState();
return;
}
- onActionEntrySelected(providerPendingIntentResponse);
+ onActionEntrySelected(providerPendingIntentResponse, entryKey);
break;
case AUTHENTICATION_ACTION_ENTRY_KEY:
Action authenticationEntry = mProviderResponseDataHandler
@@ -337,7 +342,7 @@
break;
case REMOTE_ENTRY_KEY:
if (mProviderResponseDataHandler.getRemoteEntry(entryKey) != null) {
- onRemoteEntrySelected(providerPendingIntentResponse);
+ onRemoteEntrySelected(providerPendingIntentResponse, entryKey);
} else {
Slog.i(TAG, "Unexpected remote entry key");
invokeCallbackOnInternalInvalidState();
@@ -376,7 +381,7 @@
return null;
}
- private Intent setUpFillInIntentWithFinalRequest(@NonNull String id) {
+ private Intent setUpFillInIntentWithFinalRequest(@NonNull String id, String entryKey) {
// TODO: Determine if we should skip this entry if entry id is not set, or is set
// but does not resolve to a valid option. For now, not skipping it because
// it may be possible that the provider adds their own extras and expects to receive
@@ -392,6 +397,7 @@
.getParcelable(CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
if (autofillId != null && Flags.autofillCredmanIntegration()) {
intent.putExtra(CredentialProviderService.EXTRA_AUTOFILL_ID, autofillId);
+ mCredentialEntryKeyToAutofilLIdMap.put(entryKey, autofillId);
}
return intent.putExtra(
CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
@@ -408,12 +414,13 @@
}
private void onRemoteEntrySelected(
- ProviderPendingIntentResponse providerPendingIntentResponse) {
- onCredentialEntrySelected(providerPendingIntentResponse);
+ ProviderPendingIntentResponse providerPendingIntentResponse, String entryKey) {
+ onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
}
private void onCredentialEntrySelected(
- ProviderPendingIntentResponse providerPendingIntentResponse) {
+ ProviderPendingIntentResponse providerPendingIntentResponse,
+ String entryKey) {
if (providerPendingIntentResponse == null) {
invokeCallbackOnInternalInvalidState();
return;
@@ -430,7 +437,18 @@
GetCredentialResponse getCredentialResponse = PendingIntentResultHandler
.extractGetCredentialResponse(
providerPendingIntentResponse.getResultData());
- if (getCredentialResponse != null) {
+ if (getCredentialResponse != null && getCredentialResponse.getCredential() != null) {
+ Bundle credentialData = getCredentialResponse.getCredential().getData();
+ AutofillId autofillId = mCredentialEntryKeyToAutofilLIdMap.get(entryKey);
+ if (Flags.autofillCredmanIntegration()
+ && entryKey != null && autofillId != null && credentialData != null
+ ) {
+ Slog.d(TAG, "Adding autofillId to credential response: " + autofillId);
+ credentialData.putParcelable(
+ CredentialProviderService.EXTRA_AUTOFILL_ID,
+ mCredentialEntryKeyToAutofilLIdMap.get(entryKey)
+ );
+ }
mCallbacks.onFinalResponseReceived(mComponentName,
getCredentialResponse);
return;
@@ -514,9 +532,9 @@
/** Returns true if either an exception or a response is found. */
private void onActionEntrySelected(ProviderPendingIntentResponse
- providerPendingIntentResponse) {
+ providerPendingIntentResponse, String entryKey) {
Slog.i(TAG, "onActionEntrySelected");
- onCredentialEntrySelected(providerPendingIntentResponse);
+ onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
}
@@ -614,7 +632,7 @@
Entry entry = new Entry(CREDENTIAL_ENTRY_KEY,
id, credentialEntry.getSlice(),
setUpFillInIntentWithFinalRequest(credentialEntry
- .getBeginGetCredentialOptionId()));
+ .getBeginGetCredentialOptionId(), id));
mUiCredentialEntries.put(id, new Pair<>(credentialEntry, entry));
mCredentialEntryTypes.add(credentialEntry.getType());
}
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
index b8dcecd..e27bb4c 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
@@ -320,4 +320,10 @@
mWindowInfosReportedListeners.add(listener);
return this;
}
+
+ @Override
+ public SurfaceControl.Transaction setCanOccludePresentation(SurfaceControl sc,
+ boolean canOccludePresentation) {
+ return this;
+ }
}
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 ef0ac33..8261dee 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1254,6 +1254,11 @@
verify(mAlarmManager).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture());
assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
captor.getValue().getIntent().getPackage());
+
+ mService.cancelScheduledTimeoutLocked(r);
+ verify(mAlarmManager).cancel(captor.capture());
+ assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
+ captor.getValue().getIntent().getPackage());
}
@Test
@@ -11681,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();
@@ -11764,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/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h
index 5d3abc6..9276402 100644
--- a/tools/aapt/ApkBuilder.h
+++ b/tools/aapt/ApkBuilder.h
@@ -44,7 +44,7 @@
android::status_t createSplitForConfigs(const std::set<ConfigDescription>& configs);
/**
- * Adds a file to be written to the final APK. It's name must not collide
+ * Adds a file to be written to the final APK. Its name must not collide
* with that of any files previously added. When a Split APK is being
* generated, duplicates can exist as long as they are in different splits
* (resources.arsc, AndroidManifest.xml).
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 2f2ef92..66a0510 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -187,7 +187,7 @@
" be loaded alongside the base APK at runtime.\n"
" --feature-of\n"
" Builds a split APK that is a feature of the apk specified here. Resources\n"
- " in the base APK can be referenced from the the feature APK.\n"
+ " in the base APK can be referenced from the feature APK.\n"
" --feature-after\n"
" An app can have multiple Feature Split APKs which must be totally ordered.\n"
" If --feature-of is specified, this flag specifies which Feature Split APK\n"