Merge "Invocation shortcut for creating user action results that show overlays." into main
diff --git a/core/api/current.txt b/core/api/current.txt
index ddfd364..5685221 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -43963,11 +43963,11 @@
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT = "satellite_connection_hysteresis_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool";
- field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_esos_inactivity_timeout_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool";
- field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_p2p_sms_inactivity_timeout_sec_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_esos_inactivity_timeout_sec_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_p2p_sms_inactivity_timeout_sec_int";
field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL = "satellite_roaming_p2p_sms_supported_bool";
- field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_screen_off_inactivity_timeout_sec_int";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_screen_off_inactivity_timeout_sec_int";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 1100731..c22f46c 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -646,6 +646,37 @@
private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags);
/**
+ * This list is to hold strong reference to the frozen state callbacks. The callbacks are only
+ * weakly referenced by JNI so the strong references here are needed to keep the callbacks
+ * around until the proxy is GC'ed.
+ */
+ private List<IFrozenStateChangeCallback> mFrozenStateChangeCallbacks =
+ Collections.synchronizedList(new ArrayList<>());
+
+ /**
+ * See {@link IBinder#addFrozenStateChangeCallback(IFrozenStateChangeCallback)}
+ */
+ public void addFrozenStateChangeCallback(IFrozenStateChangeCallback callback)
+ throws RemoteException {
+ addFrozenStateChangeCallbackNative(callback);
+ mFrozenStateChangeCallbacks.add(callback);
+ }
+
+ /**
+ * See {@link IBinder#removeFrozenStateChangeCallback}
+ */
+ public boolean removeFrozenStateChangeCallback(IFrozenStateChangeCallback callback) {
+ mFrozenStateChangeCallbacks.remove(callback);
+ return removeFrozenStateChangeCallbackNative(callback);
+ }
+
+ private native void addFrozenStateChangeCallbackNative(IFrozenStateChangeCallback callback)
+ throws RemoteException;
+
+ private native boolean removeFrozenStateChangeCallbackNative(
+ IFrozenStateChangeCallback callback);
+
+ /**
* Perform a dump on the remote object
*
* @param fd The raw file descriptor that the dump is being sent to.
@@ -730,6 +761,17 @@
}
}
+ private static void invokeFrozenStateChangeCallback(
+ IFrozenStateChangeCallback callback, IBinder binderProxy, int stateIndex) {
+ try {
+ callback.onFrozenStateChanged(binderProxy,
+ IFrozenStateChangeCallback.State.values()[stateIndex]);
+ } catch (RuntimeException exc) {
+ Log.w("BinderNative", "Uncaught exception from frozen state change callback",
+ exc);
+ }
+ }
+
/**
* C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
* native IBinder object, and a DeathRecipientList.
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 50242ba..8185e8e 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -376,4 +376,53 @@
* return value instead.
*/
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);
+
+ /** @hide */
+ interface IFrozenStateChangeCallback {
+ enum State {FROZEN, UNFROZEN};
+
+ /**
+ * Interface for receiving a callback when the process hosting an IBinder
+ * has changed its frozen state.
+ * @param who The IBinder whose hosting process has changed state.
+ * @param state The latest state.
+ */
+ void onFrozenStateChanged(@NonNull IBinder who, State state);
+ }
+
+ /**
+ * {@link addFrozenStateChangeCallback} provides a callback mechanism to notify about process
+ * frozen/unfrozen events. Upon registration and any subsequent state changes, the callback is
+ * invoked with the latest process frozen state.
+ *
+ * <p>If the listener process (the one using this API) is itself frozen, state change events
+ * might be combined into a single one with the latest frozen state. This single event would
+ * then be delivered when the listener process becomes unfrozen. Similarly, if an event happens
+ * before the previous event is consumed, they might be combined. This means the callback might
+ * not be called for every single state change, so don't rely on this API to count how many
+ * times the state has changed.</p>
+ *
+ * <p>The callback is automatically removed when all references to the binder proxy are
+ * dropped.</p>
+ *
+ * <p>You will only receive state change notifications for remote binders, as local binders by
+ * definition can't be frozen without you being frozen too.</p>
+ *
+ * <p>@throws {@link UnsupportedOperationException} if the kernel binder driver does not support
+ * this feature.
+ * @hide
+ */
+ default void addFrozenStateChangeCallback(@NonNull IFrozenStateChangeCallback callback)
+ throws RemoteException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Unregister a {@link IFrozenStateChangeCallback}. The callback will no longer be invoked when
+ * the hosting process changes its frozen state.
+ * @hide
+ */
+ default boolean removeFrozenStateChangeCallback(@NonNull IFrozenStateChangeCallback callback) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 46b4695..921b77d 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -56,11 +56,11 @@
//#undef ALOGV
//#define ALOGV(...) fprintf(stderr, __VA_ARGS__)
-#define DEBUG_DEATH 0
-#if DEBUG_DEATH
-#define LOGDEATH ALOGD
+#define DEBUG_DEATH_FREEZE 0
+#if DEBUG_DEATH_FREEZE
+#define LOG_DEATH_FREEZE ALOGD
#else
-#define LOGDEATH ALOGV
+#define LOG_DEATH_FREEZE ALOGV
#endif
using namespace android;
@@ -116,6 +116,7 @@
jclass mClass;
jmethodID mGetInstance;
jmethodID mSendDeathNotice;
+ jmethodID mInvokeFrozenStateChangeCallback;
// Object state.
jfieldID mNativeData; // Field holds native pointer to BinderProxyNativeData.
@@ -547,23 +548,59 @@
// ----------------------------------------------------------------------------
-// Per-IBinder death recipient bookkeeping. This is how we reconcile local jobject
-// death recipient references passed in through JNI with the permanent corresponding
-// JavaDeathRecipient objects.
+// A JavaRecipient receives either death notifications or frozen state change
+// callbacks from natve code (IBinder) and dispatch the notifications to its
+// corresponding Java listener object.
+//
+// A RecipientList keeps tracks of all JavaRecipients for an IBinder. This way
+// we can find a JavaRecipient given a Java listener object.
+//
+// The implementation is shared between death recipients and frozen state change
+// callbacks via template. For death recipients the template is instantiated as
+// follows:
+//
+// IBinder::DeathRecipient
+// ^
+// |
+// (inherits)
+// |
+// JavaRecipient<IBinder::DeathRecipient> <----> RecipientList<IBinder::DeathRecipient>
+// ^
+// |
+// (inherits)
+// |
+// JavaDeathRecipient
+//
+//
+// The instantiation for frozen state change callbacks are:
+//
+// IBinder::FrozenStateChangeCallback
+// ^
+// |
+// (inherits)
+// |
+// JavaRecipient<IBinder::FrozenStateChangeCallback>
+// ^ ^
+// | |
+// (inherits) +--> RecipientList<IBinder::FrozenStateChangeCallback>
+// |
+// JavaFrozenStateChangeCallback
-class JavaDeathRecipient;
+template <typename T>
+class JavaRecipient;
-class DeathRecipientList : public RefBase {
- List< sp<JavaDeathRecipient> > mList;
+template <typename T>
+class RecipientList : public RefBase {
+ List<sp<JavaRecipient<T> > > mList;
Mutex mLock;
public:
- DeathRecipientList();
- ~DeathRecipientList();
+ RecipientList();
+ ~RecipientList();
- void add(const sp<JavaDeathRecipient>& recipient);
- void remove(const sp<JavaDeathRecipient>& recipient);
- sp<JavaDeathRecipient> find(jobject recipient);
+ void add(const sp<JavaRecipient<T> >& recipient);
+ void remove(const sp<JavaRecipient<T> >& recipient);
+ sp<JavaRecipient<T> > find(jobject recipient);
Mutex& lock(); // Use with care; specifically for mutual exclusion during binder death
};
@@ -584,11 +621,113 @@
#endif // __BIONIC__
#endif // BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
-class JavaDeathRecipient : public IBinder::DeathRecipient
-{
+template <typename T>
+constexpr const char* logPrefix();
+
+template <>
+constexpr const char* logPrefix<IBinder::DeathRecipient>() {
+ return "[DEATH]";
+}
+
+template <>
+constexpr const char* logPrefix<IBinder::FrozenStateChangeCallback>() {
+ return "[FREEZE]";
+}
+
+template <typename T>
+class JavaRecipient : public T {
public:
- JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
+ JavaRecipient(JNIEnv* env, jobject object, const sp<RecipientList<T> >& list,
+ bool useWeakReference)
: mVM(jnienv_to_javavm(env)), mObject(NULL), mObjectWeak(NULL), mList(list) {
+ if (useWeakReference) {
+ mObjectWeak = env->NewWeakGlobalRef(object);
+ } else {
+ mObject = env->NewGlobalRef(object);
+ }
+ // These objects manage their own lifetimes so are responsible for final bookkeeping.
+ // The list holds a strong reference to this object.
+ LOG_DEATH_FREEZE("%s Adding JavaRecipient %p to RecipientList %p", logPrefix<T>(), this,
+ list.get());
+ list->add(this);
+ }
+
+ void clearReference() {
+ sp<RecipientList<T> > list = mList.promote();
+ if (list != NULL) {
+ LOG_DEATH_FREEZE("%s Removing JavaRecipient %p from RecipientList %p", logPrefix<T>(),
+ this, list.get());
+ list->remove(this);
+ } else {
+ LOG_DEATH_FREEZE("%s clearReference() on JavaRecipient %p but RecipientList wp purged",
+ logPrefix<T>(), this);
+ }
+ }
+
+ bool matches(jobject obj) {
+ bool result;
+ JNIEnv* env = javavm_to_jnienv(mVM);
+
+ if (mObject != NULL) {
+ result = env->IsSameObject(obj, mObject);
+ } else {
+ ScopedLocalRef<jobject> me(env, env->NewLocalRef(mObjectWeak));
+ result = env->IsSameObject(obj, me.get());
+ }
+ return result;
+ }
+
+ void warnIfStillLive() {
+ if (mObject != NULL) {
+ // Okay, something is wrong -- we have a hard reference to a live death
+ // recipient on the VM side, but the list is being torn down.
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
+ ScopedLocalRef<jstring> nameRef(env,
+ (jstring)env->CallObjectMethod(objClassRef.get(),
+ gClassOffsets.mGetName));
+ ScopedUtfChars nameUtf(env, nameRef.get());
+ if (nameUtf.c_str() != NULL) {
+ ALOGW("BinderProxy is being destroyed but the application did not call "
+ "unlinkToDeath to unlink all of its death recipients beforehand. "
+ "Releasing leaked death recipient: %s",
+ nameUtf.c_str());
+ } else {
+ ALOGW("BinderProxy being destroyed; unable to get DR object name");
+ env->ExceptionClear();
+ }
+ }
+ }
+
+protected:
+ virtual ~JavaRecipient() {
+ // ALOGI("Removing death ref: recipient=%p\n", mObject);
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ if (mObject != NULL) {
+ env->DeleteGlobalRef(mObject);
+ } else {
+ env->DeleteWeakGlobalRef(mObjectWeak);
+ }
+ }
+
+ JavaVM* const mVM;
+
+ // If useWeakReference is false (e.g. JavaDeathRecipient when target sdk version < 35), the
+ // Java-side Recipient is strongly referenced from mObject initially, and may later be demoted
+ // to a weak reference (mObjectWeak), e.g. upon linkToDeath() and then after binderDied() is
+ // called.
+ // If useWeakReference is true, the strong reference is never made here (i.e. mObject == NULL
+ // always). Instead, the strong reference to the Java-side Recipient is made in
+ // BinderProxy.{mDeathRecipients,mFrozenStateChangeCallbacks}. In the native world, only the
+ // weak reference is kept.
+ jobject mObject;
+ jweak mObjectWeak;
+ wp<RecipientList<T> > mList;
+};
+
+class JavaDeathRecipient : public JavaRecipient<IBinder::DeathRecipient> {
+public:
+ static bool useWeakReference() {
// b/298374304: For apps targeting Android V or beyond, we no longer hold the global JNI ref
// to the death recipient objects. This is to prevent the memory leak which can happen when
// the death recipient object internally has a strong reference to the proxy object. Under
@@ -604,25 +743,26 @@
// reference to. If however you want to get binderDied() regardless of the proxy object's
// lifecycle, keep a strong reference to the death recipient object by yourself.
#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
- if (target_sdk_is_at_least_vic()) {
- mObjectWeak = env->NewWeakGlobalRef(object);
- } else
+ return target_sdk_is_at_least_vic();
+#else
+ return false;
#endif
- {
- mObject = env->NewGlobalRef(object);
- }
- // These objects manage their own lifetimes so are responsible for final bookkeeping.
- // The list holds a strong reference to this object.
- LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
- list->add(this);
+ }
+ JavaDeathRecipient(JNIEnv* env, jobject object,
+ const sp<RecipientList<IBinder::DeathRecipient> >& list)
+ : JavaRecipient(env, object, list, useWeakReference()) {
gNumDeathRefsCreated.fetch_add(1, std::memory_order_relaxed);
gcIfManyNewRefs(env);
}
+ ~JavaDeathRecipient() {
+ gNumDeathRefsDeleted.fetch_add(1, std::memory_order_relaxed);
+ }
+
void binderDied(const wp<IBinder>& who)
{
- LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
+ LOG_DEATH_FREEZE("Receiving binderDied() on JavaDeathRecipient %p\n", this);
if (mObject == NULL && mObjectWeak == NULL) {
return;
}
@@ -662,7 +802,7 @@
// with our containing DeathRecipientList so that we can't delete the global ref on mObject
// while the list is being iterated.
if (mObject != NULL) {
- sp<DeathRecipientList> list = mList.promote();
+ auto list = mList.promote();
if (list != NULL) {
AutoMutex _l(list->lock());
@@ -673,126 +813,96 @@
}
}
- void clearReference()
- {
- sp<DeathRecipientList> list = mList.promote();
- if (list != NULL) {
- LOGDEATH("Removing JDR %p from DRL %p", this, list.get());
- list->remove(this);
- } else {
- LOGDEATH("clearReference() on JDR %p but DRL wp purged", this);
- }
- }
-
- bool matches(jobject obj) {
- bool result;
- JNIEnv* env = javavm_to_jnienv(mVM);
-
- if (mObject != NULL) {
- result = env->IsSameObject(obj, mObject);
- } else {
- ScopedLocalRef<jobject> me(env, env->NewLocalRef(mObjectWeak));
- result = env->IsSameObject(obj, me.get());
- }
- return result;
- }
-
- void warnIfStillLive() {
- if (mObject != NULL) {
- // Okay, something is wrong -- we have a hard reference to a live death
- // recipient on the VM side, but the list is being torn down.
- JNIEnv* env = javavm_to_jnienv(mVM);
- ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
- ScopedLocalRef<jstring> nameRef(env,
- (jstring) env->CallObjectMethod(objClassRef.get(), gClassOffsets.mGetName));
- ScopedUtfChars nameUtf(env, nameRef.get());
- if (nameUtf.c_str() != NULL) {
- ALOGW("BinderProxy is being destroyed but the application did not call "
- "unlinkToDeath to unlink all of its death recipients beforehand. "
- "Releasing leaked death recipient: %s", nameUtf.c_str());
- } else {
- ALOGW("BinderProxy being destroyed; unable to get DR object name");
- env->ExceptionClear();
- }
- }
- }
-
-protected:
- virtual ~JavaDeathRecipient()
- {
- //ALOGI("Removing death ref: recipient=%p\n", mObject);
- gNumDeathRefsDeleted.fetch_add(1, std::memory_order_relaxed);
- JNIEnv* env = javavm_to_jnienv(mVM);
- if (mObject != NULL) {
- env->DeleteGlobalRef(mObject);
- } else {
- env->DeleteWeakGlobalRef(mObjectWeak);
- }
- }
-
private:
- JavaVM* const mVM;
-
- // If target sdk version < 35, the Java-side DeathRecipient is strongly referenced from mObject
- // upon linkToDeath() and then after binderDied() is called, the strong reference is demoted to
- // a weak reference (mObjectWeak).
- // If target sdk version >= 35, the strong reference is never made here (i.e. mObject == NULL
- // always). Instead, the strong reference to the Java-side DeathRecipient is made in
- // BinderProxy.mDeathRecipients. In the native world, only the weak reference is kept.
- jobject mObject;
- jweak mObjectWeak;
- wp<DeathRecipientList> mList;
-
// Whether binderDied was called or not.
bool mFired = false;
};
+class JavaFrozenStateChangeCallback : public JavaRecipient<IBinder::FrozenStateChangeCallback> {
+public:
+ JavaFrozenStateChangeCallback(
+ JNIEnv* env, jobject object,
+ const sp<RecipientList<IBinder::FrozenStateChangeCallback> >& list)
+ : JavaRecipient(env, object, list, /*useWeakReference=*/true) {}
+
+ void onStateChanged(const wp<IBinder>& who, State state) {
+ LOG_DEATH_FREEZE("Receiving onStateChanged() on JavaFrozenStateChangeCallback %p. state: "
+ "%s\n",
+ this, state == State::FROZEN ? "FROZEN" : "UNFROZEN");
+ if (mObjectWeak == NULL) {
+ return;
+ }
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
+
+ // Hold a local reference to the recipient. This may fail if the recipient is weakly
+ // referenced, in which case we can't deliver the notification.
+ ScopedLocalRef<jobject> jCallback(env, env->NewLocalRef(mObjectWeak));
+ if (jCallback.get() == NULL) {
+ return;
+ }
+ env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
+ gBinderProxyOffsets.mInvokeFrozenStateChangeCallback,
+ jCallback.get(), jBinderProxy.get(), state);
+ if (env->ExceptionCheck()) {
+ jthrowable excep = env->ExceptionOccurred();
+ binder_report_exception(env, excep,
+ "*** Uncaught exception returned from frozen state change "
+ "notification!");
+ }
+ }
+};
+
// ----------------------------------------------------------------------------
-DeathRecipientList::DeathRecipientList() {
- LOGDEATH("New DRL @ %p", this);
+template <typename T>
+RecipientList<T>::RecipientList() {
+ LOG_DEATH_FREEZE("%s New RecipientList @ %p", logPrefix<T>(), this);
}
-DeathRecipientList::~DeathRecipientList() {
- LOGDEATH("Destroy DRL @ %p", this);
+template <typename T>
+RecipientList<T>::~RecipientList() {
+ LOG_DEATH_FREEZE("%s Destroy RecipientList @ %p", logPrefix<T>(), this);
AutoMutex _l(mLock);
- // Should never happen -- the JavaDeathRecipient objects that have added themselves
+ // Should never happen -- the JavaRecipientList objects that have added themselves
// to the list are holding references on the list object. Only when they are torn
// down can the list header be destroyed.
if (mList.size() > 0) {
- List< sp<JavaDeathRecipient> >::iterator iter;
- for (iter = mList.begin(); iter != mList.end(); iter++) {
+ for (auto iter = mList.begin(); iter != mList.end(); iter++) {
(*iter)->warnIfStillLive();
}
}
}
-void DeathRecipientList::add(const sp<JavaDeathRecipient>& recipient) {
+template <typename T>
+void RecipientList<T>::add(const sp<JavaRecipient<T> >& recipient) {
AutoMutex _l(mLock);
- LOGDEATH("DRL @ %p : add JDR %p", this, recipient.get());
+ LOG_DEATH_FREEZE("%s RecipientList @ %p : add JavaRecipient %p", logPrefix<T>(), this,
+ recipient.get());
mList.push_back(recipient);
}
-void DeathRecipientList::remove(const sp<JavaDeathRecipient>& recipient) {
+template <typename T>
+void RecipientList<T>::remove(const sp<JavaRecipient<T> >& recipient) {
AutoMutex _l(mLock);
- List< sp<JavaDeathRecipient> >::iterator iter;
- for (iter = mList.begin(); iter != mList.end(); iter++) {
+ for (auto iter = mList.begin(); iter != mList.end(); iter++) {
if (*iter == recipient) {
- LOGDEATH("DRL @ %p : remove JDR %p", this, recipient.get());
+ LOG_DEATH_FREEZE("%s RecipientList @ %p : remove JavaRecipient %p", logPrefix<T>(),
+ this, recipient.get());
mList.erase(iter);
return;
}
}
}
-sp<JavaDeathRecipient> DeathRecipientList::find(jobject recipient) {
+template <typename T>
+sp<JavaRecipient<T> > RecipientList<T>::find(jobject recipient) {
AutoMutex _l(mLock);
- List< sp<JavaDeathRecipient> >::iterator iter;
- for (iter = mList.begin(); iter != mList.end(); iter++) {
+ for (auto iter = mList.begin(); iter != mList.end(); iter++) {
if ((*iter)->matches(recipient)) {
return *iter;
}
@@ -800,10 +910,14 @@
return NULL;
}
-Mutex& DeathRecipientList::lock() {
+template <typename T>
+Mutex& RecipientList<T>::lock() {
return mLock;
}
+using DeathRecipientList = RecipientList<IBinder::DeathRecipient>;
+using FrozenStateChangeCallbackList = RecipientList<IBinder::FrozenStateChangeCallback>;
+
// ----------------------------------------------------------------------------
namespace android {
@@ -821,6 +935,11 @@
// Death recipients for mObject. Reference counted only because DeathRecipients
// hold a weak reference that can be temporarily promoted.
sp<DeathRecipientList> mOrgue; // Death recipients for mObject.
+
+ // Frozen state change callbacks for mObject. Reference counted only because
+ // JavaFrozenStateChangeCallback hold a weak reference that can be
+ // temporarily promoted.
+ sp<FrozenStateChangeCallbackList> mFrozenStateChangCallbackList;
};
BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
@@ -840,12 +959,13 @@
if (val->checkSubclass(&gBinderOffsets)) {
// It's a JavaBBinder created by ibinderForJavaObject. Already has Java object.
jobject object = static_cast<JavaBBinder*>(val.get())->object();
- LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
+ LOG_DEATH_FREEZE("objectForBinder %p: it's our own %p!\n", val.get(), object);
return object;
}
BinderProxyNativeData* nativeData = new BinderProxyNativeData();
nativeData->mOrgue = new DeathRecipientList;
+ nativeData->mFrozenStateChangCallbackList = new FrozenStateChangeCallbackList;
nativeData->mObject = val;
jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
@@ -1448,7 +1568,7 @@
BinderProxyNativeData *nd = getBPNativeData(env, obj);
IBinder* target = nd->mObject.get();
- LOGDEATH("linkToDeath: binder=%p recipient=%p\n", target, recipient);
+ LOG_DEATH_FREEZE("linkToDeath: binder=%p recipient=%p\n", target, recipient);
if (!target->localBinder()) {
DeathRecipientList* list = nd->mOrgue.get();
@@ -1479,15 +1599,15 @@
return JNI_FALSE;
}
- LOGDEATH("unlinkToDeath: binder=%p recipient=%p\n", target, recipient);
+ LOG_DEATH_FREEZE("unlinkToDeath: binder=%p recipient=%p\n", target, recipient);
if (!target->localBinder()) {
status_t err = NAME_NOT_FOUND;
// If we find the matching recipient, proceed to unlink using that
DeathRecipientList* list = nd->mOrgue.get();
- sp<JavaDeathRecipient> origJDR = list->find(recipient);
- LOGDEATH(" unlink found list %p and JDR %p", list, origJDR.get());
+ sp<JavaRecipient<IBinder::DeathRecipient> > origJDR = list->find(recipient);
+ LOG_DEATH_FREEZE(" unlink found list %p and JDR %p", list, origJDR.get());
if (origJDR != NULL) {
wp<IBinder::DeathRecipient> dr;
err = target->unlinkToDeath(origJDR, NULL, flags, &dr);
@@ -1513,11 +1633,85 @@
return res;
}
+static void android_os_BinderProxy_addFrozenStateChangeCallback(
+ JNIEnv* env, jobject obj,
+ jobject callback) // throws RemoteException
+{
+ if (callback == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return;
+ }
+
+ BinderProxyNativeData* nd = getBPNativeData(env, obj);
+ IBinder* target = nd->mObject.get();
+
+ LOG_DEATH_FREEZE("addFrozenStateChangeCallback: binder=%p callback=%p\n", target, callback);
+
+ if (!target->localBinder()) {
+ FrozenStateChangeCallbackList* list = nd->mFrozenStateChangCallbackList.get();
+ auto jfscc = sp<JavaFrozenStateChangeCallback>::make(env, callback, list);
+ status_t err = target->addFrozenStateChangeCallback(jfscc);
+ if (err != NO_ERROR) {
+ // Failure adding the callback, so clear its reference now.
+ jfscc->clearReference();
+ signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
+ }
+ }
+}
+
+static jboolean android_os_BinderProxy_removeFrozenStateChangeCallback(JNIEnv* env, jobject obj,
+ jobject callback) {
+ jboolean res = JNI_FALSE;
+ if (callback == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return res;
+ }
+
+ BinderProxyNativeData* nd = getBPNativeData(env, obj);
+ IBinder* target = nd->mObject.get();
+ if (target == NULL) {
+ ALOGW("Binder has been finalized when calling removeFrozenStateChangeCallback() with "
+ "callback=%p)\n",
+ callback);
+ return JNI_FALSE;
+ }
+
+ LOG_DEATH_FREEZE("removeFrozenStateChangeCallback: binder=%p callback=%p\n", target, callback);
+
+ if (!target->localBinder()) {
+ status_t err = NAME_NOT_FOUND;
+
+ // If we find the matching callback, proceed to unlink using that
+ FrozenStateChangeCallbackList* list = nd->mFrozenStateChangCallbackList.get();
+ sp<JavaRecipient<IBinder::FrozenStateChangeCallback> > origJFSCC = list->find(callback);
+ LOG_DEATH_FREEZE(" removeFrozenStateChangeCallback found list %p and JFSCC %p", list,
+ origJFSCC.get());
+ if (origJFSCC != NULL) {
+ err = target->removeFrozenStateChangeCallback(origJFSCC);
+ if (err == NO_ERROR) {
+ origJFSCC->clearReference();
+ }
+ }
+
+ if (err == NO_ERROR || err == DEAD_OBJECT) {
+ res = JNI_TRUE;
+ } else {
+ jniThrowException(env, "java/util/NoSuchElementException",
+ base::StringPrintf("Frozen state change callback does not exist (%s)",
+ statusToString(err).c_str())
+ .c_str());
+ }
+ }
+
+ return res;
+}
+
static void BinderProxy_destroy(void* rawNativeData)
{
BinderProxyNativeData * nativeData = (BinderProxyNativeData *) rawNativeData;
- LOGDEATH("Destroying BinderProxy: binder=%p drl=%p\n",
- nativeData->mObject.get(), nativeData->mOrgue.get());
+ LOG_DEATH_FREEZE("Destroying BinderProxy: binder=%p drl=%p fsccl=%p\n",
+ nativeData->mObject.get(), nativeData->mOrgue.get(),
+ nativeData->mFrozenStateChangCallbackList.get());
delete nativeData;
IPCThreadState::self()->flushCommands();
}
@@ -1552,6 +1746,10 @@
{"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
{"linkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
{"unlinkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
+ {"addFrozenStateChangeCallbackNative",
+ "(Landroid/os/IBinder$IFrozenStateChangeCallback;)V", (void*)android_os_BinderProxy_addFrozenStateChangeCallback},
+ {"removeFrozenStateChangeCallbackNative",
+ "(Landroid/os/IBinder$IFrozenStateChangeCallback;)Z", (void*)android_os_BinderProxy_removeFrozenStateChangeCallback},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
{"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension},
};
@@ -1574,6 +1772,10 @@
gBinderProxyOffsets.mSendDeathNotice =
GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
"(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
+ gBinderProxyOffsets.mInvokeFrozenStateChangeCallback =
+ GetStaticMethodIDOrDie(env, clazz, "invokeFrozenStateChangeCallback",
+ "(Landroid/os/IBinder$IFrozenStateChangeCallback;Landroid/os/"
+ "IBinder;I)V");
gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
clazz = FindClassOrDie(env, "java/lang/Class");
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index b0e48f1..99cbf05 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -21,6 +21,7 @@
srcs: [
"DisabledTestApp/src/**/*.java",
"EnabledTestApp/src/**/*.java",
+ "BinderFrozenStateChangeCallbackTestApp/src/**/*.java",
"BinderProxyCountingTestApp/src/**/*.java",
"BinderProxyCountingTestService/src/**/*.java",
"BinderDeathRecipientHelperApp/src/**/*.java",
@@ -138,6 +139,7 @@
":BinderDeathRecipientHelperApp1",
":BinderDeathRecipientHelperApp2",
":com.android.cts.helpers.aosp",
+ ":BinderFrozenStateChangeCallbackTestApp",
":BinderProxyCountingTestApp",
":BinderProxyCountingTestService",
":AppThatUsesAppOps",
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 99b73a4..b1f1e2c 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -22,6 +22,7 @@
<option name="test-file-name" value="FrameworksCoreTests.apk" />
<option name="test-file-name" value="BinderDeathRecipientHelperApp1.apk" />
<option name="test-file-name" value="BinderDeathRecipientHelperApp2.apk" />
+ <option name="test-file-name" value="BinderFrozenStateChangeCallbackTestApp.apk" />
<option name="test-file-name" value="BinderProxyCountingTestApp.apk" />
<option name="test-file-name" value="BinderProxyCountingTestService.apk" />
<option name="test-file-name" value="AppThatUsesAppOps.apk" />
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp
new file mode 100644
index 0000000..de97dda
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp
@@ -0,0 +1,34 @@
+// 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 {
+ // 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_test_helper_app {
+ name: "BinderFrozenStateChangeCallbackTestApp",
+
+ static_libs: ["coretests-aidl"],
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.xml b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..29c8f55
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.bfscctestapp">
+
+ <application>
+ <service android:name=".BfsccTestAppCmdService"
+ android:exported="true"/>
+ </application>
+</manifest>
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
new file mode 100644
index 0000000..77e8a40
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
@@ -0,0 +1,71 @@
+/*
+ * 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.frameworks.coretests.bfscctestapp;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.frameworks.coretests.aidl.IBfsccTestAppCmdService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class BfsccTestAppCmdService extends Service {
+ private IBfsccTestAppCmdService.Stub mBinder = new IBfsccTestAppCmdService.Stub() {
+ private final LinkedBlockingQueue<IBinder.IFrozenStateChangeCallback.State> mNotifications =
+ new LinkedBlockingQueue<>();
+
+ @Override
+ public void listenTo(IBinder binder) throws RemoteException {
+ binder.addFrozenStateChangeCallback(
+ (IBinder who, IBinder.IFrozenStateChangeCallback.State state)
+ -> mNotifications.offer(state));
+ }
+
+ @Override
+ public boolean[] waitAndConsumeNotifications() {
+ List<Boolean> results = new ArrayList<>();
+ try {
+ IBinder.IFrozenStateChangeCallback.State state =
+ mNotifications.poll(5, TimeUnit.SECONDS);
+ if (state != null) {
+ results.add(state == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ }
+ } catch (InterruptedException e) {
+ return null;
+ }
+ while (mNotifications.size() > 0) {
+ results.add(mNotifications.poll()
+ == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ }
+ boolean[] convertedResults = new boolean[results.size()];
+ for (int i = 0; i < results.size(); i++) {
+ convertedResults[i] = results.get(i).booleanValue();
+ }
+ return convertedResults;
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
index 41b4c69..09d79a6 100644
--- a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
+++ b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
@@ -50,4 +50,4 @@
public IBinder onBind(Intent intent) {
return mBinder;
}
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl
new file mode 100644
index 0000000..d8d7dc4
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.frameworks.coretests.aidl;
+
+interface IBfsccTestAppCmdService {
+ void listenTo(IBinder binder);
+ boolean[] waitAndConsumeNotifications();
+}
diff --git a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
new file mode 100644
index 0000000..ee2e7e0
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
@@ -0,0 +1,267 @@
+/*
+ * 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 android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.frameworks.coretests.aidl.IBfsccTestAppCmdService;
+import com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Queue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests functionality of {@link android.os.IBinder.IFrozenStateChangeCallback}.
+ */
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+public class BinderFrozenStateChangeNotificationTest {
+ private static final String TAG = BinderFrozenStateChangeNotificationTest.class.getSimpleName();
+
+ private static final String TEST_PACKAGE_NAME_1 =
+ "com.android.frameworks.coretests.bfscctestapp";
+ private static final String TEST_PACKAGE_NAME_2 =
+ "com.android.frameworks.coretests.bdr_helper_app1";
+ private static final String TEST_APP_CMD_SERVICE =
+ TEST_PACKAGE_NAME_1 + ".BfsccTestAppCmdService";
+
+ private static final int CALLBACK_WAIT_TIMEOUT_SECS = 5;
+
+ private IBfsccTestAppCmdService mBfsccTestAppCmdService;
+ private ServiceConnection mTestAppConnection;
+ private Context mContext;
+ private Handler mHandler;
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mHandler = new Handler(Looper.getMainLooper());
+ ((ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE)).killUid(
+ mContext.getPackageManager().getPackageUid(TEST_PACKAGE_NAME_1, 0),
+ "Wiping Test Package");
+ mTestAppConnection = bindService();
+ }
+
+ private IBinder getNewRemoteBinder(String testPackage) throws InterruptedException {
+ final CountDownLatch resultLatch = new CountDownLatch(1);
+ final AtomicInteger resultCode = new AtomicInteger(Activity.RESULT_CANCELED);
+ final AtomicReference<Bundle> resultExtras = new AtomicReference<>();
+
+ final Intent intent = new Intent(TestCommsReceiver.ACTION_GET_BINDER)
+ .setClassName(testPackage, TestCommsReceiver.class.getName());
+ mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ resultCode.set(getResultCode());
+ resultExtras.set(getResultExtras(true));
+ resultLatch.countDown();
+ }
+ }, mHandler, Activity.RESULT_CANCELED, null, null);
+
+ assertTrue("Request for binder timed out", resultLatch.await(5, TimeUnit.SECONDS));
+ assertEquals(Activity.RESULT_OK, resultCode.get());
+ return resultExtras.get().getBinder(TestCommsReceiver.EXTRA_KEY_BINDER);
+ }
+
+ private ServiceConnection bindService()
+ throws Exception {
+ final CountDownLatch bindLatch = new CountDownLatch(1);
+ ServiceConnection connection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.i(TAG, "Service connected");
+ mBfsccTestAppCmdService = IBfsccTestAppCmdService.Stub.asInterface(service);
+ bindLatch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.i(TAG, "Service disconnected");
+ }
+ };
+ mContext.bindService(
+ new Intent().setComponent(
+ new ComponentName(TEST_PACKAGE_NAME_1, TEST_APP_CMD_SERVICE)),
+ connection,
+ Context.BIND_AUTO_CREATE
+ | Context.BIND_NOT_FOREGROUND);
+ if (!bindLatch.await(5, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for the service to bind");
+ }
+ return connection;
+ }
+
+ private void unbindService(ServiceConnection service) {
+ if (service != null) {
+ mContext.unbindService(service);
+ }
+ }
+
+ @Test
+ public void onStateChangeCalled() throws Exception {
+ final LinkedBlockingQueue<Boolean> results = new LinkedBlockingQueue<>();
+ if (createCallback(mBfsccTestAppCmdService.asBinder(), results) == null) {
+ return;
+ }
+ ensureUnfrozenCallback(results);
+ freezeApp1();
+ ensureFrozenCallback(results);
+ unfreezeApp1();
+ ensureUnfrozenCallback(results);
+ }
+
+ @Test
+ public void onStateChangeNotCalledAfterCallbackRemoved() throws Exception {
+ final LinkedBlockingQueue<Boolean> results = new LinkedBlockingQueue<>();
+ IBinder.IFrozenStateChangeCallback callback;
+ if ((callback = createCallback(mBfsccTestAppCmdService.asBinder(), results)) == null) {
+ return;
+ }
+ ensureUnfrozenCallback(results);
+ mBfsccTestAppCmdService.asBinder().removeFrozenStateChangeCallback(callback);
+ freezeApp1();
+ assertEquals("No more callbacks should be invoked.", 0, results.size());
+ }
+
+ @Test
+ public void multipleCallbacks() throws Exception {
+ final LinkedBlockingQueue<Boolean> results1 = new LinkedBlockingQueue<>();
+ final LinkedBlockingQueue<Boolean> results2 = new LinkedBlockingQueue<>();
+ IBinder.IFrozenStateChangeCallback callback1;
+ if ((callback1 = createCallback(mBfsccTestAppCmdService.asBinder(), results1)) == null) {
+ return;
+ }
+ ensureUnfrozenCallback(results1);
+ freezeApp1();
+ ensureFrozenCallback(results1);
+ if (createCallback(mBfsccTestAppCmdService.asBinder(), results2) == null) {
+ return;
+ }
+ ensureFrozenCallback(results2);
+
+ unfreezeApp1();
+ ensureUnfrozenCallback(results1);
+ ensureUnfrozenCallback(results2);
+
+ mBfsccTestAppCmdService.asBinder().removeFrozenStateChangeCallback(callback1);
+ freezeApp1();
+ assertEquals("No more callbacks should be invoked.", 0, results1.size());
+ ensureFrozenCallback(results2);
+ }
+
+ @Test
+ public void onStateChangeCalledWithTheRightBinder() throws Exception {
+ final IBinder binder = mBfsccTestAppCmdService.asBinder();
+ final LinkedBlockingQueue<IBinder> results = new LinkedBlockingQueue<>();
+ IBinder.IFrozenStateChangeCallback callback =
+ (IBinder who, IBinder.IFrozenStateChangeCallback.State state) -> results.offer(who);
+ try {
+ binder.addFrozenStateChangeCallback(callback);
+ } catch (UnsupportedOperationException e) {
+ return;
+ }
+ assertEquals("Callback received the wrong Binder object.",
+ binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ freezeApp1();
+ assertEquals("Callback received the wrong Binder object.",
+ binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ unfreezeApp1();
+ assertEquals("Callback received the wrong Binder object.",
+ binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ }
+
+ @After
+ public void tearDown() {
+ if (mTestAppConnection != null) {
+ mContext.unbindService(mTestAppConnection);
+ }
+ }
+
+ private IBinder.IFrozenStateChangeCallback createCallback(IBinder binder, Queue<Boolean> queue)
+ throws RemoteException {
+ try {
+ final IBinder.IFrozenStateChangeCallback callback =
+ (IBinder who, IBinder.IFrozenStateChangeCallback.State state) ->
+ queue.offer(state == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+ binder.addFrozenStateChangeCallback(callback);
+ return callback;
+ } catch (UnsupportedOperationException e) {
+ return null;
+ }
+ }
+
+ private void ensureFrozenCallback(LinkedBlockingQueue<Boolean> queue)
+ throws InterruptedException {
+ assertEquals(Boolean.TRUE, queue.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ }
+
+ private void ensureUnfrozenCallback(LinkedBlockingQueue<Boolean> queue)
+ throws InterruptedException {
+ assertEquals(Boolean.FALSE, queue.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+ }
+
+ private String executeShellCommand(String cmd) throws Exception {
+ return UiDevice.getInstance(
+ InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+ }
+
+ private void freezeApp1() throws Exception {
+ executeShellCommand("am freeze " + TEST_PACKAGE_NAME_1);
+ }
+
+ private void freezeApp2() throws Exception {
+ executeShellCommand("am freeze " + TEST_PACKAGE_NAME_2);
+ }
+
+ private void unfreezeApp1() throws Exception {
+ executeShellCommand("am unfreeze " + TEST_PACKAGE_NAME_1);
+ }
+
+ private void unfreezeApp2() throws Exception {
+ executeShellCommand("am unfreeze " + TEST_PACKAGE_NAME_2);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 66de3d7..397cdcf 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -124,6 +124,16 @@
return this;
}
+ @Override
+ public void addFrozenStateChangeCallback(IFrozenStateChangeCallback callback)
+ throws RemoteException {
+ }
+
+ @Override
+ public boolean removeFrozenStateChangeCallback(IFrozenStateChangeCallback callback) {
+ return false;
+ }
+
public void die() {
isAlive = false;
if (mRecipient != null) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 121bd3e..bfbf41d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -99,6 +99,12 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print("SyncDisabledForTests: ");
+ MyShellCommand.getSyncDisabledForTests(pw, pw);
+
+ pw.print("Is mainline: ");
+ pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService());
+
final IContentProvider iprovider = mProvider.getIContentProvider();
pw.println("DeviceConfig flags:");
for (String line : MyShellCommand.listAll(iprovider)) {
@@ -232,6 +238,17 @@
return Binder.getCallingUid() == Process.ROOT_UID;
}
+ private static int getSyncDisabledForTests(PrintWriter pOut, PrintWriter pErr) {
+ int syncDisabledModeInt = DeviceConfig.getSyncDisabledMode();
+ String syncDisabledModeString = formatSyncDisabledMode(syncDisabledModeInt);
+ if (syncDisabledModeString == null) {
+ pErr.println("Unknown mode: " + syncDisabledModeInt);
+ return -1;
+ }
+ pOut.println(syncDisabledModeString);
+ return 0;
+ }
+
public static HashMap<String, String> getAllFlags(IContentProvider provider) {
HashMap<String, String> allFlags = new HashMap<String, String>();
for (DeviceConfig.Properties properties : DeviceConfig.getAllProperties()) {
@@ -597,14 +614,7 @@
DeviceConfig.setSyncDisabledMode(syncDisabledModeArg);
break;
case GET_SYNC_DISABLED_FOR_TESTS:
- int syncDisabledModeInt = DeviceConfig.getSyncDisabledMode();
- String syncDisabledModeString = formatSyncDisabledMode(syncDisabledModeInt);
- if (syncDisabledModeString == null) {
- perr.println("Unknown mode: " + syncDisabledModeInt);
- return -1;
- }
- pout.println(syncDisabledModeString);
- break;
+ return getSyncDisabledForTests(pout, perr);
default:
perr.println("Unspecified command");
return -1;
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 4c0feb8..56c08b9 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -853,19 +853,32 @@
content,
element.key,
transition,
+ isInContent = { it in element.stateByContent },
)
}
-internal fun shouldPlaceOrComposeSharedElement(
+internal inline fun shouldPlaceOrComposeSharedElement(
layoutImpl: SceneTransitionLayoutImpl,
content: ContentKey,
element: ElementKey,
transition: TransitionState.Transition,
+ isInContent: (ContentKey) -> Boolean,
): Boolean {
- // If we are overscrolling, only place/compose the element in the overscrolling scene.
- val overscrollScene = transition.currentOverscrollSpec?.content
- if (overscrollScene != null) {
- return content == overscrollScene
+ val overscrollContent = transition.currentOverscrollSpec?.content
+ if (overscrollContent != null) {
+ return when (transition) {
+ // If we are overscrolling between scenes, only place/compose the element in the
+ // overscrolling scene.
+ is TransitionState.Transition.ChangeScene -> content == overscrollContent
+
+ // If we are overscrolling an overlay, place/compose the element if [content] is the
+ // overscrolling content or if [content] is the current scene and the overscrolling
+ // overlay does not contain the element.
+ is TransitionState.Transition.ReplaceOverlay,
+ is TransitionState.Transition.ShowOrHideOverlay ->
+ content == overscrollContent ||
+ (content == transition.currentScene && !isInContent(overscrollContent))
+ }
}
val scenePicker = element.contentPicker
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index 715222c..471ad3f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -194,11 +194,13 @@
is TransitionState.Transition -> {
// During transitions, always compose movable elements in the scene picked by their
// content picker.
+ val contents = element.contentPicker.contents
shouldPlaceOrComposeSharedElement(
layoutImpl,
content,
element,
elementState,
+ isInContent = { contents.contains(it) }
)
}
}
@@ -208,8 +210,8 @@
element: MovableElementKey,
transitionStates: List<TransitionState>,
): TransitionState? {
- val content = element.contentPicker.contents
- return elementState(transitionStates, isInContent = { content.contains(it) })
+ val contents = element.contentPicker.contents
+ return elementState(transitionStates, isInContent = { contents.contains(it) })
}
private fun movableElementContentWhenIdle(
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
index c25478b..471362b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
@@ -18,6 +18,7 @@
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
@@ -44,9 +45,12 @@
import com.android.compose.animation.scene.TestOverlays.OverlayB
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.test.assertSizeIsEqualTo
+import com.android.compose.test.setContentAndCreateMainScope
import com.android.compose.test.subjects.assertThat
+import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -648,4 +652,42 @@
}
}
}
+
+ @Test
+ fun overscrollingOverlay_movableElementNotInOverlay() {
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutStateImpl(
+ SceneA,
+ transitions {
+ // Make OverlayA overscrollable.
+ overscroll(OverlayA, orientation = Orientation.Horizontal) {
+ translate(ElementKey("elementThatDoesNotExist"), x = 10.dp)
+ }
+ }
+ )
+ }
+
+ val key = MovableElementKey("Foo", contents = setOf(SceneA))
+ val movableElementChildTag = "movableElementChildTag"
+ val scope =
+ rule.setContentAndCreateMainScope {
+ SceneTransitionLayout(state) {
+ scene(SceneA) {
+ MovableElement(key, Modifier) {
+ content { Box(Modifier.testTag(movableElementChildTag).size(100.dp)) }
+ }
+ }
+ overlay(OverlayA) { /* Does not contain the element. */ }
+ }
+ }
+
+ // Overscroll on Overlay A.
+ scope.launch { state.startTransition(transition(SceneA, OverlayA, progress = { 1.5f })) }
+ rule
+ .onNode(hasTestTag(movableElementChildTag) and inContent(SceneA))
+ .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+ .assertSizeIsEqualTo(100.dp)
+ .assertIsDisplayed()
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index cd20a29a..d356c25 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -32,7 +32,7 @@
import com.android.compose.animation.scene.transition.link.StateLink
import com.android.compose.animation.scene.transition.seekToScene
import com.android.compose.test.MonotonicClockTestScope
-import com.android.compose.test.TestTransition
+import com.android.compose.test.TestSceneTransition
import com.android.compose.test.runMonotonicClockTest
import com.android.compose.test.transition
import com.google.common.truth.Truth.assertThat
@@ -556,8 +556,8 @@
@Test
fun multipleTransitions() = runTest {
- val frozenTransitions = mutableSetOf<TestTransition>()
- fun onFreezeAndAnimate(transition: TestTransition): () -> Unit {
+ val frozenTransitions = mutableSetOf<TestSceneTransition>()
+ fun onFreezeAndAnimate(transition: TestSceneTransition): () -> Unit {
// Instead of letting the transition finish when it is frozen, we put the transition in
// the frozenTransitions set so that we can verify that freezeAndAnimateToCurrentState()
// is called when expected and then we call finish() ourselves to finish the
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
similarity index 77%
copy from packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestTransition.kt
copy to packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
index a6a83ee..646cff8 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
@@ -18,18 +18,26 @@
import androidx.compose.foundation.gestures.Orientation
import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.Transition
import kotlinx.coroutines.CompletableDeferred
-/** A transition for tests that will be finished once [finish] is called. */
-abstract class TestTransition(
+/** A [Transition.ShowOrHideOverlay] for tests that will be finished once [finish] is called. */
+abstract class TestOverlayTransition(
fromScene: SceneKey,
- toScene: SceneKey,
+ overlay: OverlayKey,
replacedTransition: Transition?,
-) : Transition.ChangeScene(fromScene, toScene, replacedTransition) {
+) :
+ Transition.ShowOrHideOverlay(
+ overlay = overlay,
+ fromOrToScene = fromScene,
+ fromContent = fromScene,
+ toContent = overlay,
+ replacedTransition = replacedTransition,
+ ) {
private val finishCompletable = CompletableDeferred<Unit>()
override suspend fun run() {
@@ -42,11 +50,11 @@
}
}
-/** A utility to easily create a [TestTransition] in tests. */
+/** A utility to easily create a [TestOverlayTransition] in tests. */
fun transition(
- from: SceneKey,
- to: SceneKey,
- current: () -> SceneKey = { to },
+ fromScene: SceneKey,
+ overlay: OverlayKey,
+ isEffectivelyShown: () -> Boolean = { true },
progress: () -> Float = { 0f },
progressVelocity: () -> Float = { 0f },
previewProgress: () -> Float = { 0f },
@@ -58,13 +66,14 @@
isUpOrLeft: Boolean = false,
bouncingContent: ContentKey? = null,
orientation: Orientation = Orientation.Horizontal,
- onFreezeAndAnimate: ((TestTransition) -> Unit)? = null,
+ onFreezeAndAnimate: ((TestOverlayTransition) -> Unit)? = null,
replacedTransition: Transition? = null,
-): TestTransition {
+): TestOverlayTransition {
return object :
- TestTransition(from, to, replacedTransition), TransitionState.HasOverscrollProperties {
- override val currentScene: SceneKey
- get() = current()
+ TestOverlayTransition(fromScene, overlay, replacedTransition),
+ TransitionState.HasOverscrollProperties {
+ override val isEffectivelyShown: Boolean
+ get() = isEffectivelyShown()
override val progress: Float
get() = progress()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
similarity index 89%
rename from packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestTransition.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
index a6a83ee..d24b895 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
@@ -24,8 +24,8 @@
import com.android.compose.animation.scene.content.state.TransitionState.Transition
import kotlinx.coroutines.CompletableDeferred
-/** A transition for tests that will be finished once [finish] is called. */
-abstract class TestTransition(
+/** A [Transition.ChangeScene] for tests that will be finished once [finish] is called. */
+abstract class TestSceneTransition(
fromScene: SceneKey,
toScene: SceneKey,
replacedTransition: Transition?,
@@ -42,7 +42,7 @@
}
}
-/** A utility to easily create a [TestTransition] in tests. */
+/** A utility to easily create a [TestSceneTransition] in tests. */
fun transition(
from: SceneKey,
to: SceneKey,
@@ -58,11 +58,11 @@
isUpOrLeft: Boolean = false,
bouncingContent: ContentKey? = null,
orientation: Orientation = Orientation.Horizontal,
- onFreezeAndAnimate: ((TestTransition) -> Unit)? = null,
+ onFreezeAndAnimate: ((TestSceneTransition) -> Unit)? = null,
replacedTransition: Transition? = null,
-): TestTransition {
+): TestSceneTransition {
return object :
- TestTransition(from, to, replacedTransition), TransitionState.HasOverscrollProperties {
+ TestSceneTransition(from, to, replacedTransition), TransitionState.HasOverscrollProperties {
override val currentScene: SceneKey
get() = current()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
index 22e5896..c1dcf37 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
@@ -34,7 +34,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS
-import com.android.systemui.flags.Flags.MEDIA_SESSION_ACTIONS
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.graphics.imageLoader
import com.android.systemui.kosmos.testDispatcher
@@ -96,7 +95,6 @@
@Before
fun setUp() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
mediaControllerFactory.setControllerForToken(session.sessionToken, mediaController)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 75ecb2c..beba162 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -133,7 +133,8 @@
mVisibilityLocationProvider,
mVisualStabilityProvider,
mWakefulnessLifecycle,
- mKosmos.getCommunalInteractor(),
+ mKosmos.getCommunalSceneInteractor(),
+ mKosmos.getShadeInteractor(),
mKosmos.getKeyguardTransitionInteractor(),
mLogger);
mCoordinator.attach(mNotifPipeline);
@@ -561,11 +562,12 @@
@Test
public void testCommunalShowingWillNotSuppressReordering() {
- // GIVEN panel is expanded and communal is showing
+ // GIVEN panel is expanded, communal is showing, and QS is collapsed
setPulsing(false);
setFullyDozed(false);
setSleepy(false);
setPanelExpanded(true);
+ setQsExpanded(false);
setCommunalShowing(true);
// Reordering should be allowed
@@ -573,6 +575,20 @@
}
@Test
+ public void testQsExpandedOverCommunalWillSuppressReordering() {
+ // GIVEN panel is expanded and communal is showing, but QS is expanded
+ setPulsing(false);
+ setFullyDozed(false);
+ setSleepy(false);
+ setPanelExpanded(true);
+ setQsExpanded(true);
+ setCommunalShowing(true);
+
+ // Reordering should not be allowed
+ assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
+ }
+
+ @Test
public void testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() {
// GIVEN visual stability is being maintained b/c panel is expanded
setPulsing(false);
@@ -631,7 +647,12 @@
new ObservableTransitionState.Idle(
isShowing ? CommunalScenes.Communal : CommunalScenes.Blank)
);
- mKosmos.getCommunalRepository().setTransitionState(showingFlow);
+ mKosmos.getCommunalSceneInteractor().setTransitionState(showingFlow);
+ mTestScope.getTestScheduler().runCurrent();
+ }
+
+ private void setQsExpanded(boolean isExpanded) {
+ mKosmos.getShadeRepository().setQsExpansion(isExpanded ? 1.0f : 0.0f);
mTestScope.getTestScheduler().runCurrent();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index 7733841..5e36539 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.Flags.msdlFeedback;
import android.annotation.SuppressLint;
import android.app.ActivityOptions;
@@ -46,6 +47,9 @@
import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.util.ViewController;
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -67,6 +71,7 @@
private final Executor mMainExecutor;
private final Executor mBackgroundExecutor;
private final SelectedUserInteractor mSelectedUserInteractor;
+ private final MSDLPlayer mMSDLPlayer;
private final KeyguardUpdateMonitorCallback mInfoCallback =
new KeyguardUpdateMonitorCallback() {
@@ -99,7 +104,8 @@
MetricsLogger metricsLogger,
LockPatternUtils lockPatternUtils,
Executor mainExecutor, Executor backgroundExecutor,
- SelectedUserInteractor selectedUserInteractor) {
+ SelectedUserInteractor selectedUserInteractor,
+ MSDLPlayer msdlPlayer) {
super(view);
mConfigurationController = configurationController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -112,6 +118,7 @@
mMainExecutor = mainExecutor;
mBackgroundExecutor = backgroundExecutor;
mSelectedUserInteractor = selectedUserInteractor;
+ mMSDLPlayer = msdlPlayer;
}
@Override
@@ -165,6 +172,9 @@
@SuppressLint("MissingPermission")
public void takeEmergencyCallAction() {
mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_CALL);
+ if (msdlFeedback()) {
+ mMSDLPlayer.playToken(MSDLToken.KEYPRESS_RETURN, null);
+ }
if (mPowerManager != null) {
mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
}
@@ -221,6 +231,7 @@
private final Executor mMainExecutor;
private final Executor mBackgroundExecutor;
private final SelectedUserInteractor mSelectedUserInteractor;
+ private final MSDLPlayer mMSDLPlayer;
@Inject
public Factory(ConfigurationController configurationController,
@@ -233,7 +244,8 @@
LockPatternUtils lockPatternUtils,
@Main Executor mainExecutor,
@Background Executor backgroundExecutor,
- SelectedUserInteractor selectedUserInteractor) {
+ SelectedUserInteractor selectedUserInteractor,
+ MSDLPlayer msdlPlayer) {
mConfigurationController = configurationController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -246,6 +258,7 @@
mMainExecutor = mainExecutor;
mBackgroundExecutor = backgroundExecutor;
mSelectedUserInteractor = selectedUserInteractor;
+ mMSDLPlayer = msdlPlayer;
}
/** Construct an {@link com.android.keyguard.EmergencyButtonController}. */
@@ -253,7 +266,7 @@
return new EmergencyButtonController(view, mConfigurationController,
mKeyguardUpdateMonitor, mPowerManager, mActivityTaskManager, mShadeController,
mTelecomManager, mMetricsLogger, mLockPatternUtils, mMainExecutor,
- mBackgroundExecutor, mSelectedUserInteractor);
+ mBackgroundExecutor, mSelectedUserInteractor, mMSDLPlayer);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
index cc8dce79..49dadce 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt
@@ -2,13 +2,11 @@
import android.view.ViewGroup
import com.android.keyguard.KeyguardMessageAreaController
-import com.android.keyguard.ViewMediatorCallback
import com.android.keyguard.dagger.KeyguardBouncerComponent
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.bouncer.ui.BouncerDialogFactory
+import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.dagger.SysUISingleton
@@ -39,12 +37,9 @@
data class ComposeBouncerDependencies
@Inject
constructor(
- val legacyInteractor: PrimaryBouncerInteractor,
val viewModelFactory: BouncerSceneContentViewModel.Factory,
val dialogFactory: BouncerDialogFactory,
- val authenticationInteractor: AuthenticationInteractor,
- val viewMediatorCallback: ViewMediatorCallback?,
- val selectedUserInteractor: SelectedUserInteractor,
+ val bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory,
)
/**
@@ -63,12 +58,9 @@
val deps = composeBouncerDependencies.get()
ComposeBouncerViewBinder.bind(
view,
- deps.legacyInteractor,
deps.viewModelFactory,
deps.dialogFactory,
- deps.authenticationInteractor,
- deps.selectedUserInteractor,
- deps.viewMediatorCallback,
+ deps.bouncerContainerViewModelFactory,
)
} else {
val deps = legacyBouncerDependencies.get()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
index c4bbd9c..b5e54d5 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt
@@ -5,89 +5,55 @@
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.compose.ui.platform.ComposeView
-import androidx.core.view.isVisible
+import androidx.core.view.isGone
import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.keyguard.ViewMediatorCallback
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.composable.BouncerContainer
+import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
+import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
+import com.android.systemui.lifecycle.setSnapshotBinding
+import com.android.systemui.lifecycle.viewModel
+import kotlinx.coroutines.awaitCancellation
/** View binder responsible for binding the compose version of the bouncer. */
object ComposeBouncerViewBinder {
fun bind(
view: ViewGroup,
- legacyInteractor: PrimaryBouncerInteractor,
viewModelFactory: BouncerSceneContentViewModel.Factory,
dialogFactory: BouncerDialogFactory,
- authenticationInteractor: AuthenticationInteractor,
- selectedUserInteractor: SelectedUserInteractor,
- viewMediatorCallback: ViewMediatorCallback?,
+ bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory,
) {
- view.addView(
- ComposeView(view.context).apply {
- repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- setViewTreeOnBackPressedDispatcherOwner(
- object : OnBackPressedDispatcherOwner {
- override val onBackPressedDispatcher =
- OnBackPressedDispatcher().apply {
- setOnBackInvokedDispatcher(
- view.viewRootImpl.onBackInvokedDispatcher
- )
- }
-
- override val lifecycle: Lifecycle =
- this@repeatWhenAttached.lifecycle
- }
- )
- setContent { BouncerContainer(viewModelFactory, dialogFactory) }
- }
- }
- }
- )
-
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch {
- legacyInteractor.isShowing.collectLatest { bouncerShowing ->
- view.isVisible = bouncerShowing
- }
- }
-
- launch {
- authenticationInteractor.onAuthenticationResult.collectLatest {
- authenticationSucceeded ->
- if (authenticationSucceeded) {
- // Some dismiss actions require that keyguard be dismissed right away or
- // deferred until something else later on dismisses keyguard (eg. end of
- // a hide animation).
- val deferKeyguardDone =
- legacyInteractor.bouncerDismissAction?.onDismissAction?.onDismiss()
- legacyInteractor.setDismissAction(null, null)
-
- viewMediatorCallback?.let {
- val selectedUserId = selectedUserInteractor.getSelectedUserId()
- if (deferKeyguardDone == true) {
- it.keyguardDonePending(selectedUserId)
- } else {
- it.keyguardDone(selectedUserId)
+ view.viewModel(
+ minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+ factory = { bouncerContainerViewModelFactory.create() },
+ traceName = "ComposeBouncerViewBinder",
+ ) { viewModel ->
+ try {
+ view.setViewTreeOnBackPressedDispatcherOwner(
+ object : OnBackPressedDispatcherOwner {
+ override val onBackPressedDispatcher =
+ OnBackPressedDispatcher().apply {
+ setOnBackInvokedDispatcher(
+ view.viewRootImpl.onBackInvokedDispatcher
+ )
}
- }
+
+ override val lifecycle: Lifecycle = this@repeatWhenAttached.lifecycle
}
- }
- }
- launch {
- legacyInteractor.startingDisappearAnimation.collectLatest {
- it.run()
- legacyInteractor.hide()
- }
+ )
+
+ view.addView(
+ ComposeView(view.context).apply {
+ setContent { BouncerContainer(viewModelFactory, dialogFactory) }
+ }
+ )
+ view.setSnapshotBinding { view.isGone = !viewModel.isVisible }
+ awaitCancellation()
+ } finally {
+ view.removeAllViews()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
new file mode 100644
index 0000000..d223657
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.systemui.bouncer.ui.viewmodel
+
+import androidx.compose.runtime.getValue
+import com.android.keyguard.ViewMediatorCallback
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+class BouncerContainerViewModel
+@AssistedInject
+constructor(
+ private val legacyInteractor: PrimaryBouncerInteractor,
+ private val authenticationInteractor: AuthenticationInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val viewMediatorCallback: ViewMediatorCallback?,
+) : ExclusiveActivatable() {
+
+ private val hydrator = Hydrator("BouncerContainerViewModel")
+
+ val isVisible: Boolean by
+ hydrator.hydratedStateOf(traceName = "isVisible", source = legacyInteractor.isShowing)
+
+ override suspend fun onActivated(): Nothing {
+ coroutineScope {
+ launch {
+ authenticationInteractor.onAuthenticationResult.collect { authenticationSucceeded ->
+ if (authenticationSucceeded) {
+ // Some dismiss actions require that keyguard be dismissed right away or
+ // deferred until something else later on dismisses keyguard (eg. end of
+ // a hide animation).
+ val deferKeyguardDone =
+ legacyInteractor.bouncerDismissAction?.onDismissAction?.onDismiss()
+ legacyInteractor.setDismissAction(null, null)
+
+ viewMediatorCallback?.let {
+ val selectedUserId = selectedUserInteractor.getSelectedUserId()
+ if (deferKeyguardDone == true) {
+ it.keyguardDonePending(selectedUserId)
+ } else {
+ it.keyguardDone(selectedUserId)
+ }
+ }
+ }
+ }
+ }
+
+ launch {
+ legacyInteractor.startingDisappearAnimation.collect {
+ it.run()
+ legacyInteractor.hide()
+ }
+ }
+
+ hydrator.activate()
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): BouncerContainerViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2a2884c..c9fafce 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -230,9 +230,6 @@
// TODO(b/254512697): Tracking Bug
val MEDIA_TAP_TO_TRANSFER = releasedFlag("media_tap_to_transfer")
- // TODO(b/254512502): Tracking Bug
- val MEDIA_SESSION_ACTIONS = unreleasedFlag("media_session_actions")
-
// TODO(b/254512654): Tracking Bug
@JvmField val DREAM_MEDIA_COMPLICATION = unreleasedFlag("dream_media_complication")
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index a65243d..d4af1b5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -29,9 +29,8 @@
* Check whether media control actions should be based on PlaybackState instead of notification
*/
fun areMediaSessionActionsEnabled(packageName: String, user: UserHandle): Boolean {
- val enabled = StatusBarManager.useMediaSessionActionsForApp(packageName, user)
// Allow global override with flag
- return enabled || featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS)
+ return StatusBarManager.useMediaSessionActionsForApp(packageName, user)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 3a2f95e..6d0148a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -22,7 +22,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.systemui.Dumpable;
-import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
@@ -32,6 +32,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -43,6 +44,7 @@
import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.kotlin.BooleanFlowOperators;
import com.android.systemui.util.kotlin.JavaAdapter;
import java.io.PrintWriter;
@@ -70,7 +72,8 @@
private final VisibilityLocationProvider mVisibilityLocationProvider;
private final VisualStabilityProvider mVisualStabilityProvider;
private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final CommunalInteractor mCommunalInteractor;
+ private final CommunalSceneInteractor mCommunalSceneInteractor;
+ private final ShadeInteractor mShadeInteractor;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final VisualStabilityCoordinatorLogger mLogger;
@@ -110,7 +113,8 @@
VisibilityLocationProvider visibilityLocationProvider,
VisualStabilityProvider visualStabilityProvider,
WakefulnessLifecycle wakefulnessLifecycle,
- CommunalInteractor communalInteractor,
+ CommunalSceneInteractor communalSceneInteractor,
+ ShadeInteractor shadeInteractor,
KeyguardTransitionInteractor keyguardTransitionInteractor,
VisualStabilityCoordinatorLogger logger) {
mHeadsUpManager = headsUpManager;
@@ -122,7 +126,8 @@
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mDelayableExecutor = delayableExecutor;
- mCommunalInteractor = communalInteractor;
+ mCommunalSceneInteractor = communalSceneInteractor;
+ mShadeInteractor = shadeInteractor;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mLogger = logger;
@@ -141,7 +146,11 @@
this::onShadeOrQsClosingChanged);
mJavaAdapter.alwaysCollectFlow(mShadeAnimationInteractor.isLaunchingActivity(),
this::onLaunchingActivityChanged);
- mJavaAdapter.alwaysCollectFlow(mCommunalInteractor.isIdleOnCommunal(),
+ mJavaAdapter.alwaysCollectFlow(
+ BooleanFlowOperators.INSTANCE.allOf(
+ mCommunalSceneInteractor.isIdleOnCommunal(),
+ BooleanFlowOperators.INSTANCE.not(mShadeInteractor.isAnyFullyExpanded())
+ ),
this::onCommunalShowingChanged);
if (SceneContainerFlag.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index b9628e9..48e69893 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3710,7 +3710,7 @@
if (!isScrollingEnabled()) {
return false;
}
- if (isInsideQsHeader(ev) && !mIsBeingDragged) {
+ if (!isInScrollableRegion(ev) && !mIsBeingDragged) {
return false;
}
mForcedScroll = null;
@@ -3878,11 +3878,26 @@
return mFlingAfterUpEvent;
}
- protected boolean isInsideQsHeader(MotionEvent ev) {
- if (SceneContainerFlag.isEnabled()) {
- return ev.getY() < mAmbientState.getStackTop();
+ /** Is this touch event inside the scrollable region? */
+ @VisibleForTesting
+ boolean isInScrollableRegion(MotionEvent ev) {
+ if (!SceneContainerFlag.isEnabled()) {
+ return !isInsideQsHeader(ev);
+ }
+ ShadeScrimShape shape = mScrollViewFields.getScrimClippingShape();
+ if (shape == null) {
+ return true; // When there is no scrim, consider this event scrollable.
}
+ ShadeScrimBounds bounds = shape.getBounds();
+ return ev.getX() >= bounds.getLeft()
+ && ev.getX() <= bounds.getRight()
+ && ev.getY() >= bounds.getTop()
+ && ev.getY() <= bounds.getBottom();
+ }
+
+ protected boolean isInsideQsHeader(MotionEvent ev) {
+ SceneContainerFlag.assertInLegacyMode();
if (QSComposeFragment.isEnabled()) {
if (mQSHeaderBoundsProvider == null) {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index c3da7fc..178c318 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -435,13 +435,14 @@
/** Should only be called from {@link KeyguardStatusBarViewController}. */
void onOverlayChanged() {
- int theme = Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall);
- mCarrierLabel.setTextAppearance(theme);
+ final int carrierTheme = R.style.TextAppearance_StatusBar_Clock;
+ mCarrierLabel.setTextAppearance(carrierTheme);
mBatteryView.updatePercentView();
+ final int userSwitcherTheme = R.style.TextAppearance_StatusBar_UserChip;
TextView userSwitcherName = mUserSwitcherContainer.findViewById(R.id.current_user_name);
if (userSwitcherName != null) {
- userSwitcherName.setTextAppearance(theme);
+ userSwitcherName.setTextAppearance(userSwitcherTheme);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
index 347605d..43a78035 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
@@ -19,6 +19,7 @@
import android.app.ActivityTaskManager
import android.content.pm.PackageManager
import android.os.PowerManager
+import android.platform.test.annotations.EnableFlags
import android.telecom.TelecomManager
import android.telephony.TelephonyManager
import android.testing.TestableLooper
@@ -26,14 +27,20 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.haptics.msdl.FakeMSDLPlayer
+import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.shade.ShadeController
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -64,6 +71,8 @@
val fakeSystemClock = FakeSystemClock()
val mainExecutor = FakeExecutor(fakeSystemClock)
val backgroundExecutor = FakeExecutor(fakeSystemClock)
+ private val kosmos = testKosmos()
+ private val msdlPlayer: FakeMSDLPlayer = kosmos.msdlPlayer
lateinit var underTest: EmergencyButtonController
@@ -84,6 +93,7 @@
mainExecutor,
backgroundExecutor,
mSelectedUserInteractor,
+ msdlPlayer,
)
context.setMockPackageManager(packageManager)
Mockito.`when`(emergencyButton.context).thenReturn(context)
@@ -113,4 +123,13 @@
/* isSecure= */ eq(true)
)
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun takeEmergencyCallAction_withMSDLFeedback_playsEmergencyButtonTokenAndNullAttributes() {
+ underTest.takeEmergencyCallAction()
+
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.KEYPRESS_RETURN)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
index fd53b5ba..ad7a5b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
@@ -56,7 +56,6 @@
import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS
import com.android.systemui.flags.Flags.MEDIA_RETAIN_RECOMMENDATIONS
import com.android.systemui.flags.Flags.MEDIA_RETAIN_SESSIONS
-import com.android.systemui.flags.Flags.MEDIA_SESSION_ACTIONS
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -317,7 +316,6 @@
whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList)
whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(SMARTSPACE_CREATION_TIME)
whenever(mediaSmartspaceTarget.expiryTimeMillis).thenReturn(SMARTSPACE_EXPIRY_TIME)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, false)
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, false)
fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, false)
fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, false)
@@ -1671,7 +1669,6 @@
@Test
fun testPlaybackActions_noState_usesNotification() {
val desc = "Notification Action"
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
whenever(controller.playbackState).thenReturn(null)
val notifWithAction =
@@ -1705,7 +1702,6 @@
@Test
fun testPlaybackActions_hasPrevNext() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions =
PlaybackState.ACTION_PLAY or
PlaybackState.ACTION_SKIP_TO_PREVIOUS or
@@ -1749,7 +1745,6 @@
@Test
fun testPlaybackActions_noPrevNext_usesCustom() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
customDesc.forEach {
@@ -1781,7 +1776,6 @@
@Test
fun testPlaybackActions_connecting() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder =
PlaybackState.Builder()
@@ -1802,7 +1796,6 @@
@Test
fun testPlaybackActions_reservedSpace() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
customDesc.forEach {
@@ -1840,7 +1833,6 @@
@Test
fun testPlaybackActions_playPause_hasButton() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY_PAUSE
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
whenever(controller.playbackState).thenReturn(stateBuilder.build())
@@ -1939,7 +1931,6 @@
@Test
fun testPlaybackState_PauseWhenFlagTrue_keyExists_callsListener() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val state = PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 1f).build()
whenever(controller.playbackState).thenReturn(state)
@@ -2161,7 +2152,6 @@
@Test
fun testRetain_sessionPlayer_notifRemoved_doesNotChange() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2181,7 +2171,6 @@
@Test
fun testRetain_sessionPlayer_sessionDestroyed_setToResume() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2215,7 +2204,6 @@
@Test
fun testRetain_sessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions is added, and then the session is destroyed
@@ -2235,7 +2223,6 @@
@Test
fun testRetain_sessionPlayer_canResume_destroyedWhileActive_setToResume() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions and that does allow resumption is added,
@@ -2268,7 +2255,6 @@
@Test
fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2295,7 +2281,6 @@
@Test
fun testSessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions is added, and then the session is destroyed
@@ -2314,7 +2299,6 @@
@Test
fun testSessionPlayer_canResume_destroyedWhileActive_setToResume() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions and that does allow resumption is added,
@@ -2348,7 +2332,6 @@
@Test
fun testSessionDestroyed_noNotificationKey_stillRemoved() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
// When a notiifcation is added and then removed before it is fully processed
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index f4c2b47..c0f503d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -61,7 +61,6 @@
import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS
import com.android.systemui.flags.Flags.MEDIA_RETAIN_RECOMMENDATIONS
import com.android.systemui.flags.Flags.MEDIA_RETAIN_SESSIONS
-import com.android.systemui.flags.Flags.MEDIA_SESSION_ACTIONS
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
@@ -338,7 +337,6 @@
whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList)
whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(SMARTSPACE_CREATION_TIME)
whenever(mediaSmartspaceTarget.expiryTimeMillis).thenReturn(SMARTSPACE_EXPIRY_TIME)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, false)
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, false)
fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, false)
fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, false)
@@ -1680,7 +1678,6 @@
@Test
fun testPlaybackActions_noState_usesNotification() {
val desc = "Notification Action"
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
whenever(controller.playbackState).thenReturn(null)
val notifWithAction =
@@ -1714,7 +1711,6 @@
@Test
fun testPlaybackActions_hasPrevNext() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions =
PlaybackState.ACTION_PLAY or
PlaybackState.ACTION_SKIP_TO_PREVIOUS or
@@ -1758,7 +1754,6 @@
@Test
fun testPlaybackActions_noPrevNext_usesCustom() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
customDesc.forEach {
@@ -1790,7 +1785,6 @@
@Test
fun testPlaybackActions_connecting() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder =
PlaybackState.Builder()
@@ -1811,7 +1805,6 @@
@Test
@EnableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
fun postWithPlaybackActions_drawablesReused() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
val stateActions =
@@ -1845,7 +1838,6 @@
@Test
@DisableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
fun postWithPlaybackActions_drawablesNotReused() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
val stateActions =
@@ -1879,7 +1871,6 @@
@Test
fun testPlaybackActions_reservedSpace() {
val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
customDesc.forEach {
@@ -1917,7 +1908,6 @@
@Test
fun testPlaybackActions_playPause_hasButton() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val stateActions = PlaybackState.ACTION_PLAY_PAUSE
val stateBuilder = PlaybackState.Builder().setActions(stateActions)
whenever(controller.playbackState).thenReturn(stateBuilder.build())
@@ -2016,7 +2006,6 @@
@Test
fun testPlaybackState_PauseWhenFlagTrue_keyExists_callsListener() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
val state = PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 1f).build()
whenever(controller.playbackState).thenReturn(state)
@@ -2237,7 +2226,6 @@
@Test
fun testRetain_sessionPlayer_notifRemoved_doesNotChange() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2257,7 +2245,6 @@
@Test
fun testRetain_sessionPlayer_sessionDestroyed_setToResume() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2291,7 +2278,6 @@
@Test
fun testRetain_sessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions is added, and then the session is destroyed
@@ -2311,7 +2297,6 @@
@Test
fun testRetain_sessionPlayer_canResume_destroyedWhileActive_setToResume() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions and that does allow resumption is added,
@@ -2344,7 +2329,6 @@
@Test
fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control with PlaybackState actions is added, times out,
@@ -2371,7 +2355,6 @@
@Test
fun testSessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions is added, and then the session is destroyed
@@ -2390,7 +2373,6 @@
@Test
fun testSessionPlayer_canResume_destroyedWhileActive_setToResume() {
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
addPlaybackStateAction()
// When a media control using session actions and that does allow resumption is added,
@@ -2424,7 +2406,6 @@
@Test
fun testSessionDestroyed_noNotificationKey_stillRemoved() {
fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
- fakeFeatureFlags.set(MEDIA_SESSION_ACTIONS, true)
// When a notiifcation is added and then removed before it is fully processed
mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
new file mode 100644
index 0000000..4ab3c7b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.systemui.recordissue
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.settings.userFileManager
+import com.android.systemui.settings.userTracker
+import com.google.common.truth.Truth
+import java.util.concurrent.CountDownLatch
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class IssueRecordingStateTest : SysuiTestCase() {
+
+ private val kosmos = Kosmos()
+ private lateinit var underTest: IssueRecordingState
+
+ @Before
+ fun setup() {
+ underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ }
+
+ @Test
+ fun takeBugreport_isSaved_betweenDifferentSessions() {
+ val expected = true
+
+ underTest.takeBugreport = expected
+ underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+ Truth.assertThat(underTest.takeBugreport).isEqualTo(expected)
+ }
+
+ @Test
+ fun recordScreen_isSaved_betweenDifferentSessions() {
+ val expected = true
+
+ underTest.recordScreen = expected
+ underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+ Truth.assertThat(underTest.recordScreen).isEqualTo(expected)
+ }
+
+ @Test
+ fun hasUserApprovedScreenRecording_isTrue_afterBeingMarkedAsCompleted() {
+ underTest.markUserApprovalForScreenRecording()
+ underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+ Truth.assertThat(underTest.hasUserApprovedScreenRecording).isEqualTo(true)
+ }
+
+ @Test
+ fun tagTitles_areSavedConsistently() {
+ val expected = setOf("a", "b", "c")
+
+ underTest.tagTitles = expected
+ underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+ Truth.assertThat(underTest.tagTitles).isEqualTo(expected)
+ }
+
+ @Test
+ fun isRecording_callsListeners_onTheValueChanging() {
+ val count = CountDownLatch(1)
+ val listener = Runnable { count.countDown() }
+
+ underTest.addListener(listener)
+ underTest.isRecording = true
+
+ Truth.assertThat(count.count).isEqualTo(0)
+ }
+
+ @Test
+ fun isRecording_callsOnlyListeners_whoHaveNotBeenRemoved() {
+ val count1 = CountDownLatch(1)
+ val count2 = CountDownLatch(1)
+ val listener1 = Runnable { count1.countDown() }
+ val listener2 = Runnable { count2.countDown() }
+
+ underTest.addListener(listener1)
+ underTest.removeListener(listener1)
+ underTest.addListener(listener2)
+ underTest.isRecording = true
+
+ Truth.assertThat(count1.count).isEqualTo(1)
+ Truth.assertThat(count2.count).isEqualTo(0)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index a18de68..a06f4d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -95,6 +95,8 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds;
+import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -893,7 +895,7 @@
@Test
@DisableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
- @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+ @DisableSceneContainer
public void testInsideQSHeader_noOffset() {
ViewGroup qsHeader = mock(ViewGroup.class);
Rect boundsOnScreen = new Rect(0, 0, 1000, 1000);
@@ -911,7 +913,7 @@
@Test
@DisableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
- @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+ @DisableSceneContainer
public void testInsideQSHeader_Offset() {
ViewGroup qsHeader = mock(ViewGroup.class);
Rect boundsOnScreen = new Rect(100, 100, 1000, 1000);
@@ -932,7 +934,7 @@
@Test
@EnableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
- @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+ @DisableSceneContainer
public void testInsideQSHeader_noOffset_qsCompose() {
ViewGroup qsHeader = mock(ViewGroup.class);
Rect boundsOnScreen = new Rect(0, 0, 1000, 1000);
@@ -959,7 +961,7 @@
@Test
@EnableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
- @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+ @DisableSceneContainer
public void testInsideQSHeader_Offset_qsCompose() {
ViewGroup qsHeader = mock(ViewGroup.class);
Rect boundsOnScreen = new Rect(100, 100, 1000, 1000);
@@ -988,6 +990,53 @@
}
@Test
+ @EnableSceneContainer
+ public void testIsInsideScrollableRegion_noScrim() {
+ mStackScroller.setLeftTopRightBottom(0, 0, 2000, 2000);
+
+ MotionEvent event = transformEventForView(createMotionEvent(250f, 250f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event)).isTrue();
+ }
+
+ @Test
+ @EnableSceneContainer
+ public void testIsInsideScrollableRegion_noOffset() {
+ mStackScroller.setLeftTopRightBottom(0, 0, 1000, 2000);
+ mStackScroller.setScrimClippingShape(createScrimShape(100, 500, 900, 2000));
+
+ MotionEvent event1 = transformEventForView(createMotionEvent(500f, 400f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event1)).isFalse();
+
+ MotionEvent event2 = transformEventForView(createMotionEvent(50, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event2)).isFalse();
+
+ MotionEvent event3 = transformEventForView(createMotionEvent(950f, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event3)).isFalse();
+
+ MotionEvent event4 = transformEventForView(createMotionEvent(500f, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event4)).isTrue();
+ }
+
+ @Test
+ @EnableSceneContainer
+ public void testIsInsideScrollableRegion_offset() {
+ mStackScroller.setLeftTopRightBottom(1000, 0, 2000, 2000);
+ mStackScroller.setScrimClippingShape(createScrimShape(100, 500, 900, 2000));
+
+ MotionEvent event1 = transformEventForView(createMotionEvent(1500f, 400f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event1)).isFalse();
+
+ MotionEvent event2 = transformEventForView(createMotionEvent(1050, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event2)).isFalse();
+
+ MotionEvent event3 = transformEventForView(createMotionEvent(1950f, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event3)).isFalse();
+
+ MotionEvent event4 = transformEventForView(createMotionEvent(1500f, 1000f), mStackScroller);
+ assertThat(mStackScroller.isInScrollableRegion(event4)).isTrue();
+ }
+
+ @Test
@DisableSceneContainer // TODO(b/312473478): address disabled test
public void setFractionToShade_recomputesStackHeight() {
mStackScroller.setFractionToShade(1f);
@@ -1438,7 +1487,7 @@
private static MotionEvent transformEventForView(MotionEvent event, View view) {
// From `ViewGroup#dispatchTransformedTouchEvent`
MotionEvent transformed = event.copy();
- transformed.offsetLocation(-view.getTop(), -view.getLeft());
+ transformed.offsetLocation(/* deltaX = */-view.getLeft(), /* deltaY = */ -view.getTop());
return transformed;
}
@@ -1474,4 +1523,9 @@
}
private abstract static class BooleanConsumer implements Consumer<Boolean> { }
+
+ private ShadeScrimShape createScrimShape(int left, int top, int right, int bottom) {
+ ShadeScrimBounds bounds = new ShadeScrimBounds(left, top, right, bottom);
+ return new ShadeScrimShape(bounds, 0, 0);
+ }
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 0e266f5..7e3f613 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1126,26 +1126,31 @@
final int numLru = lruList.size();
if (mConstants.USE_TIERED_CACHED_ADJ) {
final long now = mInjector.getUptimeMillis();
+ int uiTargetAdj = 10;
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
- if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
- >= UNKNOWN_ADJ) {
+ if (!app.isKilledByAm() && app.getThread() != null
+ && (state.getCurAdj() >= UNKNOWN_ADJ
+ || (state.hasShownUi() && state.getCurAdj() >= CACHED_APP_MIN_ADJ))) {
final ProcessServiceRecord psr = app.mServices;
int targetAdj = CACHED_APP_MIN_ADJ;
if (opt != null && opt.isFreezeExempt()) {
// BIND_WAIVE_PRIORITY and the like get oom_adj 900
targetAdj += 0;
+ } else if (state.hasShownUi() && uiTargetAdj < 15) {
+ // The most recent 5 apps that have shown UI get 910-914
+ targetAdj += uiTargetAdj++;
} else if ((state.getSetAdj() >= CACHED_APP_MIN_ADJ)
&& (state.getLastStateTime()
+ mConstants.TIERED_CACHED_ADJ_DECAY_TIME) < now) {
// Older cached apps get 950
targetAdj += 50;
} else {
- // Newer cached apps get 910
- targetAdj += 10;
+ // Newer cached apps get 920
+ targetAdj += 20;
}
state.setCurRawAdj(targetAdj);
state.setCurAdj(psr.modifyRawOomAdj(targetAdj));
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 51aa528..3b284a2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -90,7 +90,6 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.ApplicationExitInfo;
-import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.content.ComponentName;
import android.content.Context;
@@ -164,6 +163,10 @@
private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ
+ ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+ private static int sFirstUiCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10;
+ private static int sFirstNonUiCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 20;
+ private static int sUiTierSize = 5;
+
private Context mContext;
private PackageManagerInternal mPackageManagerInternal;
private ActivityManagerService mService;
@@ -232,9 +235,6 @@
mInjector);
mService.mOomAdjuster.mAdjSeq = 10000;
mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
- if (mService.mConstants.USE_TIERED_CACHED_ADJ) {
- sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10;
- }
mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC);
}
@@ -473,7 +473,8 @@
mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
- final int expectedAdj = sFirstCachedAdj;
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstUiCachedAdj : sFirstCachedAdj;
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj,
SCHED_GROUP_BACKGROUND);
}
@@ -701,7 +702,9 @@
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertEquals(sFirstCachedAdj, app.mState.getSetAdj());
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstUiCachedAdj : sFirstCachedAdj;
+ assertEquals(expectedAdj, app.mState.getSetAdj());
// Follow up should not have been called again.
verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
followUpTimeCaptor.capture());
@@ -836,7 +839,9 @@
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, CACHED_APP_MIN_ADJ,
+ int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstUiCachedAdj : CACHED_APP_MIN_ADJ;
+ assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, expectedAdj,
SCHED_GROUP_BACKGROUND, "previous-expired");
// Follow up should not have been called again.
verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
@@ -877,9 +882,15 @@
for (int i = 0; i < numberOfApps; i++) {
final int mruIndex = numberOfApps - i - 1;
- int expectedAdj = CACHED_APP_MIN_ADJ + (mruIndex * 2 * CACHED_APP_IMPORTANCE_LEVELS);
- if (expectedAdj > CACHED_APP_MAX_ADJ) {
- expectedAdj = CACHED_APP_MAX_ADJ;
+ int expectedAdj;
+ if (mService.mConstants.USE_TIERED_CACHED_ADJ) {
+ expectedAdj = (i < numberOfApps - sUiTierSize)
+ ? sFirstNonUiCachedAdj : sFirstUiCachedAdj + mruIndex;
+ } else {
+ expectedAdj = CACHED_APP_MIN_ADJ + (mruIndex * 2 * CACHED_APP_IMPORTANCE_LEVELS);
+ if (expectedAdj > CACHED_APP_MAX_ADJ) {
+ expectedAdj = CACHED_APP_MAX_ADJ;
+ }
}
assertProcStates(apps[i], PROCESS_STATE_LAST_ACTIVITY, expectedAdj,
SCHED_GROUP_BACKGROUND, "previous-expired");
@@ -1003,7 +1014,9 @@
updateOomAdj(client, app);
doReturn(null).when(mService).getTopApp();
- assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_SERVICE, expectedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -1053,7 +1066,9 @@
mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
- assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -1469,7 +1484,9 @@
bindProvider(app, app, null, null, false);
updateOomAdj(app);
- assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -1484,7 +1501,9 @@
mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client);
- assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -1592,7 +1611,9 @@
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND,
"cch-empty");
// Follow up should not have been called again.
verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
@@ -2623,12 +2644,11 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
final int userOwner = 0;
final int userOther = 1;
- final int cachedAdj1 = mService.mConstants.USE_TIERED_CACHED_ADJ
- ? CACHED_APP_MIN_ADJ + 10
- : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
- final int cachedAdj2 = mService.mConstants.USE_TIERED_CACHED_ADJ
- ? CACHED_APP_MIN_ADJ + 10
- : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+
+ // cachedAdj1 and cachedAdj2 will be read if USE_TIERED_CACHED_ADJ is disabled. Otherwise,
+ // sFirstUiCachedAdj and sFirstNonUiCachedAdj are used instead.
+ final int cachedAdj1 = CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+ final int cachedAdj2 = cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
doReturn(userOwner).when(mService.mUserController).getCurrentUserId();
final ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP();
@@ -2669,8 +2689,12 @@
mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj();
- assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services");
- assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj2, "cch-started-services");
+ assertProcStates(app, true, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstUiCachedAdj : cachedAdj1,
+ "cch-started-ui-services");
+ assertProcStates(app2, true, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj2,
+ "cch-started-services");
app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
app.mState.setAdjType(null);
@@ -2686,7 +2710,10 @@
s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
updateOomAdj();
- assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+ // hasShownUi was set to false for 'app', so 920 is expected for USE_TIERED_CACHED_ADJ.
+ assertProcStates(app, true, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+ "cch-started-services");
app.mServices.stopService(s);
app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
@@ -2705,7 +2732,9 @@
updateOomAdj();
assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
- assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+ assertProcStates(app2, true, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+ "cch-started-services");
app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
app.mState.setAdjType(null);
@@ -2715,13 +2744,17 @@
updateOomAdj();
assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
- assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+ assertProcStates(app2, true, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+ "cch-started-services");
doReturn(userOther).when(mService.mUserController).getCurrentUserId();
mService.mOomAdjuster.handleUserSwitchedLocked();
updateOomAdj();
- assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+ assertProcStates(app, true, PROCESS_STATE_SERVICE,
+ mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+ "cch-started-services");
assertProcStates(app2, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
}
@@ -2998,7 +3031,9 @@
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app, PROCESS_STATE_SERVICE, expectedAdj, SCHED_GROUP_BACKGROUND,
"cch-started-services");
// Follow up should not have been called again.
verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
@@ -3031,14 +3066,16 @@
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+ ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+ assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND,
"cch-empty");
verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime(
eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
- assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND,
"cch-empty");
}
diff --git a/services/tests/powerstatstests/TEST_MAPPING b/services/tests/powerstatstests/TEST_MAPPING
index fb24361..1e8d2de 100644
--- a/services/tests/powerstatstests/TEST_MAPPING
+++ b/services/tests/powerstatstests/TEST_MAPPING
@@ -14,8 +14,7 @@
"name": "PowerStatsTestsRavenwood",
"host": true,
"options": [
- {"include-filter": "com.android.server.power.stats"},
- {"exclude-annotation": "android.platform.test.annotations.DisabledOnRavenwood"}
+ {"include-filter": "com.android.server.power.stats"}
]
}
],
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 13bd5eb..aca0941 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -10098,8 +10098,8 @@
* The default value is 30 seconds.
*/
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT =
- "satellite_screen_off_inactivity_timeout_sec_int";
+ public static final String KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT =
+ "satellite_roaming_screen_off_inactivity_timeout_sec_int";
/**
* An integer key holds the timeout duration in seconds used to determine whether to exit P2P
@@ -10112,8 +10112,8 @@
* The default value is 180 seconds.
*/
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static final String KEY_SATELLITE_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT =
- "satellite_p2p_sms_inactivity_timeout_sec_int";
+ public static final String KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT =
+ "satellite_roaming_p2p_sms_inactivity_timeout_sec_int";
/**
* An integer key holds the timeout duration in seconds used to determine whether to exit ESOS
@@ -10126,8 +10126,8 @@
* The default value is 600 seconds.
*/
@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static final String KEY_SATELLITE_ESOS_INACTIVITY_TIMEOUT_SEC_INT =
- "satellite_esos_inactivity_timeout_sec_int";
+ public static final String KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT =
+ "satellite_roaming_esos_inactivity_timeout_sec_int";
/**
* Indicating whether DUN APN should be disabled when the device is roaming. In that case,
@@ -11294,9 +11294,9 @@
sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT,
SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911);
sDefaults.putInt(KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, 180);
- sDefaults.putInt(KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, 30);
- sDefaults.putInt(KEY_SATELLITE_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, 180);
- sDefaults.putInt(KEY_SATELLITE_ESOS_INACTIVITY_TIMEOUT_SEC_INT, 600);
+ sDefaults.putInt(KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, 30);
+ sDefaults.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, 180);
+ sDefaults.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT, 600);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL, false);