Merge "Fix isSystemUi ManagedServiceInfo bool" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 2047168..eed248b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -310,6 +310,8 @@
aconfig_declarations {
name: "android.os.flags-aconfig",
package: "android.os",
+ exportable: true,
+ container: "system",
srcs: ["core/java/android/os/*.aconfig"],
}
@@ -326,6 +328,13 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.os.flags-aconfig-java-export",
+ aconfig_declarations: "android.os.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ mode: "exported",
+}
+
// VirtualDeviceManager
cc_aconfig_library {
name: "android.companion.virtualdevice.flags-aconfig-cc",
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a28dc49..17ed908 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1366,7 +1366,7 @@
}
@FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public class IntentFactory {
- method @NonNull public static android.content.Intent createCancelUiIntent(@NonNull android.os.IBinder, boolean, @NonNull String);
+ method @NonNull public static android.content.Intent createCancelUiIntent(@NonNull android.content.Context, @NonNull android.os.IBinder, boolean, @NonNull String);
method @NonNull public static android.content.Intent createCredentialSelectorIntent(@NonNull android.content.Context, @NonNull android.credentials.selection.RequestInfo, @NonNull java.util.ArrayList<android.credentials.selection.ProviderData>, @NonNull java.util.ArrayList<android.credentials.selection.DisabledProviderData>, @NonNull android.os.ResultReceiver);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 41151c0..1d39186 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -40,6 +40,7 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL;
import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
+import static com.android.window.flags.Flags.activityWindowInfoFlag;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -63,6 +64,7 @@
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ActivityResultItem;
import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.app.servertransaction.DestroyActivityItem;
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.PendingTransactionActions;
@@ -606,6 +608,8 @@
Configuration overrideConfig;
@NonNull
private ActivityWindowInfo mActivityWindowInfo;
+ @Nullable
+ private ActivityWindowInfo mLastReportedActivityWindowInfo;
// Used for consolidating configs before sending on to Activity.
private final Configuration tmpConfig = new Configuration();
@@ -4180,6 +4184,9 @@
pendingActions.setRestoreInstanceState(true);
pendingActions.setCallOnPostCreate(true);
}
+
+ // Trigger ActivityWindowInfo callback if first launch or change from relaunch.
+ handleActivityWindowInfoChanged(r);
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
ActivityClient.getInstance().finishActivity(r.token, Activity.RESULT_CANCELED,
@@ -4558,7 +4565,7 @@
private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) {
final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token,
- r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags,
+ r.activity.isFinishing(), /* userLeaving */ true,
/* dontReport */ false, /* autoEnteringPip */ false);
transaction.addTransactionItem(pauseActivityItem);
executeTransaction(transaction);
@@ -5432,13 +5439,12 @@
@Override
public void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving,
- int configChanges, boolean autoEnteringPip, PendingTransactionActions pendingActions,
+ boolean autoEnteringPip, PendingTransactionActions pendingActions,
String reason) {
if (userLeaving) {
performUserLeavingActivity(r);
}
- r.activity.mConfigChangeFlags |= configChanges;
if (autoEnteringPip) {
// Set mIsInPictureInPictureMode earlier in case of auto-enter-pip, see also
// {@link Activity#enterPictureInPictureMode(PictureInPictureParams)}.
@@ -5687,9 +5693,8 @@
}
@Override
- public void handleStopActivity(ActivityClientRecord r, int configChanges,
+ public void handleStopActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
- r.activity.mConfigChangeFlags |= configChanges;
final StopInfo stopInfo = new StopInfo();
performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest,
@@ -5859,11 +5864,10 @@
/** Core implementation of activity destroy call. */
void performDestroyActivity(ActivityClientRecord r, boolean finishing,
- int configChanges, boolean getNonConfigInstance, String reason) {
+ boolean getNonConfigInstance, String reason) {
Class<? extends Activity> activityClass;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
activityClass = r.activity.getClass();
- r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
}
@@ -5928,9 +5932,9 @@
}
@Override
- public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges,
+ public void handleDestroyActivity(ActivityClientRecord r, boolean finishing,
boolean getNonConfigInstance, String reason) {
- performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason);
+ performDestroyActivity(r, finishing, getNonConfigInstance, reason);
cleanUpPendingRemoveWindows(r, finishing);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
@@ -6130,7 +6134,7 @@
r.activity.mChangingConfigurations = true;
- handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
+ handleRelaunchActivityInner(r, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, tmp.mActivityWindowInfo,
"handleRelaunchActivity");
}
@@ -6199,7 +6203,7 @@
executeTransaction(transaction);
}
- private void handleRelaunchActivityInner(@NonNull ActivityClientRecord r, int configChanges,
+ private void handleRelaunchActivityInner(@NonNull ActivityClientRecord r,
@Nullable List<ResultInfo> pendingResults,
@Nullable List<ReferrerIntent> pendingIntents,
@NonNull PendingTransactionActions pendingActions, boolean startsNotResumed,
@@ -6215,7 +6219,7 @@
callActivityOnStop(r, true /* saveState */, reason);
}
- handleDestroyActivity(r, false, configChanges, true, reason);
+ handleDestroyActivity(r, false /* finishing */, true /* getNonConfigInstance */, reason);
r.activity = null;
r.window = null;
@@ -6740,7 +6744,7 @@
// Perform updates.
r.overrideConfig = overrideConfig;
r.mActivityWindowInfo = activityWindowInfo;
- // TODO(b/287582673): notify on ActivityWindowInfo change
+
final ViewRootImpl viewRoot = r.activity.mDecor != null
? r.activity.mDecor.getViewRootImpl() : null;
@@ -6763,6 +6767,22 @@
viewRoot.updateConfiguration(displayId);
}
mSomeActivitiesChanged = true;
+
+ // Trigger ActivityWindowInfo callback if changed.
+ handleActivityWindowInfoChanged(r);
+ }
+
+ private void handleActivityWindowInfoChanged(@NonNull ActivityClientRecord r) {
+ if (!activityWindowInfoFlag()) {
+ return;
+ }
+ if (r.mActivityWindowInfo == null
+ || r.mActivityWindowInfo.equals(r.mLastReportedActivityWindowInfo)) {
+ return;
+ }
+ r.mLastReportedActivityWindowInfo = r.mActivityWindowInfo;
+ ClientTransactionListenerController.getInstance().onActivityWindowInfoChanged(r.token,
+ r.mActivityWindowInfo);
}
final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index b5b3669..01153c9 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -114,11 +114,11 @@
/** Destroy the activity. */
public abstract void handleDestroyActivity(@NonNull ActivityClientRecord r, boolean finishing,
- int configChanges, boolean getNonConfigInstance, String reason);
+ boolean getNonConfigInstance, String reason);
/** Pause the activity. */
public abstract void handlePauseActivity(@NonNull ActivityClientRecord r, boolean finished,
- boolean userLeaving, int configChanges, boolean autoEnteringPip,
+ boolean userLeaving, boolean autoEnteringPip,
PendingTransactionActions pendingActions, String reason);
/**
@@ -146,14 +146,13 @@
/**
* Stop the activity.
* @param r Target activity record.
- * @param configChanges Activity configuration changes.
* @param pendingActions Pending actions to be used on this or later stages of activity
* transaction.
* @param finalStateRequest Flag indicating if this call is handling final lifecycle state
* request for a transaction.
* @param reason Reason for performing this operation.
*/
- public abstract void handleStopActivity(@NonNull ActivityClientRecord r, int configChanges,
+ public abstract void handleStopActivity(@NonNull ActivityClientRecord r,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
/** Report that activity was stopped to server. */
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 1b19ecd..095cfc5 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -413,7 +413,7 @@
if (localLOGV) Log.v(TAG, r.id + ": destroying");
final ActivityClientRecord clientRecord = mActivityThread.getActivityClient(r);
if (clientRecord != null) {
- mActivityThread.performDestroyActivity(clientRecord, finish, 0 /* configChanges */,
+ mActivityThread.performDestroyActivity(clientRecord, finish,
false /* getNonConfigInstance */, "LocalActivityManager::performDestroy");
}
r.activity = null;
@@ -684,7 +684,7 @@
if (localLOGV) Log.v(TAG, r.id + ": no corresponding record");
continue;
}
- mActivityThread.performDestroyActivity(clientRecord, finishing, 0 /* configChanges */,
+ mActivityThread.performDestroyActivity(clientRecord, finishing,
false /* getNonConfigInstance */, "LocalActivityManager::dispatchDestroy");
}
mActivities.clear();
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index b2be27f..4a06f7d 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -248,7 +248,8 @@
}
/**
- * Returns the default locale if specified, otherwise null
+ * Returns the locale the strings in values/strings.xml (the default strings in the directory
+ * with no locale qualifier) are in if specified, otherwise null
*
* @return The default Locale or null
*/
diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java
index d81eb20..51f3137 100644
--- a/core/java/android/app/admin/AccountTypePolicyKey.java
+++ b/core/java/android/app/admin/AccountTypePolicyKey.java
@@ -19,12 +19,12 @@
import static android.app.admin.PolicyUpdateReceiver.EXTRA_ACCOUNT_TYPE;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -54,7 +54,7 @@
@TestApi
public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) {
super(key);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
}
mAccountType = Objects.requireNonNull((accountType));
diff --git a/core/java/android/app/admin/BundlePolicyValue.java b/core/java/android/app/admin/BundlePolicyValue.java
index cc5e75f..4f70604 100644
--- a/core/java/android/app/admin/BundlePolicyValue.java
+++ b/core/java/android/app/admin/BundlePolicyValue.java
@@ -16,10 +16,9 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -32,7 +31,7 @@
public BundlePolicyValue(Bundle value) {
super(value);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxParcelableFieldsLength(value);
}
}
diff --git a/core/java/android/app/admin/ComponentNamePolicyValue.java b/core/java/android/app/admin/ComponentNamePolicyValue.java
index 4d36195..a957dbf 100644
--- a/core/java/android/app/admin/ComponentNamePolicyValue.java
+++ b/core/java/android/app/admin/ComponentNamePolicyValue.java
@@ -16,10 +16,9 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.os.Parcel;
@@ -32,7 +31,7 @@
public ComponentNamePolicyValue(@NonNull ComponentName value) {
super(value);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxComponentNameLength(value);
}
}
diff --git a/core/java/android/app/admin/IntentFilterPolicyKey.java b/core/java/android/app/admin/IntentFilterPolicyKey.java
index de7ff9f..63c3a4cb 100644
--- a/core/java/android/app/admin/IntentFilterPolicyKey.java
+++ b/core/java/android/app/admin/IntentFilterPolicyKey.java
@@ -19,12 +19,12 @@
import static android.app.admin.PolicyUpdateReceiver.EXTRA_INTENT_FILTER;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Parcel;
@@ -60,7 +60,7 @@
@TestApi
public IntentFilterPolicyKey(@NonNull String identifier, @NonNull IntentFilter filter) {
super(identifier);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxParcelableFieldsLength(filter);
}
mFilter = Objects.requireNonNull(filter);
diff --git a/core/java/android/app/admin/LockTaskPolicy.java b/core/java/android/app/admin/LockTaskPolicy.java
index 9d6ce24..a36ea05 100644
--- a/core/java/android/app/admin/LockTaskPolicy.java
+++ b/core/java/android/app/admin/LockTaskPolicy.java
@@ -16,11 +16,10 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.admin.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -136,7 +135,7 @@
}
private void setPackagesInternal(Set<String> packages) {
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
for (String p : packages) {
PolicySizeVerifier.enforceMaxPackageNameLength(p);
}
diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java
index 2241fdd..389585f 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -20,12 +20,12 @@
import static android.app.admin.PolicyUpdateReceiver.EXTRA_PERMISSION_NAME;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -59,7 +59,7 @@
public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName,
@NonNull String permissionName) {
super(identifier);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
}
diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java
index 2ea17a1..68dc797 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -19,12 +19,12 @@
import static android.app.admin.PolicyUpdateReceiver.EXTRA_PACKAGE_NAME;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_BUNDLE_KEY;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -55,7 +55,7 @@
@TestApi
public PackagePolicyKey(@NonNull String key, @NonNull String packageName) {
super(key);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
}
mPackageName = Objects.requireNonNull((packageName));
diff --git a/core/java/android/app/admin/StringPolicyValue.java b/core/java/android/app/admin/StringPolicyValue.java
index f4d4adc..8995c0f 100644
--- a/core/java/android/app/admin/StringPolicyValue.java
+++ b/core/java/android/app/admin/StringPolicyValue.java
@@ -16,10 +16,9 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.Objects;
@@ -31,7 +30,7 @@
public StringPolicyValue(@NonNull String value) {
super(value);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
}
}
diff --git a/core/java/android/app/admin/StringSetPolicyValue.java b/core/java/android/app/admin/StringSetPolicyValue.java
index 82fe761..f37dfee 100644
--- a/core/java/android/app/admin/StringSetPolicyValue.java
+++ b/core/java/android/app/admin/StringSetPolicyValue.java
@@ -16,10 +16,9 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.flags.Flags;
import android.os.Parcel;
import java.util.HashSet;
@@ -33,7 +32,7 @@
public StringSetPolicyValue(@NonNull Set<String> value) {
super(value);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
for (String str : value) {
PolicySizeVerifier.enforceMaxStringLength(str, "policyValue");
}
diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java
index d69a5f0..ee90ccd 100644
--- a/core/java/android/app/admin/UserRestrictionPolicyKey.java
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -17,11 +17,11 @@
package android.app.admin;
import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_KEY;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import android.os.Bundle;
import android.os.Parcel;
@@ -45,7 +45,7 @@
@TestApi
public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
super(identifier);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
}
mRestriction = Objects.requireNonNull(restriction);
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index e1a6913..1927019 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -10,7 +10,14 @@
flag {
name: "device_policy_size_tracking_enabled"
namespace: "enterprise"
- description: "Add feature to track the total policy size and have a max threshold."
+ description: "Add feature to track the total policy size and have a max threshold - public API changes"
+ bug: "281543351"
+}
+
+flag {
+ name: "device_policy_size_tracking_internal_enabled"
+ namespace: "enterprise"
+ description: "Add feature to track the total policy size and have a max threshold - internal changes"
bug: "281543351"
}
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index 1a8136e..7383d07 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -16,16 +16,24 @@
package android.app.servertransaction;
+import static com.android.window.flags.Flags.activityWindowInfoFlag;
import static com.android.window.flags.Flags.bundleClientTransactionFlag;
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
+import android.app.Activity;
import android.app.ActivityThread;
import android.hardware.display.DisplayManagerGlobal;
+import android.os.IBinder;
+import android.util.ArraySet;
+import android.window.ActivityWindowInfo;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.function.BiConsumer;
+
/**
* Singleton controller to manage listeners to individual {@link ClientTransaction}.
*
@@ -35,8 +43,14 @@
private static ClientTransactionListenerController sController;
+ private final Object mLock = new Object();
private final DisplayManagerGlobal mDisplayManager;
+ /** Listeners registered via {@link #registerActivityWindowInfoChangedListener(BiConsumer)}. */
+ @GuardedBy("mLock")
+ private final ArraySet<BiConsumer<IBinder, ActivityWindowInfo>>
+ mActivityWindowInfoChangedListeners = new ArraySet<>();
+
/** Gets the singleton controller. */
@NonNull
public static ClientTransactionListenerController getInstance() {
@@ -62,6 +76,57 @@
}
/**
+ * Registers to listen on activity {@link ActivityWindowInfo} change.
+ * The listener will be invoked with two parameters: {@link Activity#getActivityToken()} and
+ * {@link ActivityWindowInfo}.
+ */
+ public void registerActivityWindowInfoChangedListener(
+ @NonNull BiConsumer<IBinder, ActivityWindowInfo> listener) {
+ if (!activityWindowInfoFlag()) {
+ return;
+ }
+ synchronized (mLock) {
+ mActivityWindowInfoChangedListeners.add(listener);
+ }
+ }
+
+ /**
+ * Unregisters the listener that was previously registered via
+ * {@link #registerActivityWindowInfoChangedListener(BiConsumer)}
+ */
+ public void unregisterActivityWindowInfoChangedListener(
+ @NonNull BiConsumer<IBinder, ActivityWindowInfo> listener) {
+ if (!activityWindowInfoFlag()) {
+ return;
+ }
+ synchronized (mLock) {
+ mActivityWindowInfoChangedListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Called when receives a {@link ClientTransaction} that is updating an activity's
+ * {@link ActivityWindowInfo}.
+ */
+ public void onActivityWindowInfoChanged(@NonNull IBinder activityToken,
+ @NonNull ActivityWindowInfo activityWindowInfo) {
+ if (!activityWindowInfoFlag()) {
+ return;
+ }
+ final Object[] activityWindowInfoChangedListeners;
+ synchronized (mLock) {
+ if (mActivityWindowInfoChangedListeners.isEmpty()) {
+ return;
+ }
+ activityWindowInfoChangedListeners = mActivityWindowInfoChangedListeners.toArray();
+ }
+ for (Object activityWindowInfoChangedListener : activityWindowInfoChangedListeners) {
+ ((BiConsumer<IBinder, ActivityWindowInfo>) activityWindowInfoChangedListener)
+ .accept(activityToken, activityWindowInfo);
+ }
+ }
+
+ /**
* Called when receives a {@link ClientTransaction} that is updating display-related
* window configuration.
*/
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index f9cf075..b0213d7 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -33,7 +33,6 @@
public class DestroyActivityItem extends ActivityLifecycleItem {
private boolean mFinished;
- private int mConfigChanges;
@Override
public void preExecute(@NonNull ClientTransactionHandler client) {
@@ -44,7 +43,7 @@
public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@NonNull PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
- client.handleDestroyActivity(r, mFinished, mConfigChanges,
+ client.handleDestroyActivity(r, mFinished,
false /* getNonConfigInstance */, "DestroyActivityItem");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -67,15 +66,13 @@
/** Obtain an instance initialized with provided params. */
@NonNull
- public static DestroyActivityItem obtain(@NonNull IBinder activityToken, boolean finished,
- int configChanges) {
+ public static DestroyActivityItem obtain(@NonNull IBinder activityToken, boolean finished) {
DestroyActivityItem instance = ObjectPool.obtain(DestroyActivityItem.class);
if (instance == null) {
instance = new DestroyActivityItem();
}
instance.setActivityToken(activityToken);
instance.mFinished = finished;
- instance.mConfigChanges = configChanges;
return instance;
}
@@ -84,7 +81,6 @@
public void recycle() {
super.recycle();
mFinished = false;
- mConfigChanges = 0;
ObjectPool.recycle(this);
}
@@ -95,14 +91,12 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeBoolean(mFinished);
- dest.writeInt(mConfigChanges);
}
/** Read from Parcel. */
private DestroyActivityItem(@NonNull Parcel in) {
super(in);
mFinished = in.readBoolean();
- mConfigChanges = in.readInt();
}
public static final @NonNull Creator<DestroyActivityItem> CREATOR = new Creator<>() {
@@ -124,7 +118,7 @@
return false;
}
final DestroyActivityItem other = (DestroyActivityItem) o;
- return mFinished == other.mFinished && mConfigChanges == other.mConfigChanges;
+ return mFinished == other.mFinished;
}
@Override
@@ -132,14 +126,12 @@
int result = 17;
result = 31 * result + super.hashCode();
result = 31 * result + (mFinished ? 1 : 0);
- result = 31 * result + mConfigChanges;
return result;
}
@Override
public String toString() {
return "DestroyActivityItem{" + super.toString()
- + ",finished=" + mFinished
- + ",mConfigChanges=" + mConfigChanges + "}";
+ + ",finished=" + mFinished + "}";
}
}
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index 8f1e90b..d230284 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -37,7 +37,6 @@
private boolean mFinished;
private boolean mUserLeaving;
- private int mConfigChanges;
private boolean mDontReport;
private boolean mAutoEnteringPip;
@@ -45,7 +44,7 @@
public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@NonNull PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- client.handlePauseActivity(r, mFinished, mUserLeaving, mConfigChanges, mAutoEnteringPip,
+ client.handlePauseActivity(r, mFinished, mUserLeaving, mAutoEnteringPip,
pendingActions, "PAUSE_ACTIVITY_ITEM");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -72,7 +71,7 @@
/** Obtain an instance initialized with provided params. */
@NonNull
public static PauseActivityItem obtain(@NonNull IBinder activityToken, boolean finished,
- boolean userLeaving, int configChanges, boolean dontReport, boolean autoEnteringPip) {
+ boolean userLeaving, boolean dontReport, boolean autoEnteringPip) {
PauseActivityItem instance = ObjectPool.obtain(PauseActivityItem.class);
if (instance == null) {
instance = new PauseActivityItem();
@@ -80,7 +79,6 @@
instance.setActivityToken(activityToken);
instance.mFinished = finished;
instance.mUserLeaving = userLeaving;
- instance.mConfigChanges = configChanges;
instance.mDontReport = dontReport;
instance.mAutoEnteringPip = autoEnteringPip;
@@ -91,7 +89,7 @@
@NonNull
public static PauseActivityItem obtain(@NonNull IBinder activityToken) {
return obtain(activityToken, false /* finished */, false /* userLeaving */,
- 0 /* configChanges */, true /* dontReport */, false /* autoEnteringPip*/);
+ true /* dontReport */, false /* autoEnteringPip*/);
}
@Override
@@ -99,7 +97,6 @@
super.recycle();
mFinished = false;
mUserLeaving = false;
- mConfigChanges = 0;
mDontReport = false;
mAutoEnteringPip = false;
ObjectPool.recycle(this);
@@ -113,7 +110,6 @@
super.writeToParcel(dest, flags);
dest.writeBoolean(mFinished);
dest.writeBoolean(mUserLeaving);
- dest.writeInt(mConfigChanges);
dest.writeBoolean(mDontReport);
dest.writeBoolean(mAutoEnteringPip);
}
@@ -123,7 +119,6 @@
super(in);
mFinished = in.readBoolean();
mUserLeaving = in.readBoolean();
- mConfigChanges = in.readInt();
mDontReport = in.readBoolean();
mAutoEnteringPip = in.readBoolean();
}
@@ -148,7 +143,7 @@
}
final PauseActivityItem other = (PauseActivityItem) o;
return mFinished == other.mFinished && mUserLeaving == other.mUserLeaving
- && mConfigChanges == other.mConfigChanges && mDontReport == other.mDontReport
+ && mDontReport == other.mDontReport
&& mAutoEnteringPip == other.mAutoEnteringPip;
}
@@ -158,7 +153,6 @@
result = 31 * result + super.hashCode();
result = 31 * result + (mFinished ? 1 : 0);
result = 31 * result + (mUserLeaving ? 1 : 0);
- result = 31 * result + mConfigChanges;
result = 31 * result + (mDontReport ? 1 : 0);
result = 31 * result + (mAutoEnteringPip ? 1 : 0);
return result;
@@ -169,7 +163,6 @@
return "PauseActivityItem{" + super.toString()
+ ",finished=" + mFinished
+ ",userLeaving=" + mUserLeaving
- + ",configChanges=" + mConfigChanges
+ ",dontReport=" + mDontReport
+ ",autoEnteringPip=" + mAutoEnteringPip + "}";
}
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index b8ce52d..def7b3f 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -19,7 +19,6 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
@@ -34,13 +33,11 @@
private static final String TAG = "StopActivityItem";
- private int mConfigChanges;
-
@Override
public void execute(@NonNull ClientTransactionHandler client, @NonNull ActivityClientRecord r,
@NonNull PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
- client.handleStopActivity(r, mConfigChanges, pendingActions,
+ client.handleStopActivity(r, pendingActions,
true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -63,16 +60,14 @@
/**
* Obtain an instance initialized with provided params.
* @param activityToken the activity that stops.
- * @param configChanges Configuration pieces that changed.
*/
@NonNull
- public static StopActivityItem obtain(@NonNull IBinder activityToken, int configChanges) {
+ public static StopActivityItem obtain(@NonNull IBinder activityToken) {
StopActivityItem instance = ObjectPool.obtain(StopActivityItem.class);
if (instance == null) {
instance = new StopActivityItem();
}
instance.setActivityToken(activityToken);
- instance.mConfigChanges = configChanges;
return instance;
}
@@ -80,23 +75,14 @@
@Override
public void recycle() {
super.recycle();
- mConfigChanges = 0;
ObjectPool.recycle(this);
}
// Parcelable implementation
- /** Write to Parcel. */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(mConfigChanges);
- }
-
/** Read from Parcel. */
private StopActivityItem(@NonNull Parcel in) {
super(in);
- mConfigChanges = in.readInt();
}
public static final @NonNull Creator<StopActivityItem> CREATOR = new Creator<>() {
@@ -110,28 +96,7 @@
};
@Override
- public boolean equals(@Nullable Object o) {
- if (this == o) {
- return true;
- }
- if (!super.equals(o)) {
- return false;
- }
- final StopActivityItem other = (StopActivityItem) o;
- return mConfigChanges == other.mConfigChanges;
- }
-
- @Override
- public int hashCode() {
- int result = 17;
- result = 31 * result + super.hashCode();
- result = 31 * result + mConfigChanges;
- return result;
- }
-
- @Override
public String toString() {
- return "StopActivityItem{" + super.toString()
- + ",configChanges=" + mConfigChanges + "}";
+ return "StopActivityItem{" + super.toString() + "}";
}
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index fa73c99..c837191 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -334,18 +334,18 @@
break;
case ON_PAUSE:
mTransactionHandler.handlePauseActivity(r, false /* finished */,
- false /* userLeaving */, 0 /* configChanges */,
+ false /* userLeaving */,
false /* autoEnteringPip */, mPendingActions,
"LIFECYCLER_PAUSE_ACTIVITY");
break;
case ON_STOP:
- mTransactionHandler.handleStopActivity(r, 0 /* configChanges */,
+ mTransactionHandler.handleStopActivity(r,
mPendingActions, false /* finalStateRequest */,
"LIFECYCLER_STOP_ACTIVITY");
break;
case ON_DESTROY:
mTransactionHandler.handleDestroyActivity(r, false /* finishing */,
- 0 /* configChanges */, false /* getNonConfigInstance */,
+ false /* getNonConfigInstance */,
"performLifecycleSequence. cycling to:" + path.get(size - 1));
break;
case ON_RESTART:
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 475c6fb..710261a 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -200,7 +200,7 @@
lifecycleItem = PauseActivityItem.obtain(r.token);
break;
case ON_STOP:
- lifecycleItem = StopActivityItem.obtain(r.token, 0 /* configChanges */);
+ lifecycleItem = StopActivityItem.obtain(r.token);
break;
default:
lifecycleItem = ResumeActivityItem.obtain(r.token, false /* isForward */,
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 5e9d8f0..610057b 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -208,6 +208,14 @@
}
flag {
+ name: "restrict_nonpreloads_system_shareduids"
+ namespace: "package_manager_service"
+ description: "Feature flag to restrict apps from joining system shared uids"
+ bug: "308573169"
+ is_fixed_read_only: true
+}
+
+flag {
name: "min_target_sdk_24"
namespace: "responsible_apis"
description: "Feature flag to bump min target sdk to 24"
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index 4b0fa6d..79fba9b 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -80,17 +80,7 @@
ArrayList<DisabledProviderData> disabledProviderDataList,
@NonNull ResultReceiver resultReceiver) {
Intent intent = new Intent();
- ComponentName componentName =
- ComponentName.unflattenFromString(
- Resources.getSystem()
- .getString(
- com.android.internal.R.string
- .config_credentialManagerDialogComponent));
- ComponentName oemOverrideComponentName = getOemOverrideComponentName(context);
- if (oemOverrideComponentName != null) {
- componentName = oemOverrideComponentName;
- }
- intent.setComponent(componentName);
+ setCredentialSelectorUiComponentName(context, intent);
intent.putParcelableArrayListExtra(
ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST, disabledProviderDataList);
intent.putExtra(RequestInfo.EXTRA_REQUEST_INFO, requestInfo);
@@ -100,6 +90,24 @@
return intent;
}
+ private static void setCredentialSelectorUiComponentName(@NonNull Context context,
+ @NonNull Intent intent) {
+ if (configurableSelectorUiEnabled()) {
+ ComponentName componentName = getOemOverrideComponentName(context);
+ if (componentName == null) {
+ componentName = ComponentName.unflattenFromString(Resources.getSystem().getString(
+ com.android.internal.R.string
+ .config_fallbackCredentialManagerDialogComponent));
+ }
+ intent.setComponent(componentName);
+ } else {
+ ComponentName componentName = ComponentName.unflattenFromString(Resources.getSystem()
+ .getString(com.android.internal.R.string
+ .config_fallbackCredentialManagerDialogComponent));
+ intent.setComponent(componentName);
+ }
+ }
+
/**
* Returns null if there is not an enabled and valid oem override component. It means the
* default platform UI component name should be used instead.
@@ -107,44 +115,39 @@
@Nullable
private static ComponentName getOemOverrideComponentName(@NonNull Context context) {
ComponentName result = null;
- if (configurableSelectorUiEnabled()) {
- if (Resources.getSystem().getBoolean(
- com.android.internal.R.bool.config_enableOemCredentialManagerDialogComponent)) {
- String oemComponentString =
- Resources.getSystem()
- .getString(
- com.android.internal.R.string
- .config_oemCredentialManagerDialogComponent);
- if (!TextUtils.isEmpty(oemComponentString)) {
- ComponentName oemComponentName = ComponentName.unflattenFromString(
- oemComponentString);
- if (oemComponentName != null) {
- try {
- ActivityInfo info = context.getPackageManager().getActivityInfo(
- oemComponentName,
- PackageManager.ComponentInfoFlags.of(
- PackageManager.MATCH_SYSTEM_ONLY));
- if (info.enabled && info.exported) {
- Slog.i(TAG,
- "Found enabled oem CredMan UI component."
- + oemComponentString);
- result = oemComponentName;
- } else {
- Slog.i(TAG,
- "Found enabled oem CredMan UI component but it was not "
- + "enabled.");
- }
- } catch (PackageManager.NameNotFoundException e) {
- Slog.i(TAG, "Unable to find oem CredMan UI component: "
- + oemComponentString + ".");
- }
+ String oemComponentString =
+ Resources.getSystem()
+ .getString(
+ com.android.internal.R.string
+ .config_oemCredentialManagerDialogComponent);
+ if (!TextUtils.isEmpty(oemComponentString)) {
+ ComponentName oemComponentName = ComponentName.unflattenFromString(
+ oemComponentString);
+ if (oemComponentName != null) {
+ try {
+ ActivityInfo info = context.getPackageManager().getActivityInfo(
+ oemComponentName,
+ PackageManager.ComponentInfoFlags.of(
+ PackageManager.MATCH_SYSTEM_ONLY));
+ if (info.enabled && info.exported) {
+ Slog.i(TAG,
+ "Found enabled oem CredMan UI component."
+ + oemComponentString);
+ result = oemComponentName;
} else {
- Slog.i(TAG, "Invalid OEM ComponentName format.");
+ Slog.i(TAG,
+ "Found enabled oem CredMan UI component but it was not "
+ + "enabled.");
}
- } else {
- Slog.i(TAG, "Invalid empty OEM component name.");
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.i(TAG, "Unable to find oem CredMan UI component: "
+ + oemComponentString + ".");
}
+ } else {
+ Slog.i(TAG, "Invalid OEM ComponentName format.");
}
+ } else {
+ Slog.i(TAG, "Invalid empty OEM component name.");
}
return result;
}
@@ -186,16 +189,11 @@
* Creates an Intent that cancels any UI matching the given request token id.
*/
@NonNull
- public static Intent createCancelUiIntent(@NonNull IBinder requestToken,
- boolean shouldShowCancellationUi, @NonNull String appPackageName) {
+ public static Intent createCancelUiIntent(@NonNull Context context,
+ @NonNull IBinder requestToken, boolean shouldShowCancellationUi,
+ @NonNull String appPackageName) {
Intent intent = new Intent();
- ComponentName componentName =
- ComponentName.unflattenFromString(
- Resources.getSystem()
- .getString(
- com.android.internal.R.string
- .config_credentialManagerDialogComponent));
- intent.setComponent(componentName);
+ setCredentialSelectorUiComponentName(context, intent);
intent.putExtra(CancelSelectionRequest.EXTRA_CANCEL_UI_REQUEST,
new CancelSelectionRequest(new RequestToken(requestToken), shouldShowCancellationUi,
appPackageName));
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 57b437f..dc8f4b4 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -3521,7 +3521,7 @@
* <p>When the key is present, only a PRIVATE/YUV output of the specified size is guaranteed
* to be supported by the camera HAL in the secure camera mode. Any other format or
* resolutions might not be supported. Use
- * {@link CameraManager#isSessionConfigurationWithParametersSupported }
+ * {@link CameraDevice#isSessionConfigurationSupported }
* API to query if a secure session configuration is supported if the device supports this
* API.</p>
* <p>If this key returns null on a device with SECURE_IMAGE_DATA capability, the application
@@ -5046,18 +5046,18 @@
/**
* <p>The version of the session configuration query
- * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported }
+ * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }
* API</p>
* <p>The possible values in this key correspond to the values defined in
* android.os.Build.VERSION_CODES. Each version defines a set of feature combinations the
* camera device must reliably report whether they are supported via
- * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported }
+ * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }
* API. And the version is always less or equal to android.os.Build.VERSION.SDK_INT.</p>
* <p>If set to UPSIDE_DOWN_CAKE, this camera device doesn't support
- * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported }.
+ * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }.
* Calling the method for this camera ID throws an UnsupportedOperationException.</p>
* <p>If set to VANILLA_ICE_CREAM, the application can call
- * {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported }
+ * {@link android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported }
* to check if the combinations of below features are supported.</p>
* <ul>
* <li>A subset of LIMITED-level device stream combinations.</li>
@@ -6082,11 +6082,11 @@
/**
* <p>Minimum and maximum padding zoom factors supported by this camera device for
- * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used for the
+ * android.efv.paddingZoomFactor used for the
* {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
* extension.</p>
* <p>The minimum and maximum padding zoom factors supported by the device for
- * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used as part of the
+ * android.efv.paddingZoomFactor used as part of the
* {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
* extension feature. This extension specific camera characteristic can be queried using
* {@link android.hardware.camera2.CameraExtensionCharacteristics#get }.</p>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index e24c98e..9fb561b 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -911,10 +911,10 @@
* </ul>
* <p>Combinations of logical and physical streams, or physical streams from different
* physical cameras are not guaranteed. However, if the camera device supports
- * {@link CameraManager#isSessionConfigurationWithParametersSupported },
+ * {@link CameraDevice#isSessionConfigurationSupported },
* application must be able to query whether a stream combination involving physical
* streams is supported by calling
- * {@link CameraManager#isSessionConfigurationWithParametersSupported }.</p>
+ * {@link CameraDevice#isSessionConfigurationSupported }.</p>
* <p>Camera application shouldn't assume that there are at most 1 rear camera and 1 front
* camera in the system. For an application that switches between front and back cameras,
* the recommendation is to switch between the first rear camera and the first front
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 9fbe348..f3b7b91 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -260,7 +260,7 @@
* smaller sizes, then the resulting
* {@link android.hardware.camera2.params.SessionConfiguration session configuration} can
* be tested either by calling {@link CameraDevice#createCaptureSession} or
- * {@link CameraManager#isSessionConfigurationWithParametersSupported}.
+ * {@link CameraDeviceSetup#isSessionConfigurationSupported}.
*
* @return non-modifiable ascending list of available sizes.
*/
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 1f54959..2816f77 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -27,6 +27,7 @@
import android.hardware.input.IKeyboardBacklightState;
import android.hardware.input.IStickyModifierStateListener;
import android.hardware.input.ITabletModeChangedListener;
+import android.hardware.input.KeyboardLayoutSelectionResult;
import android.hardware.input.TouchCalibration;
import android.os.CombinedVibration;
import android.hardware.input.IInputSensorEventListener;
@@ -120,8 +121,9 @@
String keyboardLayoutDescriptor);
// New Keyboard layout config APIs
- String getKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier, int userId,
- in InputMethodInfo imeInfo, in InputMethodSubtype imeSubtype);
+ KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice(
+ in InputDeviceIdentifier identifier, int userId, in InputMethodInfo imeInfo,
+ in InputMethodSubtype imeSubtype);
@EnforcePermission("SET_KEYBOARD_LAYOUT")
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 744dfae9..a1242fb 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -784,10 +784,10 @@
*
* @hide
*/
- @Nullable
- public String getKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier,
- @UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
- @Nullable InputMethodSubtype imeSubtype) {
+ @NonNull
+ public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice(
+ @NonNull InputDeviceIdentifier identifier, @UserIdInt int userId,
+ @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) {
try {
return mIm.getKeyboardLayoutForInputDevice(identifier, userId, imeInfo, imeSubtype);
} catch (RemoteException ex) {
diff --git a/core/java/android/hardware/input/KeyboardLayoutSelectionResult.aidl b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.aidl
new file mode 100644
index 0000000..13be2ff
--- /dev/null
+++ b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 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.hardware.input;
+
+parcelable KeyboardLayoutSelectionResult;
diff --git a/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java
new file mode 100644
index 0000000..5a1c947
--- /dev/null
+++ b/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 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.hardware.input;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.util.DataClass;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides information about the selected layout and the selection criteria when the caller calls
+ * {@link InputManager#getKeyboardLayoutForInputDevice(InputDeviceIdentifier, int, InputMethodInfo,
+ * InputMethodSubtype)}
+ *
+ * @hide
+ */
+
+@DataClass(genParcelable = true, genToString = true, genEqualsHashCode = true)
+public final class KeyboardLayoutSelectionResult implements Parcelable {
+ @Nullable
+ private final String mLayoutDescriptor;
+
+ /** Unspecified layout selection criteria */
+ public static final int LAYOUT_SELECTION_CRITERIA_UNSPECIFIED = 0;
+
+ /** Manual selection by user */
+ public static final int LAYOUT_SELECTION_CRITERIA_USER = 1;
+
+ /** Auto-detection based on device provided language tag and layout type */
+ public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 2;
+
+ /** Auto-detection based on IME provided language tag and layout type */
+ public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 3;
+
+ /** Default selection */
+ public static final int LAYOUT_SELECTION_CRITERIA_DEFAULT = 4;
+
+ /** Failed layout selection */
+ public static final KeyboardLayoutSelectionResult FAILED = new KeyboardLayoutSelectionResult(
+ null, LAYOUT_SELECTION_CRITERIA_UNSPECIFIED);
+
+ @LayoutSelectionCriteria
+ private final int mSelectionCriteria;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @IntDef(prefix = "LAYOUT_SELECTION_CRITERIA_", value = {
+ LAYOUT_SELECTION_CRITERIA_UNSPECIFIED,
+ LAYOUT_SELECTION_CRITERIA_USER,
+ LAYOUT_SELECTION_CRITERIA_DEVICE,
+ LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ LAYOUT_SELECTION_CRITERIA_DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface LayoutSelectionCriteria {}
+
+ @DataClass.Generated.Member
+ public static String layoutSelectionCriteriaToString(@LayoutSelectionCriteria int value) {
+ switch (value) {
+ case LAYOUT_SELECTION_CRITERIA_UNSPECIFIED:
+ return "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED";
+ case LAYOUT_SELECTION_CRITERIA_USER:
+ return "LAYOUT_SELECTION_CRITERIA_USER";
+ case LAYOUT_SELECTION_CRITERIA_DEVICE:
+ return "LAYOUT_SELECTION_CRITERIA_DEVICE";
+ case LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD:
+ return "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD";
+ case LAYOUT_SELECTION_CRITERIA_DEFAULT:
+ return "LAYOUT_SELECTION_CRITERIA_DEFAULT";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ public KeyboardLayoutSelectionResult(
+ @Nullable String layoutDescriptor,
+ @LayoutSelectionCriteria int selectionCriteria) {
+ this.mLayoutDescriptor = layoutDescriptor;
+ this.mSelectionCriteria = selectionCriteria;
+
+ if (!(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_UNSPECIFIED)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_USER)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEFAULT)) {
+ throw new java.lang.IllegalArgumentException(
+ "selectionCriteria was " + mSelectionCriteria + " but must be one of: "
+ + "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED(" + LAYOUT_SELECTION_CRITERIA_UNSPECIFIED + "), "
+ + "LAYOUT_SELECTION_CRITERIA_USER(" + LAYOUT_SELECTION_CRITERIA_USER + "), "
+ + "LAYOUT_SELECTION_CRITERIA_DEVICE(" + LAYOUT_SELECTION_CRITERIA_DEVICE + "), "
+ + "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD(" + LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD + "), "
+ + "LAYOUT_SELECTION_CRITERIA_DEFAULT(" + LAYOUT_SELECTION_CRITERIA_DEFAULT + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getLayoutDescriptor() {
+ return mLayoutDescriptor;
+ }
+
+ @DataClass.Generated.Member
+ public @LayoutSelectionCriteria int getSelectionCriteria() {
+ return mSelectionCriteria;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "KeyboardLayoutSelectionResult { " +
+ "layoutDescriptor = " + mLayoutDescriptor + ", " +
+ "selectionCriteria = " + layoutSelectionCriteriaToString(mSelectionCriteria) +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(KeyboardLayoutSelectionResult other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ KeyboardLayoutSelectionResult that = (KeyboardLayoutSelectionResult) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mLayoutDescriptor, that.mLayoutDescriptor)
+ && mSelectionCriteria == that.mSelectionCriteria;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mLayoutDescriptor);
+ _hash = 31 * _hash + mSelectionCriteria;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mLayoutDescriptor != null) flg |= 0x1;
+ dest.writeByte(flg);
+ if (mLayoutDescriptor != null) dest.writeString(mLayoutDescriptor);
+ dest.writeInt(mSelectionCriteria);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ KeyboardLayoutSelectionResult(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ String layoutDescriptor = (flg & 0x1) == 0 ? null : in.readString();
+ int selectionCriteria = in.readInt();
+
+ this.mLayoutDescriptor = layoutDescriptor;
+ this.mSelectionCriteria = selectionCriteria;
+
+ if (!(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_UNSPECIFIED)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_USER)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD)
+ && !(mSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEFAULT)) {
+ throw new java.lang.IllegalArgumentException(
+ "selectionCriteria was " + mSelectionCriteria + " but must be one of: "
+ + "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED(" + LAYOUT_SELECTION_CRITERIA_UNSPECIFIED + "), "
+ + "LAYOUT_SELECTION_CRITERIA_USER(" + LAYOUT_SELECTION_CRITERIA_USER + "), "
+ + "LAYOUT_SELECTION_CRITERIA_DEVICE(" + LAYOUT_SELECTION_CRITERIA_DEVICE + "), "
+ + "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD(" + LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD + "), "
+ + "LAYOUT_SELECTION_CRITERIA_DEFAULT(" + LAYOUT_SELECTION_CRITERIA_DEFAULT + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<KeyboardLayoutSelectionResult> CREATOR
+ = new Parcelable.Creator<KeyboardLayoutSelectionResult>() {
+ @Override
+ public KeyboardLayoutSelectionResult[] newArray(int size) {
+ return new KeyboardLayoutSelectionResult[size];
+ }
+
+ @Override
+ public KeyboardLayoutSelectionResult createFromParcel(@NonNull android.os.Parcel in) {
+ return new KeyboardLayoutSelectionResult(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1709568115865L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/hardware/input/KeyboardLayoutSelectionResult.java",
+ inputSignatures = "private final @android.annotation.Nullable java.lang.String mLayoutDescriptor\npublic static final int LAYOUT_SELECTION_CRITERIA_UNSPECIFIED\npublic static final int LAYOUT_SELECTION_CRITERIA_USER\npublic static final int LAYOUT_SELECTION_CRITERIA_DEVICE\npublic static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD\npublic static final int LAYOUT_SELECTION_CRITERIA_DEFAULT\npublic static final android.hardware.input.KeyboardLayoutSelectionResult FAILED\nprivate final @android.hardware.input.KeyboardLayoutSelectionResult.LayoutSelectionCriteria int mSelectionCriteria\nclass KeyboardLayoutSelectionResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index abfa4e3..d9400ac 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -1,4 +1,5 @@
package: "android.os"
+container: "system"
flag {
name: "android_os_build_vanilla_ice_cream"
@@ -40,6 +41,7 @@
namespace: "profile_experiences"
description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion."
bug: "299069460"
+ is_exported: true
}
flag {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index fa9f03d..410f510 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1788,6 +1788,9 @@
/**
* Gets the permission states for requested package and persistent device.
+ * <p>
+ * <strong>Note: </strong>Default device permissions are not inherited in this API. Returns the
+ * exact permission states for the requested device.
*
* @param packageName name of the package you are checking against
* @param persistentDeviceId id of the persistent device you are checking against
@@ -2073,5 +2076,29 @@
return new PermissionState[size];
}
};
+
+ /** @hide */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PermissionState that = (PermissionState) o;
+ return mGranted == that.mGranted && mFlags == that.mFlags;
+ }
+
+ /** @hide */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mGranted, mFlags);
+ }
+
+ /** @hide */
+ @Override
+ public String toString() {
+ return "PermissionState{"
+ + "mGranted=" + mGranted
+ + ", mFlags=" + mFlags
+ + '}';
+ }
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 51585af..eea6464 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11576,6 +11576,15 @@
"extra_low_power_warning_acknowledged";
/**
+ * Whether the emergency thermal alert would be disabled
+ * (0: default) or not (1).
+ *
+ * @hide
+ */
+ public static final String EMERGENCY_THERMAL_ALERT_DISABLED =
+ "emergency_thermal_alert_disabled";
+
+ /**
* 0 (default) Auto battery saver suggestion has not been suppressed. 1) it has been
* suppressed.
* @hide
diff --git a/core/java/android/view/ScrollFeedbackProvider.java b/core/java/android/view/ScrollFeedbackProvider.java
index 8a44d4f..798203f 100644
--- a/core/java/android/view/ScrollFeedbackProvider.java
+++ b/core/java/android/view/ScrollFeedbackProvider.java
@@ -21,8 +21,11 @@
import android.view.flags.Flags;
/**
- * Interface to represent an entity giving consistent feedback for different events surrounding view
- * scroll.
+ * Provides feedback to the user for scroll events on a {@link View}. The type of feedback provided
+ * to the user may depend on the {@link InputDevice} that generated the scroll events.
+ *
+ * <p>An example of the type of feedback that this interface may provide is haptic feedback (that
+ * is, tactile feedback that provide the user physical feedback for their scroll).
*
* <p>The interface provides methods for the client to report different scroll events. The client
* should report all scroll events that they want to be considered for scroll feedback using the
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index 7f79661..d992feb 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -182,7 +182,6 @@
PHASE_CLIENT_ANIMATION_FINISHED_SHOW,
PHASE_CLIENT_ANIMATION_FINISHED_HIDE,
PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT,
- PHASE_CLIENT_ANIMATION_FINISHED_HIDE,
PHASE_IME_SHOW_WINDOW,
PHASE_IME_HIDE_WINDOW,
PHASE_IME_PRIVILEGED_OPERATIONS,
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 0e5747d..fe4ac4e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1105,6 +1105,7 @@
SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
mViewId = parcel.readInt();
mIntentId = parcel.readInt();
+ mIsReplacedIntoAction = parcel.readBoolean();
mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
mItems = mServiceIntent != null
? null
@@ -1128,6 +1129,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mViewId);
dest.writeInt(mIntentId);
+ dest.writeBoolean(mIsReplacedIntoAction);
dest.writeTypedObject(mServiceIntent, flags);
if (mItems != null) {
mItems.writeToParcel(dest, flags, /* attached= */ true);
@@ -1209,6 +1211,19 @@
}
/**
+ * The maximum size for RemoteViews with converted RemoteCollectionItemsAdapter.
+ * When converting RemoteViewsAdapter to RemoteCollectionItemsAdapter, we want to put size
+ * limits on each unique RemoteCollectionItems in order to not exceed the transaction size limit
+ * for each parcel (typically 1 MB). We leave a certain ratio of the maximum size as a buffer
+ * for missing calculations of certain parameters (e.g. writing a RemoteCollectionItems to the
+ * parcel will write its Id array as well, but that is missing when writing itschild RemoteViews
+ * directly to the parcel as we did in RemoteViewsService)
+ *
+ * @hide
+ */
+ private static final int MAX_SINGLE_PARCEL_SIZE = (int) (1_000_000 * 0.8);
+
+ /**
* @hide
*/
public CompletableFuture<Void> collectAllIntents() {
@@ -1260,17 +1275,47 @@
return mUriToCollectionMapping.get(uri);
}
- CompletableFuture<Void> collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
- CompletableFuture<Void> collectionFuture = CompletableFuture.completedFuture(null);
+ public @NonNull CompletableFuture<Void> collectAllIntentsNoComplete(
+ @NonNull RemoteViews inViews) {
+ SparseArray<Intent> idToIntentMapping = new SparseArray<>();
+ // Collect the number of uinque Intent (which is equal to the number of new connections
+ // to make) for size allocation and exclude certain collections from being written to
+ // the parcel to better estimate the space left for reallocation.
+ collectAllIntentsInternal(inViews, idToIntentMapping);
+
+ // Calculate the individual size here
+ int numOfIntents = idToIntentMapping.size();
+ if (numOfIntents == 0) {
+ Log.e(LOG_TAG, "Possibly notifying updates for nonexistent view Id");
+ return CompletableFuture.completedFuture(null);
+ }
+
+ Parcel sizeTestParcel = Parcel.obtain();
+ // Write self RemoteViews to the parcel, which includes the actions/bitmaps/collection
+ // cache to see how much space is left for the RemoteCollectionItems that are to be
+ // updated.
+ RemoteViews.this.writeToParcel(sizeTestParcel,
+ /* flags= */ 0,
+ /* intentsToIgnore= */ idToIntentMapping);
+ int remainingSize = MAX_SINGLE_PARCEL_SIZE - sizeTestParcel.dataSize();
+ sizeTestParcel.recycle();
+
+ int individualSize = remainingSize < 0
+ ? 0
+ : remainingSize / numOfIntents;
+
+ return connectAllUniqueIntents(individualSize, idToIntentMapping);
+ }
+
+ private void collectAllIntentsInternal(@NonNull RemoteViews inViews,
+ @NonNull SparseArray<Intent> idToIntentMapping) {
if (inViews.hasSizedRemoteViews()) {
for (RemoteViews remoteViews : inViews.mSizedRemoteViews) {
- collectionFuture = CompletableFuture.allOf(collectionFuture,
- collectAllIntentsNoComplete(remoteViews));
+ collectAllIntentsInternal(remoteViews, idToIntentMapping);
}
} else if (inViews.hasLandscapeAndPortraitLayouts()) {
- collectionFuture = CompletableFuture.allOf(
- collectAllIntentsNoComplete(inViews.mLandscape),
- collectAllIntentsNoComplete(inViews.mPortrait));
+ collectAllIntentsInternal(inViews.mLandscape, idToIntentMapping);
+ collectAllIntentsInternal(inViews.mPortrait, idToIntentMapping);
} else if (inViews.mActions != null) {
for (Action action : inViews.mActions) {
if (action instanceof SetRemoteCollectionItemListAdapterAction rca) {
@@ -1280,13 +1325,16 @@
}
if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) {
- final String uri = mIdToUriMapping.get(rca.mIntentId);
- collectionFuture = CompletableFuture.allOf(collectionFuture,
- getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
- .thenAccept(rc -> {
- rc.setHierarchyRootData(getHierarchyRootData());
- mUriToCollectionMapping.put(uri, rc);
- }));
+ rca.mIsReplacedIntoAction = false;
+
+ // Avoid redundant connections for the same intent. Also making sure
+ // that the number of connections we are making is always equal to the
+ // nmuber of unique intents that are being used for the updates.
+ if (idToIntentMapping.contains(rca.mIntentId)) {
+ continue;
+ }
+
+ idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent);
rca.mItems = null;
continue;
}
@@ -1295,7 +1343,7 @@
// intents.
if (rca.mServiceIntent != null) {
final String uri = rca.mServiceIntent.toUri(0);
- int index = mIdToUriMapping.indexOfValue(uri);
+ int index = mIdToUriMapping.indexOfValueByValue(uri);
if (index == -1) {
int newIntentId = mIdToUriMapping.size();
rca.mIntentId = newIntentId;
@@ -1305,41 +1353,50 @@
rca.mItems = null;
continue;
}
- collectionFuture = CompletableFuture.allOf(collectionFuture,
- getItemsFutureFromIntentWithTimeout(rca.mServiceIntent)
- .thenAccept(rc -> {
- rc.setHierarchyRootData(getHierarchyRootData());
- mUriToCollectionMapping.put(uri, rc);
- }));
+
+ idToIntentMapping.put(rca.mIntentId, rca.mServiceIntent);
rca.mItems = null;
} else {
for (RemoteViews views : rca.mItems.mViews) {
- collectionFuture = CompletableFuture.allOf(collectionFuture,
- collectAllIntentsNoComplete(views));
+ collectAllIntentsInternal(views, idToIntentMapping);
}
}
} else if (action instanceof ViewGroupActionAdd vgaa
&& vgaa.mNestedViews != null) {
- collectionFuture = CompletableFuture.allOf(collectionFuture,
- collectAllIntentsNoComplete(vgaa.mNestedViews));
+ collectAllIntentsInternal(vgaa.mNestedViews, idToIntentMapping);
}
}
}
+ }
- return collectionFuture;
+ private @NonNull CompletableFuture<Void> connectAllUniqueIntents(int individualSize,
+ @NonNull SparseArray<Intent> idToIntentMapping) {
+ List<CompletableFuture<Void>> intentFutureList = new ArrayList<>();
+ for (int i = 0; i < idToIntentMapping.size(); i++) {
+ String currentIntentUri = mIdToUriMapping.get(idToIntentMapping.keyAt(i));
+ Intent currentIntent = idToIntentMapping.valueAt(i);
+ intentFutureList.add(getItemsFutureFromIntentWithTimeout(currentIntent,
+ individualSize)
+ .thenAccept(items -> {
+ items.setHierarchyRootData(getHierarchyRootData());
+ mUriToCollectionMapping.put(currentIntentUri, items);
+ }));
+ }
+
+ return CompletableFuture.allOf(intentFutureList.toArray(CompletableFuture[]::new));
}
private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
- Intent intent) {
+ Intent intent, int individualSize) {
if (intent == null) {
Log.e(LOG_TAG, "Null intent received when generating adapter future");
return CompletableFuture.completedFuture(new RemoteCollectionItems
- .Builder().build());
+ .Builder().build());
}
final Context context = ActivityThread.currentApplication();
- final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();
+ final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>();
context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE),
result.defaultExecutor(), new ServiceConnection() {
@Override
@@ -1348,11 +1405,11 @@
RemoteCollectionItems items;
try {
items = IRemoteViewsFactory.Stub.asInterface(iBinder)
- .getRemoteCollectionItems();
+ .getRemoteCollectionItems(individualSize);
} catch (RemoteException re) {
items = new RemoteCollectionItems.Builder().build();
- Log.e(LOG_TAG, "Error getting collection items from the factory",
- re);
+ Log.e(LOG_TAG, "Error getting collection items from the"
+ + " factory", re);
} finally {
context.unbindService(this);
}
@@ -1371,10 +1428,17 @@
return result;
}
- public void writeToParcel(Parcel out, int flags) {
+ public void writeToParcel(Parcel out, int flags,
+ @Nullable SparseArray<Intent> intentsToIgnore) {
out.writeInt(mIdToUriMapping.size());
for (int i = 0; i < mIdToUriMapping.size(); i++) {
- out.writeInt(mIdToUriMapping.keyAt(i));
+ int currentIntentId = mIdToUriMapping.keyAt(i);
+ if (intentsToIgnore != null && intentsToIgnore.contains(currentIntentId)) {
+ // Skip writing collections that are to be updated in the following steps to
+ // better estimate the RemoteViews size.
+ continue;
+ }
+ out.writeInt(currentIntentId);
String intentUri = mIdToUriMapping.valueAt(i);
out.writeString8(intentUri);
mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true);
@@ -6724,7 +6788,13 @@
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcel(dest, flags, /* intentsToIgnore= */ null);
+ }
+
+ private void writeToParcel(Parcel dest, int flags,
+ @Nullable SparseArray<Intent> intentsToIgnore) {
boolean prevSquashingAllowed = dest.allowSquashing();
if (!hasMultipleLayouts()) {
@@ -6733,7 +6803,7 @@
// is shared by all children.
if (mIsRoot) {
mBitmapCache.writeBitmapsToParcel(dest, flags);
- mCollectionCache.writeToParcel(dest, flags);
+ mCollectionCache.writeToParcel(dest, flags, intentsToIgnore);
}
mApplication.writeToParcel(dest, flags);
if (mIsRoot || mIdealSize == null) {
@@ -6750,7 +6820,7 @@
dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS);
if (mIsRoot) {
mBitmapCache.writeBitmapsToParcel(dest, flags);
- mCollectionCache.writeToParcel(dest, flags);
+ mCollectionCache.writeToParcel(dest, flags, intentsToIgnore);
}
dest.writeInt(mSizedRemoteViews.size());
for (RemoteViews view : mSizedRemoteViews) {
@@ -6762,7 +6832,7 @@
// is shared by all children.
if (mIsRoot) {
mBitmapCache.writeBitmapsToParcel(dest, flags);
- mCollectionCache.writeToParcel(dest, flags);
+ mCollectionCache.writeToParcel(dest, flags, intentsToIgnore);
}
mLandscape.writeToParcel(dest, flags);
// Both RemoteViews already share the same package and user
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index a250a86..d4a5bbd 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -19,6 +19,7 @@
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
+import android.os.Parcel;
import com.android.internal.widget.IRemoteViewsFactory;
@@ -43,13 +44,6 @@
private static final Object sLock = new Object();
/**
- * Used for determining the maximum number of entries to retrieve from RemoteViewsFactory
- *
- * @hide
- */
- private static final int MAX_NUM_ENTRY = 10;
-
- /**
* An interface for an adapter between a remote collection view (ListView, GridView, etc) and
* the underlying data for that view. The implementor is responsible for making a RemoteView
* for each item in the data set. This interface is a thin wrapper around {@link Adapter}.
@@ -235,9 +229,10 @@
}
@Override
- public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() {
+ public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems
.Builder().build();
+ Parcel capSizeTestParcel = Parcel.obtain();
try {
RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
@@ -245,15 +240,25 @@
mFactory.onDataSetChanged();
itemsBuilder.setHasStableIds(mFactory.hasStableIds());
- final int numOfEntries = Math.min(mFactory.getCount(), MAX_NUM_ENTRY);
+ final int numOfEntries = mFactory.getCount();
+
for (int i = 0; i < numOfEntries; i++) {
- itemsBuilder.addItem(mFactory.getItemId(i), mFactory.getViewAt(i));
+ final long currentItemId = mFactory.getItemId(i);
+ final RemoteViews currentView = mFactory.getViewAt(i);
+ currentView.writeToParcel(capSizeTestParcel, 0);
+ if (capSizeTestParcel.dataSize() > capSize) {
+ break;
+ }
+ itemsBuilder.addItem(currentItemId, currentView);
}
items = itemsBuilder.build();
} catch (Exception ex) {
Thread t = Thread.currentThread();
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
+ } finally {
+ // Recycle the parcel
+ capSizeTestParcel.recycle();
}
return items;
}
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
index 918d9c0..1d0e972 100644
--- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -39,6 +39,6 @@
boolean hasStableIds();
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean isCreated();
- RemoteViews.RemoteCollectionItems getRemoteCollectionItems();
+ RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 487b5be..ba9751f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8621,6 +8621,10 @@
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
+ <service android:name="com.android.system.virtualmachine.SecretkeeperJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
<service android:name="com.android.server.PruneInstantAppsJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8edf42a..90f2731 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3348,26 +3348,23 @@
<string name="config_carrierAppInstallDialogComponent" translatable="false"
>com.android.simappdialog/com.android.simappdialog.InstallCarrierAppActivity</string>
- <!-- Name of the default framework dialog that is used to get or save an app credential.
+ <!-- Name of the fallback CredentialManager dialog that is used to get or save an app
+ credential.
- This UI should be always launch-able and is used as a fallback when an oem replacement activity
- (defined at config_oemCredentialManagerDialogComponent) is undefined / not found. -->
- <string name="config_credentialManagerDialogComponent" translatable="false"
+ If empty, no fallback will be used. IMPORTANT: In that case the OEM dialog value specified in
+ config_oemCredentialManagerDialogComponent must always launch-able. Otherwise, the
+ CredentialManager API contract is broken.
+
+ If specified, this UI should be always launch-able. It will be used as a fallback when the OEM
+ dialog value specified in config_oemCredentialManagerDialogComponent) is undefined / not
+ found. -->
+ <string name="config_fallbackCredentialManagerDialogComponent" translatable="false"
>com.android.credentialmanager/com.android.credentialmanager.CredentialSelectorActivity</string>
- <!-- Whether to allow the credential selector activity to be replaced by an activity at
- run-time (restricted to the privileged activity specified by
- config_credentialSelectorActivityName).
-
- When disabled, the fallback activity defined at
- config_credentialManagerDialogComponent will be used instead. -->
- <bool name="config_enableOemCredentialManagerDialogComponent" translatable="false">true</bool>
<!-- Fully qualified activity name providing the credential selector UI, that serves the
- CredentialManager APIs.
+ CredentialManager APIs. Must be a system app component.
- Used only when config_enableOemCredentialManagerDialogComponent is true.
-
- If the activity specified cannot be found or launched, then the fallback activity defined at
- config_credentialManagerDialogComponent will be used instead. -->
+ If empty, or if this activity specified cannot be found or launched, then the fallback activity
+ defined at config_fallbackCredentialManagerDialogComponent will be used instead. -->
<string name="config_oemCredentialManagerDialogComponent" translatable="false"></string>
<!-- Name of the broadcast receiver that is used to receive provider change events -->
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 660e4c0..6e56fe2 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -334,17 +334,9 @@
<bool name="config_enable_cellular_on_boot_default">true</bool>
<java-symbol type="bool" name="config_enable_cellular_on_boot_default" />
- <!-- The network capabilities that would be forced marked as cellular transport regardless it's
- on cellular or satellite-->
- <string-array name="config_force_cellular_transport_capabilities">
- <!-- Added the following three capabilities for now. For the long term solution, the client
- requests satellite network should really include TRANSPORT_SATELLITE in the network
- request. With the following workaround, the clients can continue request network with
- the following capabilities with TRANSPORT_CELLULAR. The network with one of the
- following capabilities would also be marked as cellular. -->
- <item>ims</item>
- <item>eims</item>
- <item>xcap</item>
- </string-array>
- <java-symbol type="array" name="config_force_cellular_transport_capabilities" />
+ <!-- Defines metrics pull cooldown period. The default cooldown period is 23 hours,
+ some Telephony metrics need to be pulled more frequently -->
+ <integer name="config_metrics_pull_cooldown_millis">82800000</integer>
+ <java-symbol type="integer" name="config_metrics_pull_cooldown_millis" />
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a180467..a025c8d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2297,8 +2297,7 @@
<java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
<java-symbol type="string" name="config_platformVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_carrierAppInstallDialogComponent" />
- <java-symbol type="string" name="config_credentialManagerDialogComponent" />
- <java-symbol type="bool" name="config_enableOemCredentialManagerDialogComponent" />
+ <java-symbol type="string" name="config_fallbackCredentialManagerDialogComponent" />
<java-symbol type="string" name="config_oemCredentialManagerDialogComponent" />
<java-symbol type="string" name="config_credentialManagerReceiverComponent" />
<java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index d115bf3..927c67c 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -21,16 +21,22 @@
import static android.content.Intent.ACTION_VIEW;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.window.flags.Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
@@ -46,6 +52,7 @@
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.app.servertransaction.ConfigurationChangeItem;
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.ResumeActivityItem;
@@ -60,7 +67,9 @@
import android.hardware.display.VirtualDisplay;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
import android.view.Display;
@@ -81,11 +90,14 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -103,11 +115,17 @@
// few sequence numbers the framework used to launch the test activity.
private static final int BASE_SEQ = 10000000;
- @Rule
+ @Rule(order = 0)
public final ActivityTestRule<TestActivity> mActivityTestRule =
new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */,
false /* launchActivity */);
+ @Rule(order = 1)
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
+ @Mock
+ private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener;
+
private WindowTokenClientController mOriginalWindowTokenClientController;
private Configuration mOriginalAppConfig;
@@ -115,6 +133,8 @@
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
+
// Keep track of the original controller, so that it can be used to restore in tearDown()
// when there is override in some test cases.
mOriginalWindowTokenClientController = WindowTokenClientController.getInstance();
@@ -129,6 +149,8 @@
mCreatedVirtualDisplays = null;
}
WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController);
+ ClientTransactionListenerController.getInstance()
+ .unregisterActivityWindowInfoChangedListener(mActivityWindowInfoListener);
InstrumentationRegistry.getInstrumentation().runOnMainSync(
() -> restoreConfig(ActivityThread.currentActivityThread(), mOriginalAppConfig));
}
@@ -783,6 +805,101 @@
verify(windowTokenClientController).onWindowContextWindowRemoved(clientToken);
}
+ @Test
+ public void testActivityWindowInfoChanged_activityLaunch() {
+ mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener(
+ mActivityWindowInfoListener);
+
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity);
+
+ verify(mActivityWindowInfoListener).accept(activityClientRecord.token,
+ activityClientRecord.getActivityWindowInfo());
+ }
+
+ @Test
+ public void testActivityWindowInfoChanged_activityRelaunch() throws RemoteException {
+ mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener(
+ mActivityWindowInfoListener);
+
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+ appThread.scheduleTransaction(newRelaunchResumeTransaction(activity));
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity);
+
+ // The same ActivityWindowInfo won't trigger duplicated callback.
+ verify(mActivityWindowInfoListener).accept(activityClientRecord.token,
+ activityClientRecord.getActivityWindowInfo());
+
+ final Configuration currentConfig = activity.getResources().getConfiguration();
+ final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+ activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000),
+ new Rect(0, 0, 1000, 1000));
+ final ActivityRelaunchItem relaunchItem = ActivityRelaunchItem.obtain(
+ activity.getActivityToken(), null, null, 0,
+ new MergedConfiguration(currentConfig, currentConfig),
+ false /* preserveWindow */, activityWindowInfo);
+ final ClientTransaction transaction = newTransaction(activity);
+ transaction.addTransactionItem(relaunchItem);
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ verify(mActivityWindowInfoListener).accept(activityClientRecord.token,
+ activityWindowInfo);
+ }
+
+ @Test
+ public void testActivityWindowInfoChanged_activityConfigurationChanged()
+ throws RemoteException {
+ mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener(
+ mActivityWindowInfoListener);
+
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ clearInvocations(mActivityWindowInfoListener);
+ final Configuration config = new Configuration(activity.getResources().getConfiguration());
+ config.seq++;
+ final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+ final Rect taskFragmentBounds = new Rect(0, 0, 1000, 1000);
+ final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+ activityWindowInfo.set(true /* isEmbedded */, taskBounds, taskFragmentBounds);
+ final ActivityConfigurationChangeItem activityConfigurationChangeItem =
+ ActivityConfigurationChangeItem.obtain(
+ activity.getActivityToken(), config, activityWindowInfo);
+ final ClientTransaction transaction = newTransaction(activity);
+ transaction.addTransactionItem(activityConfigurationChangeItem);
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ verify(mActivityWindowInfoListener).accept(activity.getActivityToken(),
+ activityWindowInfo);
+
+ clearInvocations(mActivityWindowInfoListener);
+ final ActivityWindowInfo activityWindowInfo2 = new ActivityWindowInfo();
+ activityWindowInfo2.set(true /* isEmbedded */, taskBounds, taskFragmentBounds);
+ config.seq++;
+ final ActivityConfigurationChangeItem activityConfigurationChangeItem2 =
+ ActivityConfigurationChangeItem.obtain(
+ activity.getActivityToken(), config, activityWindowInfo2);
+ final ClientTransaction transaction2 = newTransaction(activity);
+ transaction2.addTransactionItem(activityConfigurationChangeItem2);
+ appThread.scheduleTransaction(transaction);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ // The same ActivityWindowInfo won't trigger duplicated callback.
+ verify(mActivityWindowInfoListener, never()).accept(any(), any());
+ }
+
/**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord,
* Configuration, int, ActivityWindowInfo)} to try to push activity configuration to the
@@ -871,7 +988,7 @@
@NonNull
private static ClientTransaction newStopTransaction(@NonNull Activity activity) {
final StopActivityItem stopStateRequest = StopActivityItem.obtain(
- activity.getActivityToken(), 0 /* configChanges */);
+ activity.getActivityToken());
final ClientTransaction transaction = newTransaction(activity);
transaction.addTransactionItem(stopStateRequest);
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
index 4db5d1b..9907397 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
@@ -136,7 +136,7 @@
@Test
public void testDestroyActivityItem_preExecute() {
final DestroyActivityItem item = DestroyActivityItem
- .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ .obtain(mActivityToken, false /* finished */);
item.preExecute(mHandler);
assertEquals(1, mActivitiesToBeDestroyed.size());
@@ -146,7 +146,7 @@
@Test
public void testDestroyActivityItem_postExecute() {
final DestroyActivityItem item = DestroyActivityItem
- .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ .obtain(mActivityToken, false /* finished */);
item.preExecute(mHandler);
item.postExecute(mHandler, mPendingActions);
@@ -156,11 +156,11 @@
@Test
public void testDestroyActivityItem_execute() {
final DestroyActivityItem item = DestroyActivityItem
- .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ .obtain(mActivityToken, false /* finished */);
item.execute(mHandler, mActivityClientRecord, mPendingActions);
verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */,
- eq(123) /* configChanges */, eq(false) /* getNonConfigInstance */, any());
+ eq(false) /* getNonConfigInstance */, any());
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 213fd7b..77d31a5 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -22,21 +22,29 @@
import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.IDisplayManager;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.DisplayInfo;
+import android.window.ActivityWindowInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.window.flags.Flags;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -44,6 +52,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.function.BiConsumer;
+
/**
* Tests for {@link ClientTransactionListenerController}.
*
@@ -62,6 +72,10 @@
private IDisplayManager mIDisplayManager;
@Mock
private DisplayManager.DisplayListener mListener;
+ @Mock
+ private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener;
+ @Mock
+ private IBinder mActivityToken;
private DisplayManagerGlobal mDisplayManager;
private Handler mHandler;
@@ -91,4 +105,24 @@
verify(mListener).onDisplayChanged(123);
}
+
+ @Test
+ public void testActivityWindowInfoChangedListener() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ mController.registerActivityWindowInfoChangedListener(mActivityWindowInfoListener);
+ final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+ activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000),
+ new Rect(0, 0, 1000, 1000));
+ mController.onActivityWindowInfoChanged(mActivityToken, activityWindowInfo);
+
+ verify(mActivityWindowInfoListener).accept(mActivityToken, activityWindowInfo);
+
+ clearInvocations(mActivityWindowInfoListener);
+ mController.unregisterActivityWindowInfoChangedListener(mActivityWindowInfoListener);
+
+ mController.onActivityWindowInfoChanged(mActivityToken, activityWindowInfo);
+
+ verify(mActivityWindowInfoListener, never()).accept(any(), any());
+ }
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 31ea675..584fe16 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -98,7 +98,7 @@
@Test
public void testRecycleDestroyActivityItem() {
- testRecycle(() -> DestroyActivityItem.obtain(mActivityToken, true, 117));
+ testRecycle(() -> DestroyActivityItem.obtain(mActivityToken, true));
}
@Test
@@ -169,7 +169,7 @@
@Test
public void testRecyclePauseActivityItemItem() {
- testRecycle(() -> PauseActivityItem.obtain(mActivityToken, true, true, 5, true, true));
+ testRecycle(() -> PauseActivityItem.obtain(mActivityToken, true, true, true, true));
}
@Test
@@ -185,7 +185,7 @@
@Test
public void testRecycleStopItem() {
- testRecycle(() -> StopActivityItem.obtain(mActivityToken, 4));
+ testRecycle(() -> StopActivityItem.obtain(mActivityToken));
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index adb6f2a..935bc75 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -306,7 +306,7 @@
final IBinder token = mock(IBinder.class);
final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */);
destroyTransaction.addTransactionItem(
- DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */));
+ DestroyActivityItem.obtain(token, false /* finished */));
destroyTransaction.preExecute(mTransactionHandler);
// The activity should be added to to-be-destroyed container.
assertEquals(1, activitiesToBeDestroyed.size());
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 75347bf..d451fe5 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -155,8 +155,7 @@
@Test
public void testDestroy() {
- DestroyActivityItem item = DestroyActivityItem.obtain(mActivityToken, true /* finished */,
- 135 /* configChanges */);
+ DestroyActivityItem item = DestroyActivityItem.obtain(mActivityToken, true /* finished */);
writeAndPrepareForReading(item);
// Read from parcel and assert
@@ -244,8 +243,7 @@
public void testPause() {
// Write to parcel
PauseActivityItem item = PauseActivityItem.obtain(mActivityToken, true /* finished */,
- true /* userLeaving */, 135 /* configChanges */, true /* dontReport */,
- true /* autoEnteringPip */);
+ true /* userLeaving */, true /* dontReport */, true /* autoEnteringPip */);
writeAndPrepareForReading(item);
// Read from parcel and assert
@@ -272,7 +270,7 @@
@Test
public void testStop() {
// Write to parcel
- StopActivityItem item = StopActivityItem.obtain(mActivityToken, 14 /* configChanges */);
+ StopActivityItem item = StopActivityItem.obtain(mActivityToken);
writeAndPrepareForReading(item);
// Read from parcel and assert
@@ -305,8 +303,7 @@
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
mActivityToken, config(), new ActivityWindowInfo());
- StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
- 78 /* configChanges */);
+ StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken);
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addTransactionItem(callback1);
@@ -351,8 +348,7 @@
mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
// Write to parcel
- StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
- 78 /* configChanges */);
+ StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken);
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addTransactionItem(lifecycleRequest);
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
index 4f722ce..6ab77dc 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java
@@ -355,7 +355,7 @@
}
@Override
- public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() {
+ public RemoteViews.RemoteCollectionItems getRemoteCollectionItems(int capSize) {
RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
new RemoteViews.RemoteCollectionItems.Builder();
itemsBuilder.setHasStableIds(hasStableIds())
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 66be05f..ed641e0 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -309,17 +309,17 @@
private void pauseActivity(ActivityClientRecord r) {
mThread.handlePauseActivity(r, false /* finished */,
- false /* userLeaving */, 0 /* configChanges */, false /* autoEnteringPip */,
+ false /* userLeaving */, false /* autoEnteringPip */,
null /* pendingActions */, "test");
}
private void stopActivity(ActivityClientRecord r) {
- mThread.handleStopActivity(r, 0 /* configChanges */,
+ mThread.handleStopActivity(r,
new PendingTransactionActions(), false /* finalStateRequest */, "test");
}
private void destroyActivity(ActivityClientRecord r) {
- mThread.handleDestroyActivity(r, true /* finishing */, 0 /* configChanges */,
+ mThread.handleDestroyActivity(r, true /* finishing */,
false /* getNonConfigInstance */, "test");
}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 238a3e1..1410950 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -72,6 +72,12 @@
src: "enhanced-confirmation.xml",
}
+prebuilt_etc {
+ name: "package-shareduid-allowlist.xml",
+ sub_dir: "sysconfig",
+ src: "package-shareduid-allowlist.xml",
+}
+
// Privapp permission whitelist files
prebuilt_etc {
diff --git a/data/etc/CleanSpec.mk b/data/etc/CleanSpec.mk
index 783a7ed..fd38d27 100644
--- a/data/etc/CleanSpec.mk
+++ b/data/etc/CleanSpec.mk
@@ -43,6 +43,8 @@
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/sysconfig/package-shareduid-allowlist.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/sysconfig/package-shareduid-allowlist.xml)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.carrierconfig.xml)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.carrierconfig.xml)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.emergency.xml)
diff --git a/data/etc/package-shareduid-allowlist.xml b/data/etc/package-shareduid-allowlist.xml
new file mode 100644
index 0000000..2401d4a
--- /dev/null
+++ b/data/etc/package-shareduid-allowlist.xml
@@ -0,0 +1,35 @@
+<?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.
+ -->
+
+<!--
+This XML defines an allowlist for packages that want to join a particular shared-uid.
+If a non-system package that is signed with platform signature, is trying to join a particular
+shared-uid, and not in this list, the installation will fail.
+
+- The "package" XML attribute refers to the app's package name.
+- The "shareduid" XML attribute refers to the shared uid name.
+
+Example usage
+ 1. <allow-package-shareduid package="com.example.app" shareduid="android.uid.system"/>
+ Indicates that a package - com.example.app, will be able to join android.uid.system.
+ 2. <allow-package-shareduid package="oem.example.app" shareduid="oem.uid.custom"/>
+ Indicates that a package - oem.example.app, will be able to join oem.uid.custom.
+-->
+
+<config>
+ <allow-package-shareduid package="android.test.settings" shareduid="android.uid.system" />
+</config>
diff --git a/data/fonts/font_fallback.xml b/data/fonts/font_fallback.xml
index 15ea15a..53024ab 100644
--- a/data/fonts/font_fallback.xml
+++ b/data/fonts/font_fallback.xml
@@ -792,14 +792,10 @@
</font>
</family>
<family lang="ja">
- <font weight="400" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font postScriptName="NotoSerifHentaigana-ExtraLight" supportedAxes="wght">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="700" style="normal">
- NotoSerifHentaigana-EL.ttf
- <axis tag="wght" stylevalue="700"/>
- </font>
</family>
<family lang="ko">
<font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular">
diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml
index c1ca67e..ac1b064 100644
--- a/data/fonts/font_fallback_cjkvf.xml
+++ b/data/fonts/font_fallback_cjkvf.xml
@@ -804,14 +804,10 @@
</font>
</family>
<family lang="ja">
- <font weight="400" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font postScriptName="NotoSerifHentaigana-ExtraLight" supportedAxes="wght">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="700" style="normal">
- NotoSerifHentaigana-EL.ttf
- <axis tag="wght" stylevalue="700"/>
- </font>
</family>
<family lang="ko">
<font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index b23f005..d1aa8e9 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -1433,12 +1433,12 @@
</font>
</family>
<family lang="ja">
- <font weight="400" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font weight="400" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="700" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font weight="700" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
diff --git a/data/fonts/fonts_cjkvf.xml b/data/fonts/fonts_cjkvf.xml
index 1ab71ae..9545ae7 100644
--- a/data/fonts/fonts_cjkvf.xml
+++ b/data/fonts/fonts_cjkvf.xml
@@ -1532,12 +1532,12 @@
</font>
</family>
<family lang="ja">
- <font weight="400" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font weight="400" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="700" style="normal">
- NotoSerifHentaigana-EL.ttf
+ <font weight="700" style="normal" postScriptName="NotoSerifHentaigana-ExtraLight">
+ NotoSerifHentaigana.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 6da0719..884268a 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -29,11 +29,13 @@
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.NeverInline;
import libcore.util.NativeAllocationRegistry;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
import java.util.Objects;
/**
@@ -85,6 +87,30 @@
return mChars;
}
+ private void rangeCheck(int start, int end) {
+ if (start < 0 || start > end || end > mChars.length) {
+ throwRangeError(start, end);
+ }
+ }
+
+ @NeverInline
+ private void throwRangeError(int start, int end) {
+ throw new IllegalArgumentException(String.format(Locale.US,
+ "start(%d) end(%d) length(%d) out of bounds", start, end, mChars.length));
+ }
+
+ private void offsetCheck(int offset) {
+ if (offset < 0 || offset >= mChars.length) {
+ throwOffsetError(offset);
+ }
+ }
+
+ @NeverInline
+ private void throwOffsetError(int offset) {
+ throw new IllegalArgumentException(String.format(Locale.US,
+ "offset (%d) length(%d) out of bounds", offset, mChars.length));
+ }
+
/**
* Returns the width of a given range.
*
@@ -93,12 +119,7 @@
*/
public @FloatRange(from = 0.0) @Px float getWidth(
@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
- Preconditions.checkArgument(0 <= start && start <= mChars.length,
- "start(%d) must be 0 <= start <= %d", start, mChars.length);
- Preconditions.checkArgument(0 <= end && end <= mChars.length,
- "end(%d) must be 0 <= end <= %d", end, mChars.length);
- Preconditions.checkArgument(start <= end,
- "start(%d) is larger than end(%d)", start, end);
+ rangeCheck(start, end);
return nGetWidth(mNativePtr, start, end);
}
@@ -120,12 +141,7 @@
*/
public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Rect rect) {
- Preconditions.checkArgument(0 <= start && start <= mChars.length,
- "start(%d) must be 0 <= start <= %d", start, mChars.length);
- Preconditions.checkArgument(0 <= end && end <= mChars.length,
- "end(%d) must be 0 <= end <= %d", end, mChars.length);
- Preconditions.checkArgument(start <= end,
- "start(%d) is larger than end(%d)", start, end);
+ rangeCheck(start, end);
Preconditions.checkNotNull(rect);
nGetBounds(mNativePtr, mChars, start, end, rect);
}
@@ -139,12 +155,7 @@
*/
public void getFontMetricsInt(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Paint.FontMetricsInt outMetrics) {
- Preconditions.checkArgument(0 <= start && start <= mChars.length,
- "start(%d) must be 0 <= start <= %d", start, mChars.length);
- Preconditions.checkArgument(0 <= end && end <= mChars.length,
- "end(%d) must be 0 <= end <= %d", end, mChars.length);
- Preconditions.checkArgument(start <= end,
- "start(%d) is larger than end(%d)", start, end);
+ rangeCheck(start, end);
Objects.requireNonNull(outMetrics);
long packed = nGetExtent(mNativePtr, mChars, start, end);
@@ -160,8 +171,7 @@
* @param offset an offset of the character.
*/
public @FloatRange(from = 0.0f) @Px float getCharWidthAt(@IntRange(from = 0) int offset) {
- Preconditions.checkArgument(0 <= offset && offset < mChars.length,
- "offset(%d) is larger than text length %d" + offset, mChars.length);
+ offsetCheck(offset);
return nGetCharWidthAt(mNativePtr, offset);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 038d008..1abda42 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -56,6 +56,7 @@
import android.app.ActivityThread;
import android.app.Application;
import android.app.Instrumentation;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -103,6 +104,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
/**
* Main controller class that manages split states and presentation.
@@ -178,6 +180,20 @@
private final List<ActivityStack> mLastReportedActivityStacks = new ArrayList<>();
+ /** WM Jetpack set callback for {@link EmbeddedActivityWindowInfo}. */
+ @GuardedBy("mLock")
+ @Nullable
+ private Pair<Executor, Consumer<EmbeddedActivityWindowInfo>>
+ mEmbeddedActivityWindowInfoCallback;
+
+ /** Listener registered to {@link ClientTransactionListenerController}. */
+ @GuardedBy("mLock")
+ @Nullable
+ private final BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener =
+ Flags.activityWindowInfoFlag()
+ ? this::onActivityWindowInfoChanged
+ : null;
+
private final Handler mHandler;
final Object mLock = new Object();
private final ActivityStartMonitor mActivityStartMonitor;
@@ -2456,6 +2472,13 @@
}
@VisibleForTesting
+ @Nullable
+ ActivityThread.ActivityClientRecord getActivityClientRecord(@NonNull Activity activity) {
+ return ActivityThread.currentActivityThread()
+ .getActivityClient(activity.getActivityToken());
+ }
+
+ @VisibleForTesting
ActivityStartMonitor getActivityStartMonitor() {
return mActivityStartMonitor;
}
@@ -2468,8 +2491,7 @@
@VisibleForTesting
@Nullable
IBinder getTaskFragmentTokenFromActivityClientRecord(@NonNull Activity activity) {
- final ActivityThread.ActivityClientRecord record = ActivityThread.currentActivityThread()
- .getActivityClient(activity.getActivityToken());
+ final ActivityThread.ActivityClientRecord record = getActivityClientRecord(activity);
return record != null ? record.mTaskFragmentToken : null;
}
@@ -2876,17 +2898,102 @@
}
}
+ @Override
+ public void setEmbeddedActivityWindowInfoCallback(@NonNull Executor executor,
+ @NonNull Consumer<EmbeddedActivityWindowInfo> callback) {
+ if (!Flags.activityWindowInfoFlag()) {
+ return;
+ }
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ synchronized (mLock) {
+ if (mEmbeddedActivityWindowInfoCallback == null) {
+ ClientTransactionListenerController.getInstance()
+ .registerActivityWindowInfoChangedListener(getActivityWindowInfoListener());
+ }
+ mEmbeddedActivityWindowInfoCallback = new Pair<>(executor, callback);
+ }
+ }
+
+ @Override
+ public void clearEmbeddedActivityWindowInfoCallback() {
+ if (!Flags.activityWindowInfoFlag()) {
+ return;
+ }
+ synchronized (mLock) {
+ if (mEmbeddedActivityWindowInfoCallback == null) {
+ return;
+ }
+ mEmbeddedActivityWindowInfoCallback = null;
+ ClientTransactionListenerController.getInstance()
+ .unregisterActivityWindowInfoChangedListener(getActivityWindowInfoListener());
+ }
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
@Nullable
- private static ActivityWindowInfo getActivityWindowInfo(@NonNull Activity activity) {
+ BiConsumer<IBinder, ActivityWindowInfo> getActivityWindowInfoListener() {
+ return mActivityWindowInfoListener;
+ }
+
+ @Nullable
+ @Override
+ public EmbeddedActivityWindowInfo getEmbeddedActivityWindowInfo(@NonNull Activity activity) {
+ if (!Flags.activityWindowInfoFlag()) {
+ return null;
+ }
+ synchronized (mLock) {
+ final ActivityWindowInfo activityWindowInfo = getActivityWindowInfo(activity);
+ return activityWindowInfo != null
+ ? translateActivityWindowInfo(activity, activityWindowInfo)
+ : null;
+ }
+ }
+
+ @VisibleForTesting
+ void onActivityWindowInfoChanged(@NonNull IBinder activityToken,
+ @NonNull ActivityWindowInfo activityWindowInfo) {
+ synchronized (mLock) {
+ if (mEmbeddedActivityWindowInfoCallback == null) {
+ return;
+ }
+ final Executor executor = mEmbeddedActivityWindowInfoCallback.first;
+ final Consumer<EmbeddedActivityWindowInfo> callback =
+ mEmbeddedActivityWindowInfoCallback.second;
+
+ final Activity activity = getActivity(activityToken);
+ if (activity == null) {
+ return;
+ }
+ final EmbeddedActivityWindowInfo info = translateActivityWindowInfo(
+ activity, activityWindowInfo);
+
+ executor.execute(() -> callback.accept(info));
+ }
+ }
+
+ @Nullable
+ private ActivityWindowInfo getActivityWindowInfo(@NonNull Activity activity) {
if (activity.isFinishing()) {
return null;
}
- final ActivityThread.ActivityClientRecord record =
- ActivityThread.currentActivityThread()
- .getActivityClient(activity.getActivityToken());
+ final ActivityThread.ActivityClientRecord record = getActivityClientRecord(activity);
return record != null ? record.getActivityWindowInfo() : null;
}
+ @NonNull
+ private static EmbeddedActivityWindowInfo translateActivityWindowInfo(
+ @NonNull Activity activity, @NonNull ActivityWindowInfo activityWindowInfo) {
+ final boolean isEmbedded = activityWindowInfo.isEmbedded();
+ final Rect activityBounds = new Rect(activity.getResources().getConfiguration()
+ .windowConfiguration.getBounds());
+ final Rect taskBounds = new Rect(activityWindowInfo.getTaskBounds());
+ final Rect activityStackBounds = new Rect(activityWindowInfo.getTaskFragmentBounds());
+ return new EmbeddedActivityWindowInfo(activity, isEmbedded, activityBounds, taskBounds,
+ activityStackBounds);
+ }
+
/**
* If the two rules have the same presentation, and the calculated {@link SplitAttributes}
* matches the {@link SplitAttributes} of {@link SplitContainer}, we can reuse the same
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 00f8b59..bdeeb73 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -72,6 +72,8 @@
import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityOptions;
+import android.app.ActivityThread;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -83,9 +85,11 @@
import android.os.Handler;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArraySet;
import android.view.WindowInsets;
import android.view.WindowMetrics;
+import android.window.ActivityWindowInfo;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentParentInfo;
@@ -99,7 +103,10 @@
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutInfo;
+import com.android.window.flags.Flags;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -110,6 +117,8 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -127,6 +136,9 @@
private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent(
new ComponentName("test", "placeholder"));
+ @Rule
+ public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
+
private Activity mActivity;
@Mock
private Resources mActivityResources;
@@ -138,6 +150,13 @@
private Handler mHandler;
@Mock
private WindowLayoutComponentImpl mWindowLayoutComponent;
+ @Mock
+ private ActivityWindowInfo mActivityWindowInfo;
+ @Mock
+ private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener;
+ @Mock
+ private androidx.window.extensions.core.util.function.Consumer<EmbeddedActivityWindowInfo>
+ mEmbeddedActivityWindowInfoCallback;
private SplitController mSplitController;
private SplitPresenter mSplitPresenter;
@@ -1529,6 +1548,73 @@
.getTopNonFinishingActivity(), secondaryActivity);
}
+ @Test
+ public void testIsActivityEmbedded() {
+ mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ assertFalse(mSplitController.isActivityEmbedded(mActivity));
+
+ doReturn(true).when(mActivityWindowInfo).isEmbedded();
+
+ assertTrue(mSplitController.isActivityEmbedded(mActivity));
+ }
+
+ @Test
+ public void testGetEmbeddedActivityWindowInfo() {
+ mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ final boolean isEmbedded = true;
+ final Rect activityBounds = mActivity.getResources().getConfiguration().windowConfiguration
+ .getBounds();
+ final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+ final Rect activityStackBounds = new Rect(0, 0, 500, 2000);
+ doReturn(isEmbedded).when(mActivityWindowInfo).isEmbedded();
+ doReturn(taskBounds).when(mActivityWindowInfo).getTaskBounds();
+ doReturn(activityStackBounds).when(mActivityWindowInfo).getTaskFragmentBounds();
+
+ final EmbeddedActivityWindowInfo expected = new EmbeddedActivityWindowInfo(mActivity,
+ isEmbedded, activityBounds, taskBounds, activityStackBounds);
+ assertEquals(expected, mSplitController.getEmbeddedActivityWindowInfo(mActivity));
+ }
+
+ @Test
+ public void testSetEmbeddedActivityWindowInfoCallback() {
+ mSetFlagRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
+
+ final ClientTransactionListenerController controller = ClientTransactionListenerController
+ .getInstance();
+ spyOn(controller);
+ doNothing().when(controller).registerActivityWindowInfoChangedListener(any());
+ doReturn(mActivityWindowInfoListener).when(mSplitController)
+ .getActivityWindowInfoListener();
+ final Executor executor = Runnable::run;
+
+ // Register to ClientTransactionListenerController
+ mSplitController.setEmbeddedActivityWindowInfoCallback(executor,
+ mEmbeddedActivityWindowInfoCallback);
+
+ verify(controller).registerActivityWindowInfoChangedListener(mActivityWindowInfoListener);
+ verify(mEmbeddedActivityWindowInfoCallback, never()).accept(any());
+
+ // Test onActivityWindowInfoChanged triggered.
+ mSplitController.onActivityWindowInfoChanged(mActivity.getActivityToken(),
+ mActivityWindowInfo);
+
+ verify(mEmbeddedActivityWindowInfoCallback).accept(any());
+
+ // Unregister to ClientTransactionListenerController
+ mSplitController.clearEmbeddedActivityWindowInfoCallback();
+
+ verify(controller).unregisterActivityWindowInfoChangedListener(mActivityWindowInfoListener);
+
+ // Test onActivityWindowInfoChanged triggered as no-op after clear callback.
+ clearInvocations(mEmbeddedActivityWindowInfoCallback);
+ mSplitController.onActivityWindowInfoChanged(mActivity.getActivityToken(),
+ mActivityWindowInfo);
+
+ verify(mEmbeddedActivityWindowInfoCallback, never()).accept(any());
+ }
+
/** Creates a mock activity in the organizer process. */
private Activity createMockActivity() {
return createMockActivity(TASK_ID);
@@ -1537,13 +1623,17 @@
/** Creates a mock activity in the organizer process. */
private Activity createMockActivity(int taskId) {
final Activity activity = mock(Activity.class);
+ final ActivityThread.ActivityClientRecord activityClientRecord =
+ mock(ActivityThread.ActivityClientRecord.class);
doReturn(mActivityResources).when(activity).getResources();
final IBinder activityToken = new Binder();
doReturn(activityToken).when(activity).getActivityToken();
doReturn(activity).when(mSplitController).getActivity(activityToken);
+ doReturn(activityClientRecord).when(mSplitController).getActivityClientRecord(activity);
doReturn(taskId).when(activity).getTaskId();
doReturn(new ActivityInfo()).when(activity).getActivityInfo();
doReturn(DEFAULT_DISPLAY).when(activity).getDisplayId();
+ doReturn(mActivityWindowInfo).when(activityClientRecord).getActivityWindowInfo();
return activity;
}
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 8baaf2f..a541c59 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -145,4 +145,7 @@
<!-- Whether CompatUIController is enabled -->
<bool name="config_enableCompatUIController">true</bool>
+
+ <!-- Whether pointer pilfer is required to start back animation. -->
+ <bool name="config_backAnimationRequiresPointerPilfer">true</bool>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 74967ef0..7dd3961 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -510,10 +510,6 @@
split select if dragged until the touch input is within the range. -->
<dimen name="desktop_mode_transition_area_width">32dp</dimen>
- <!-- The height of the area at the top of the screen where a freeform task will transition to
- fullscreen if dragged until the top bound of the task is within the area. -->
- <dimen name="desktop_mode_transition_area_height">16dp</dimen>
-
<!-- The width of the area where a desktop task will transition to fullscreen. -->
<dimen name="desktop_mode_fullscreen_from_desktop_width">80dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 2606fb6..9bd8531 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -64,6 +64,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.LatencyTracker;
import com.android.internal.view.AppearanceRegion;
+import com.android.wm.shell.R;
import com.android.wm.shell.animation.FlingAnimationUtils;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
@@ -115,6 +116,7 @@
private boolean mShouldStartOnNextMoveEvent = false;
private boolean mOnBackStartDispatched = false;
private boolean mPointerPilfered = false;
+ private final boolean mRequirePointerPilfer;
private final FlingAnimationUtils mFlingAnimationUtils;
@@ -220,6 +222,8 @@
mActivityTaskManager = activityTaskManager;
mContext = context;
mContentResolver = contentResolver;
+ mRequirePointerPilfer =
+ context.getResources().getBoolean(R.bool.config_backAnimationRequiresPointerPilfer);
mBgHandler = bgHandler;
shellInit.addInitCallback(this::onInit, this);
mAnimationBackground = backAnimationBackground;
@@ -560,7 +564,9 @@
private void tryDispatchOnBackStarted(
IOnBackInvokedCallback callback,
BackMotionEvent backEvent) {
- if (mOnBackStartDispatched || callback == null || !mPointerPilfered) {
+ if (mOnBackStartDispatched
+ || callback == null
+ || (!mPointerPilfered && mRequirePointerPilfer)) {
return;
}
dispatchOnBackStarted(callback, backEvent);
@@ -1006,6 +1012,8 @@
pw.println(prefix + " mBackGestureStarted=" + mBackGestureStarted);
pw.println(prefix + " mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress);
pw.println(prefix + " mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent);
+ pw.println(prefix + " mPointerPilfered=" + mPointerPilfered);
+ pw.println(prefix + " mRequirePointerPilfer=" + mRequirePointerPilfer);
pw.println(prefix + " mCurrentTracker state:");
mCurrentTracker.dump(pw, prefix + " ");
pw.println(prefix + " mQueuedTracker state:");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 7091c4b..fb0ed15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -98,6 +98,7 @@
* Based on the coordinates of the current drag event, determine which indicator type we should
* display, including no visible indicator.
*/
+ @NonNull
IndicatorType updateIndicatorType(PointF inputCoordinates, int windowingMode) {
final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
// If we are in freeform, we don't want a visible indicator in the "freeform" drag zone.
@@ -136,18 +137,18 @@
Region calculateFullscreenRegion(DisplayLayout layout,
@WindowConfiguration.WindowingMode int windowingMode, int captionHeight) {
final Region region = new Region();
- int edgeTransitionHeight = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_transition_area_height);
+ int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM
+ ? 2 * layout.stableInsets().top
+ : mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height);
// A thin, short Rect at the top of the screen.
if (windowingMode == WINDOWING_MODE_FREEFORM) {
int fromFreeformWidth = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_width);
- int fromFreeformHeight = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height);
region.union(new Rect((layout.width() / 2) - (fromFreeformWidth / 2),
-captionHeight,
(layout.width() / 2) + (fromFreeformWidth / 2),
- fromFreeformHeight));
+ transitionHeight));
}
// A screen-wide, shorter Rect if the task is in fullscreen or split.
if (windowingMode == WINDOWING_MODE_FULLSCREEN
@@ -155,7 +156,7 @@
region.union(new Rect(0,
-captionHeight,
layout.width(),
- edgeTransitionHeight));
+ transitionHeight));
}
return region;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index d1328ca..654409f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -140,7 +140,7 @@
private val transitionAreaHeight
get() = context.resources.getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_transition_area_height
+ com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height
)
private val transitionAreaWidth
@@ -565,30 +565,7 @@
* @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to.
*/
fun snapToHalfScreen(taskInfo: RunningTaskInfo, position: SnapPosition) {
- val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
-
- val stableBounds = Rect()
- displayLayout.getStableBounds(stableBounds)
-
- val destinationWidth = stableBounds.width() / 2
- val destinationBounds = when (position) {
- SnapPosition.LEFT -> {
- Rect(
- stableBounds.left,
- stableBounds.top,
- stableBounds.left + destinationWidth,
- stableBounds.bottom
- )
- }
- SnapPosition.RIGHT -> {
- Rect(
- stableBounds.right - destinationWidth,
- stableBounds.top,
- stableBounds.right,
- stableBounds.bottom
- )
- }
- }
+ val destinationBounds = getSnapBounds(taskInfo, position)
if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return
@@ -609,8 +586,35 @@
outBounds.set(0, 0, desiredWidth, desiredHeight)
// Center the task in screen bounds
outBounds.offset(
- screenBounds.centerX() - outBounds.centerX(),
- screenBounds.centerY() - outBounds.centerY())
+ screenBounds.centerX() - outBounds.centerX(),
+ screenBounds.centerY() - outBounds.centerY())
+ }
+
+ private fun getSnapBounds(taskInfo: RunningTaskInfo, position: SnapPosition): Rect {
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return Rect()
+
+ val stableBounds = Rect()
+ displayLayout.getStableBounds(stableBounds)
+
+ val destinationWidth = stableBounds.width() / 2
+ return when (position) {
+ SnapPosition.LEFT -> {
+ Rect(
+ stableBounds.left,
+ stableBounds.top,
+ stableBounds.left + destinationWidth,
+ stableBounds.bottom
+ )
+ }
+ SnapPosition.RIGHT -> {
+ Rect(
+ stableBounds.right - destinationWidth,
+ stableBounds.top,
+ stableBounds.right,
+ stableBounds.bottom
+ )
+ }
+ }
}
/**
@@ -646,7 +650,7 @@
?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) }
}
- private fun releaseVisualIndicator() {
+ fun releaseVisualIndicator() {
val t = SurfaceControl.Transaction()
visualIndicator?.releaseVisualIndicator(t)
visualIndicator = null
@@ -927,16 +931,13 @@
taskSurface: SurfaceControl,
inputX: Float,
taskTop: Float
- ) {
+ ): DesktopModeVisualIndicator.IndicatorType {
// If the visual indicator does not exist, create it.
- if (visualIndicator == null) {
- visualIndicator = DesktopModeVisualIndicator(
- syncQueue, taskInfo, displayController, context, taskSurface,
- rootTaskDisplayAreaOrganizer)
- }
- // Then, update the indicator type.
- val indicator = visualIndicator ?: return
- indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode)
+ val indicator = visualIndicator ?: DesktopModeVisualIndicator(
+ syncQueue, taskInfo, displayController, context, taskSurface,
+ rootTaskDisplayAreaOrganizer)
+ if (visualIndicator == null) visualIndicator = indicator
+ return indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode)
}
/**
@@ -956,20 +957,28 @@
if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) {
return
}
- if (taskBounds.top <= transitionAreaHeight) {
- moveToFullscreenWithAnimation(taskInfo, position)
- return
- }
- if (inputCoordinate.x <= transitionAreaWidth) {
- releaseVisualIndicator()
- snapToHalfScreen(taskInfo, SnapPosition.LEFT)
- return
- }
- if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width()
- ?.minus(transitionAreaWidth) ?: return)) {
- releaseVisualIndicator()
- snapToHalfScreen(taskInfo, SnapPosition.RIGHT)
- return
+
+ val indicator = visualIndicator ?: return
+ val indicatorType = indicator.updateIndicatorType(
+ PointF(inputCoordinate.x, taskBounds.top.toFloat()),
+ taskInfo.windowingMode
+ )
+ when (indicatorType) {
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
+ moveToFullscreenWithAnimation(taskInfo, position)
+ }
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
+ releaseVisualIndicator()
+ snapToHalfScreen(taskInfo, SnapPosition.LEFT)
+ }
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
+ releaseVisualIndicator()
+ snapToHalfScreen(taskInfo, SnapPosition.RIGHT)
+ }
+ DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR -> {
+ releaseVisualIndicator()
+ }
}
// A freeform drag-move ended, remove the indicator immediately.
releaseVisualIndicator()
@@ -982,14 +991,28 @@
* @param y height of drag, to be checked against status bar height.
*/
fun onDragPositioningEndThroughStatusBar(
+ inputCoordinates: PointF,
taskInfo: RunningTaskInfo,
freeformBounds: Rect
) {
- finalizeDragToDesktop(taskInfo, freeformBounds)
- }
-
- private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int {
- return displayController.getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0
+ val indicator = visualIndicator ?: return
+ val indicatorType = indicator
+ .updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
+ when (indicatorType) {
+ DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> {
+ finalizeDragToDesktop(taskInfo, freeformBounds)
+ }
+ DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
+ cancelDragToDesktop(taskInfo)
+ }
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
+ finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.LEFT))
+ }
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
+ finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.RIGHT))
+ }
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 2cdec81..4d47ca9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -122,6 +122,8 @@
private static final long PIP_KEEP_CLEAR_AREAS_DELAY =
SystemProperties.getLong("persist.wm.debug.pip_keep_clear_areas_delay", 200);
+ private static final long ENABLE_TOUCH_DELAY_MS = 200L;
+
private Context mContext;
protected ShellExecutor mMainExecutor;
private DisplayController mDisplayController;
@@ -152,6 +154,8 @@
private final Runnable mMovePipInResponseToKeepClearAreasChangeCallback =
this::onKeepClearAreasChangedCallback;
+ private final Runnable mEnableTouchCallback = () -> mTouchHandler.setTouchEnabled(true);
+
private void onKeepClearAreasChangedCallback() {
if (mIsKeyguardShowingOrAnimating) {
// early bail out if the change was caused by keyguard showing up
@@ -1037,6 +1041,7 @@
saveReentryState(pipBounds);
}
// Disable touches while the animation is running
+ mMainExecutor.removeCallbacks(mEnableTouchCallback);
mTouchHandler.setTouchEnabled(false);
if (mPinnedStackAnimationRecentsCallback != null) {
mPinnedStackAnimationRecentsCallback.onPipAnimationStarted();
@@ -1067,7 +1072,7 @@
InteractionJankMonitor.getInstance().end(CUJ_PIP_TRANSITION);
// Re-enable touches after the animation completes
- mTouchHandler.setTouchEnabled(true);
+ mMainExecutor.executeDelayed(mEnableTouchCallback, ENABLE_TOUCH_DELAY_MS);
mTouchHandler.onPinnedStackAnimationEnded(direction);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index fa3d8a6..f4ccd68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -30,6 +30,10 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR;
+import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR;
import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION;
import static com.android.wm.shell.windowdecor.MoveToDesktopAnimator.DRAG_FREEFORM_SCALE;
@@ -81,6 +85,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
@@ -119,9 +124,8 @@
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
- private final Optional<DesktopTasksController> mDesktopTasksController;
+ private final DesktopTasksController mDesktopTasksController;
private final InputManager mInputManager;
-
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -129,8 +133,7 @@
private final ExclusionRegionListener mExclusionRegionListener =
new ExclusionRegionListenerImpl();
- private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId =
- new SparseArray<>();
+ private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId;
private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
private final InputMonitorFactory mInputMonitorFactory;
private TaskOperations mTaskOperations;
@@ -197,7 +200,8 @@
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
SurfaceControl.Transaction::new,
- rootTaskDisplayAreaOrganizer);
+ rootTaskDisplayAreaOrganizer,
+ new SparseArray<>());
}
@VisibleForTesting
@@ -219,7 +223,8 @@
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -231,7 +236,7 @@
mDisplayInsetsController = displayInsetsController;
mSyncQueue = syncQueue;
mTransitions = transitions;
- mDesktopTasksController = desktopTasksController;
+ mDesktopTasksController = desktopTasksController.get();
mShellCommandHandler = shellCommandHandler;
mWindowManager = windowManager;
mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
@@ -239,6 +244,7 @@
mTransactionFactory = transactionFactory;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mInputManager = mContext.getSystemService(InputManager.class);
+ mWindowDecorByTaskId = windowDecorByTaskId;
shellInit.addInitCallback(this::onInit, this);
}
@@ -248,8 +254,8 @@
mShellCommandHandler.addDumpCallback(this::dump, this);
mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(),
new DesktopModeOnInsetsChangedListener());
- mDesktopTasksController.ifPresent(c -> c.setOnTaskResizeAnimationListener(
- new DeskopModeOnTaskResizeAnimationListener()));
+ mDesktopTasksController.setOnTaskResizeAnimationListener(
+ new DeskopModeOnTaskResizeAnimationListener());
try {
mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener,
mContext.getDisplayId());
@@ -273,7 +279,7 @@
DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId);
if (decor != null && DesktopModeStatus.isEnabled()
&& decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- mDesktopTasksController.ifPresent(c -> c.moveToSplit(decor.mTaskInfo));
+ mDesktopTasksController.moveToSplit(decor.mTaskInfo);
}
}
}
@@ -340,8 +346,7 @@
@Override
public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
- final DesktopModeWindowDecoration decoration =
- mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (decoration == null) return;
decoration.close();
@@ -349,6 +354,10 @@
if (mEventReceiversByDisplay.contains(displayId)) {
removeTaskFromEventReceiver(displayId);
}
+ // Remove the decoration from the cache last because WindowDecoration#close could still
+ // issue CANCEL MotionEvents to touch listeners before its view host is released.
+ // See b/327664694.
+ mWindowDecorByTaskId.remove(taskInfo.taskId);
}
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
@@ -414,13 +423,11 @@
decoration.closeHandleMenu();
}
} else if (id == R.id.desktop_button) {
- if (mDesktopTasksController.isPresent()) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- // App sometimes draws before the insets from WindowDecoration#relayout have
- // been added, so they must be added here
- mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
- mDesktopTasksController.get().moveToDesktop(mTaskId, wct);
- }
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ // App sometimes draws before the insets from WindowDecoration#relayout have
+ // been added, so they must be added here
+ mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
+ mDesktopTasksController.moveToDesktop(mTaskId, wct);
decoration.closeHandleMenu();
} else if (id == R.id.fullscreen_button) {
decoration.closeHandleMenu();
@@ -428,42 +435,37 @@
mSplitScreenController.moveTaskToFullscreen(mTaskId,
SplitScreenController.EXIT_REASON_DESKTOP_MODE);
} else {
- mDesktopTasksController.ifPresent(c ->
- c.moveToFullscreen(mTaskId));
+ mDesktopTasksController.moveToFullscreen(mTaskId);
}
} else if (id == R.id.split_screen_button) {
decoration.closeHandleMenu();
- mDesktopTasksController.ifPresent(c -> {
- c.requestSplit(decoration.mTaskInfo);
- });
+ mDesktopTasksController.requestSplit(decoration.mTaskInfo);
} else if (id == R.id.collapse_menu_button) {
decoration.closeHandleMenu();
} else if (id == R.id.select_button) {
if (DesktopModeStatus.IS_DISPLAY_CHANGE_ENABLED) {
// TODO(b/278084491): dev option to enable display switching
// remove when select is implemented
- mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId));
+ mDesktopTasksController.moveToNextDisplay(mTaskId);
}
} else if (id == R.id.maximize_window) {
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
- mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo));
+ mDesktopTasksController.toggleDesktopTaskSize(taskInfo);
} else if (id == R.id.maximize_menu_maximize_button) {
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
- mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo));
+ mDesktopTasksController.toggleDesktopTaskSize(taskInfo);
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
} else if (id == R.id.maximize_menu_snap_left_button) {
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
- mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen(
- taskInfo, SnapPosition.LEFT));
+ mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.LEFT);
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
} else if (id == R.id.maximize_menu_snap_right_button) {
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
- mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen(
- taskInfo, SnapPosition.RIGHT));
+ mDesktopTasksController.snapToHalfScreen(taskInfo, SnapPosition.RIGHT);
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
}
@@ -572,7 +574,7 @@
private void moveTaskToFront(RunningTaskInfo taskInfo) {
if (!taskInfo.isFocused) {
- mDesktopTasksController.ifPresent(c -> c.moveTaskToFront(taskInfo));
+ mDesktopTasksController.moveTaskToFront(taskInfo);
}
}
@@ -616,10 +618,10 @@
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
- mDesktopTasksController.ifPresent(c -> c.onDragPositioningMove(taskInfo,
+ mDesktopTasksController.onDragPositioningMove(taskInfo,
decoration.mTaskSurface,
e.getRawX(dragPointerIdx),
- newTaskBounds));
+ newTaskBounds);
mIsDragging = true;
return true;
}
@@ -641,10 +643,9 @@
(int) (e.getRawY(dragPointerIdx) - e.getY(dragPointerIdx)));
final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
- mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo,
- position,
+ mDesktopTasksController.onDragPositioningEnd(taskInfo, position,
new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
- newTaskBounds));
+ newTaskBounds);
if (touchingButton && !mHasLongClicked) {
// We need the input event to not be consumed here to end the ripple
// effect on the touched button. We will reset drag state in the ensuing
@@ -672,10 +673,8 @@
&& action != MotionEvent.ACTION_CANCEL)) {
return false;
}
- mDesktopTasksController.ifPresent(c -> {
- final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
- c.toggleDesktopTaskSize(decoration.mTaskInfo);
- });
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo);
return true;
}
}
@@ -839,20 +838,29 @@
return;
}
if (mTransitionDragActive) {
+ final DesktopModeVisualIndicator.IndicatorType indicatorType =
+ mDesktopTasksController.updateVisualIndicator(relevantDecor.mTaskInfo,
+ relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY());
mTransitionDragActive = false;
- final int statusBarHeight = getStatusBarHeight(
- relevantDecor.mTaskInfo.displayId);
- if (ev.getRawY() > 2 * statusBarHeight) {
+ if (indicatorType == TO_DESKTOP_INDICATOR
+ || indicatorType == TO_SPLIT_LEFT_INDICATOR
+ || indicatorType == TO_SPLIT_RIGHT_INDICATOR) {
if (DesktopModeStatus.isEnabled()) {
animateToDesktop(relevantDecor, ev);
}
mMoveToDesktopAnimator = null;
return;
} else if (mMoveToDesktopAnimator != null) {
- mDesktopTasksController.ifPresent(
- c -> c.cancelDragToDesktop(relevantDecor.mTaskInfo));
+ mDesktopTasksController.onDragPositioningEndThroughStatusBar(
+ new PointF(ev.getRawX(), ev.getRawY()),
+ relevantDecor.mTaskInfo,
+ calculateFreeformBounds(ev.getDisplayId(), DRAG_FREEFORM_SCALE));
mMoveToDesktopAnimator = null;
return;
+ } else {
+ // In cases where we create an indicator but do not start the
+ // move-to-desktop animation, we need to dismiss it.
+ mDesktopTasksController.releaseVisualIndicator();
}
}
relevantDecor.checkClickEvent(ev);
@@ -864,20 +872,17 @@
return;
}
if (mTransitionDragActive) {
- mDesktopTasksController.ifPresent(
- c -> c.updateVisualIndicator(
+ final DesktopModeVisualIndicator.IndicatorType indicatorType =
+ mDesktopTasksController.updateVisualIndicator(
relevantDecor.mTaskInfo,
- relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()));
- final int statusBarHeight = getStatusBarHeight(
- relevantDecor.mTaskInfo.displayId);
- if (ev.getRawY() > statusBarHeight) {
+ relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY());
+ if (indicatorType != TO_FULLSCREEN_INDICATOR) {
if (mMoveToDesktopAnimator == null) {
mMoveToDesktopAnimator = new MoveToDesktopAnimator(
mContext, mDragToDesktopAnimationStartBounds,
relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
- mDesktopTasksController.ifPresent(
- c -> c.startDragToDesktop(relevantDecor.mTaskInfo,
- mMoveToDesktopAnimator));
+ mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo,
+ mMoveToDesktopAnimator);
}
}
if (mMoveToDesktopAnimator != null) {
@@ -923,6 +928,8 @@
* Animates a window to the center, grows to freeform size, and transitions to Desktop Mode.
* @param relevantDecor the window decor of the task to be animated
* @param ev the motion event that triggers the animation
+ * TODO(b/315527000): This animation needs to be adjusted to allow snap left/right cases.
+ * Currently fullscreen -> split snap still animates to center screen before readjusting.
*/
private void centerAndMoveToDesktopWithAnimation(DesktopModeWindowDecoration relevantDecor,
MotionEvent ev) {
@@ -946,13 +953,12 @@
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- mDesktopTasksController.ifPresent(
- c -> {
- c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo,
- calculateFreeformBounds(ev.getDisplayId(),
- DesktopTasksController
- .DESKTOP_MODE_INITIAL_BOUNDS_SCALE));
- });
+ mDesktopTasksController.onDragPositioningEndThroughStatusBar(
+ new PointF(ev.getRawX(), ev.getRawY()),
+ relevantDecor.mTaskInfo,
+ calculateFreeformBounds(ev.getDisplayId(),
+ DesktopTasksController
+ .DESKTOP_MODE_INITIAL_BOUNDS_SCALE));
}
});
animator.start();
@@ -1082,7 +1088,7 @@
final DragPositioningCallback dragPositioningCallback;
final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.desktop_mode_transition_area_height);
+ R.dimen.desktop_mode_fullscreen_from_desktop_height);
if (!DesktopModeStatus.isVeiledResizeEnabled()) {
dragPositioningCallback = new FluidResizeTaskPositioner(
mTaskOrganizer, mTransitions, windowDecoration, mDisplayController,
@@ -1181,12 +1187,12 @@
@Override
public void onExclusionRegionChanged(int taskId, Region region) {
- mDesktopTasksController.ifPresent(d -> d.onExclusionRegionChanged(taskId, region));
+ mDesktopTasksController.onExclusionRegionChanged(taskId, region);
}
@Override
public void onExclusionRegionDismissed(int taskId) {
- mDesktopTasksController.ifPresent(d -> d.removeExclusionRegionForTask(taskId));
+ mDesktopTasksController.removeExclusionRegionForTask(taskId);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index f8ce4ee..9703dce 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -56,18 +56,16 @@
context, taskSurface, taskDisplayAreaOrganizer)
whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
+ whenever(displayLayout.stableInsets()).thenReturn(STABLE_INSETS)
}
@Test
fun testFullscreenRegionCalculation() {
val transitionHeight = context.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_transition_area_height)
+ R.dimen.desktop_mode_fullscreen_from_desktop_height)
val fromFreeformWidth = mContext.resources.getDimensionPixelSize(
R.dimen.desktop_mode_fullscreen_from_desktop_width
)
- val fromFreeformHeight = mContext.resources.getDimensionPixelSize(
- R.dimen.desktop_mode_fullscreen_from_desktop_height
- )
var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
WINDOWING_MODE_FULLSCREEN, CAPTION_HEIGHT)
assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight))
@@ -77,7 +75,7 @@
DISPLAY_BOUNDS.width() / 2 - fromFreeformWidth / 2,
-50,
DISPLAY_BOUNDS.width() / 2 + fromFreeformWidth / 2,
- fromFreeformHeight))
+ 2 * STABLE_INSETS.top))
testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
WINDOWING_MODE_MULTI_WINDOW, CAPTION_HEIGHT)
assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight))
@@ -135,5 +133,12 @@
private const val TRANSITION_AREA_WIDTH = 32
private const val CAPTION_HEIGHT = 50
private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
+ private const val NAVBAR_HEIGHT = 50
+ private val STABLE_INSETS = Rect(
+ DISPLAY_BOUNDS.left,
+ DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
+ DISPLAY_BOUNDS.right,
+ DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
+ )
}
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 917fd71..9bb5482 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -29,6 +29,7 @@
import android.os.Handler
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import android.util.SparseArray
import android.view.Choreographer
import android.view.Display.DEFAULT_DISPLAY
import android.view.IWindowManager
@@ -72,6 +73,8 @@
import org.mockito.kotlin.whenever
import java.util.Optional
import java.util.function.Supplier
+import org.mockito.Mockito
+import org.mockito.kotlin.spy
/** Tests of [DesktopModeWindowDecorViewModel] */
@@ -102,6 +105,7 @@
private val transactionFactory = Supplier<SurfaceControl.Transaction> {
SurfaceControl.Transaction()
}
+ private val windowDecorByTaskIdSpy = spy(SparseArray<DesktopModeWindowDecoration>())
private lateinit var shellInit: ShellInit
private lateinit var desktopModeOnInsetsChangedListener: DesktopModeOnInsetsChangedListener
@@ -110,6 +114,7 @@
@Before
fun setUp() {
shellInit = ShellInit(mockShellExecutor)
+ windowDecorByTaskIdSpy.clear()
desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
mContext,
mockShellExecutor,
@@ -128,7 +133,8 @@
mockDesktopModeWindowDecorFactory,
mockInputMonitorFactory,
transactionFactory,
- mockRootTaskDisplayAreaOrganizer
+ mockRootTaskDisplayAreaOrganizer,
+ windowDecorByTaskIdSpy
)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -332,6 +338,19 @@
verify(decoration, times(1)).relayout(task)
}
+ @Test
+ fun testDestroyWindowDecoration_closesBeforeCleanup() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
+ val decoration = setUpMockDecorationForTask(task)
+ val inOrder = Mockito.inOrder(decoration, windowDecorByTaskIdSpy)
+
+ onTaskOpening(task)
+ desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+
+ inOrder.verify(decoration).close()
+ inOrder.verify(windowDecorByTaskIdSpy).remove(task.taskId)
+ }
+
private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
desktopModeWindowDecorViewModel.onTaskOpening(
task,
diff --git a/packages/SettingsLib/DataStore/tests/Android.bp b/packages/SettingsLib/DataStore/tests/Android.bp
new file mode 100644
index 0000000..8770dfa
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/Android.bp
@@ -0,0 +1,24 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+ name: "SettingsLibDataStoreShell",
+ platform_apis: true,
+}
+
+android_robolectric_test {
+ name: "SettingsLibDataStoreTest",
+ srcs: ["src/**/*"],
+ static_libs: [
+ "SettingsLibDataStore",
+ "androidx.test.ext.junit",
+ "guava",
+ "mockito-robolectric-prebuilt", // mockito deps order matters!
+ "mockito-kotlin2",
+ ],
+ java_resource_dirs: ["config"],
+ instrumentation_for: "SettingsLibDataStoreShell",
+ coverage_libs: ["SettingsLibDataStore"],
+ upstream: true,
+}
diff --git a/packages/SettingsLib/DataStore/tests/AndroidManifest.xml b/packages/SettingsLib/DataStore/tests/AndroidManifest.xml
new file mode 100644
index 0000000..ffc24e4
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.datastore.test">
+
+ <application android:debuggable="true" />
+</manifest>
diff --git a/packages/SettingsLib/DataStore/tests/config/robolectric.properties b/packages/SettingsLib/DataStore/tests/config/robolectric.properties
new file mode 100644
index 0000000..fab7251
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/config/robolectric.properties
@@ -0,0 +1 @@
+sdk=NEWEST_SDK
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
new file mode 100644
index 0000000..bb791dc
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.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.settingslib.datastore
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicInteger
+import org.junit.Assert.assertThrows
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class ObserverTest {
+ @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var observer1: Observer
+
+ @Mock private lateinit var observer2: Observer
+
+ @Mock private lateinit var executor: Executor
+
+ private val observable = DataObservable()
+
+ @Test
+ fun addObserver_sameExecutor() {
+ observable.addObserver(observer1, executor)
+ observable.addObserver(observer1, executor)
+ }
+
+ @Test
+ fun addObserver_differentExecutor() {
+ observable.addObserver(observer1, executor)
+ assertThrows(IllegalStateException::class.java) {
+ observable.addObserver(observer1, MoreExecutors.directExecutor())
+ }
+ }
+
+ @Test
+ fun addObserver_weaklyReferenced() {
+ val counter = AtomicInteger()
+ var observer: Observer? = Observer { counter.incrementAndGet() }
+ observable.addObserver(observer!!, MoreExecutors.directExecutor())
+
+ observable.notifyChange(ChangeReason.UPDATE)
+ assertThat(counter.get()).isEqualTo(1)
+
+ // trigger GC, the observer callback should not be invoked
+ @Suppress("unused")
+ observer = null
+ System.gc()
+ System.runFinalization()
+
+ observable.notifyChange(ChangeReason.UPDATE)
+ assertThat(counter.get()).isEqualTo(1)
+ }
+
+ @Test
+ fun addObserver_notifyObservers_removeObserver() {
+ observable.addObserver(observer1, MoreExecutors.directExecutor())
+ observable.addObserver(observer2, executor)
+
+ observable.notifyChange(ChangeReason.DELETE)
+
+ verify(observer1).onChanged(ChangeReason.DELETE)
+ verify(observer2, never()).onChanged(any())
+ verify(executor).execute(any())
+
+ reset(observer1, executor)
+ observable.removeObserver(observer2)
+
+ observable.notifyChange(ChangeReason.UPDATE)
+ verify(observer1).onChanged(ChangeReason.UPDATE)
+ verify(executor, never()).execute(any())
+ }
+
+ @Test
+ fun notifyChange_addObserverWithinCallback() {
+ // ConcurrentModificationException is raised if it is not implemented correctly
+ observable.addObserver(
+ { observable.addObserver(observer1, executor) },
+ MoreExecutors.directExecutor()
+ )
+ observable.notifyChange(ChangeReason.UPDATE)
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index bdb5871..5f026c4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -74,6 +74,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -84,6 +85,7 @@
private static final String TAG = "InfoMediaManager";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ protected final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
/** Checked exception that signals the specified package is not present in the system. */
public static class PackageNotAvailableException extends Exception {
@@ -227,6 +229,16 @@
Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference, mPreferenceItemMap);
}
+ protected final MediaDevice findMediaDevice(@NonNull String id) {
+ for (MediaDevice mediaDevice : mMediaDevices) {
+ if (mediaDevice.getId().equals(id)) {
+ return mediaDevice;
+ }
+ }
+ Log.e(TAG, "findMediaDevice() can't find device with id: " + id);
+ return null;
+ }
+
/**
* Get current device that played media.
* @return MediaDevice
@@ -433,7 +445,7 @@
protected final synchronized void refreshDevices() {
rebuildDeviceList();
- dispatchDeviceListAdded();
+ dispatchDeviceListAdded(mMediaDevices);
}
// MediaRoute2Info.getType was made public on API 34, but exists since API 30.
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
index 8bebd6e..d562c8a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
@@ -15,9 +15,9 @@
*/
package com.android.settingslib.media;
+import android.annotation.NonNull;
import android.app.Notification;
import android.content.Context;
-import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
@@ -29,10 +29,7 @@
*/
public abstract class MediaManager {
- private static final String TAG = "MediaManager";
-
protected final Collection<MediaDeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
- protected final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
protected Context mContext;
protected Notification mNotification;
@@ -54,19 +51,9 @@
}
}
- protected MediaDevice findMediaDevice(String id) {
- for (MediaDevice mediaDevice : mMediaDevices) {
- if (mediaDevice.getId().equals(id)) {
- return mediaDevice;
- }
- }
- Log.e(TAG, "findMediaDevice() can't found device");
- return null;
- }
-
- protected void dispatchDeviceListAdded() {
+ protected void dispatchDeviceListAdded(@NonNull List<MediaDevice> devices) {
for (MediaDeviceCallback callback : getCallbacks()) {
- callback.onDeviceListAdded(new ArrayList<>(mMediaDevices));
+ callback.onDeviceListAdded(new ArrayList<>(devices));
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
index 65b73ca..0117ece 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
@@ -46,7 +46,7 @@
/**
* Wrapper the [.getInternetIconResource] for testing compatibility.
*/
- class InternetIconInjector(protected val context: Context) {
+ open class InternetIconInjector(protected val context: Context) {
/**
* Returns the Internet icon for a given RSSI level.
*
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java
index 46e724d..c3237f0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaManagerTest.java
@@ -16,7 +16,6 @@
package com.android.settingslib.media;
-import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
@@ -32,6 +31,8 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.util.Collections;
+
@RunWith(RobolectricTestRunner.class)
public class MediaManagerTest {
@@ -59,7 +60,7 @@
public void dispatchDeviceListAdded_registerCallback_shouldDispatchCallback() {
mMediaManager.registerCallback(mCallback);
- mMediaManager.dispatchDeviceListAdded();
+ mMediaManager.dispatchDeviceListAdded(Collections.emptyList());
verify(mCallback).onDeviceListAdded(any());
}
@@ -68,9 +69,9 @@
public void dispatchDeviceListRemoved_registerCallback_shouldDispatchCallback() {
mMediaManager.registerCallback(mCallback);
- mMediaManager.dispatchDeviceListRemoved(mMediaManager.mMediaDevices);
+ mMediaManager.dispatchDeviceListRemoved(Collections.emptyList());
- verify(mCallback).onDeviceListRemoved(mMediaManager.mMediaDevices);
+ verify(mCallback).onDeviceListRemoved(Collections.emptyList());
}
@Test
@@ -83,24 +84,6 @@
}
@Test
- public void findMediaDevice_idExist_shouldReturnMediaDevice() {
- mMediaManager.mMediaDevices.add(mDevice);
-
- final MediaDevice device = mMediaManager.findMediaDevice(TEST_ID);
-
- assertThat(device.getId()).isEqualTo(mDevice.getId());
- }
-
- @Test
- public void findMediaDevice_idNotExist_shouldReturnNull() {
- mMediaManager.mMediaDevices.add(mDevice);
-
- final MediaDevice device = mMediaManager.findMediaDevice("123");
-
- assertThat(device).isNull();
- }
-
- @Test
public void dispatchOnRequestFailed_registerCallback_shouldDispatchCallback() {
mMediaManager.registerCallback(mCallback);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 2fa1c6e..a490b6f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -154,6 +154,7 @@
Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT,
Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
Settings.Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED,
+ Settings.Secure.EMERGENCY_THERMAL_ALERT_DISABLED,
Settings.Secure.HUSH_GESTURE_USED,
Settings.Secure.IN_CALL_NOTIFICATION_ENABLED,
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 2535fdb..4cdf98cb 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -235,6 +235,7 @@
VALIDATORS.put(Secure.MANUAL_RINGER_TOGGLE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.LOW_POWER_WARNING_ACKNOWLEDGED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.EMERGENCY_THERMAL_ALERT_DISABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.IN_CALL_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index 085fc29..88181e7 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -56,10 +56,12 @@
implements View.OnTouchListener {
public static final String PACKAGE_NAME = AccessibilityMenuService.class.getPackageName();
+ public static final String PACKAGE_TESTS = ".tests";
public static final String INTENT_TOGGLE_MENU = ".toggle_menu";
public static final String INTENT_HIDE_MENU = ".hide_menu";
public static final String INTENT_GLOBAL_ACTION = ".global_action";
public static final String INTENT_GLOBAL_ACTION_EXTRA = "GLOBAL_ACTION";
+ public static final String INTENT_OPEN_BLOCKED = "OPEN_BLOCKED";
private static final String TAG = "A11yMenuService";
private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L;
@@ -192,7 +194,7 @@
IntentFilter hideMenuFilter = new IntentFilter();
hideMenuFilter.addAction(Intent.ACTION_SCREEN_OFF);
- hideMenuFilter.addAction(PACKAGE_NAME + INTENT_HIDE_MENU);
+ hideMenuFilter.addAction(INTENT_HIDE_MENU);
// Including WRITE_SECURE_SETTINGS enforces that we only listen to apps
// with the restricted WRITE_SECURE_SETTINGS permission who broadcast this intent.
@@ -200,7 +202,7 @@
Manifest.permission.WRITE_SECURE_SETTINGS, null,
Context.RECEIVER_EXPORTED);
registerReceiver(mToggleMenuReceiver,
- new IntentFilter(PACKAGE_NAME + INTENT_TOGGLE_MENU),
+ new IntentFilter(INTENT_TOGGLE_MENU),
Manifest.permission.WRITE_SECURE_SETTINGS, null,
Context.RECEIVER_EXPORTED);
@@ -245,8 +247,9 @@
* @return {@code true} if successful, {@code false} otherwise.
*/
private boolean performGlobalActionInternal(int globalAction) {
- Intent intent = new Intent(PACKAGE_NAME + INTENT_GLOBAL_ACTION);
+ Intent intent = new Intent(INTENT_GLOBAL_ACTION);
intent.putExtra(INTENT_GLOBAL_ACTION_EXTRA, globalAction);
+ intent.setPackage(PACKAGE_NAME + PACKAGE_TESTS);
sendBroadcast(intent);
Log.i("A11yMenuService", "Broadcasting global action " + globalAction);
return performGlobalAction(globalAction);
@@ -410,9 +413,16 @@
private void toggleVisibility() {
boolean locked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
- if (!locked && SystemClock.uptimeMillis() - mLastTimeTouchedOutside
- > BUTTON_CLICK_TIMEOUT) {
- mA11yMenuLayout.toggleVisibility();
+ if (!locked) {
+ if (SystemClock.uptimeMillis() - mLastTimeTouchedOutside
+ > BUTTON_CLICK_TIMEOUT) {
+ mA11yMenuLayout.toggleVisibility();
+ }
+ } else {
+ // Broadcast for testing.
+ Intent intent = new Intent(INTENT_OPEN_BLOCKED);
+ intent.setPackage(PACKAGE_NAME + PACKAGE_TESTS);
+ sendBroadcast(intent);
}
}
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
index 72c1092..6546b87 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -23,6 +23,7 @@
import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_RECENTS;
import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT;
+import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_OPEN_BLOCKED;
import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_GLOBAL_ACTION;
import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_GLOBAL_ACTION_EXTRA;
import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_HIDE_MENU;
@@ -65,6 +66,7 @@
import org.junit.runner.RunWith;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@RunWith(AndroidJUnit4.class)
@@ -75,12 +77,11 @@
private static final int TIMEOUT_SERVICE_STATUS_CHANGE_S = 5;
private static final int TIMEOUT_UI_CHANGE_S = 5;
private static final int NO_GLOBAL_ACTION = -1;
- private static final String INPUT_KEYEVENT_KEYCODE_BACK = "input keyevent KEYCODE_BACK";
- private static final String TEST_PIN = "1234";
private static Instrumentation sInstrumentation;
private static UiAutomation sUiAutomation;
- private static AtomicInteger sLastGlobalAction;
+ private static final AtomicInteger sLastGlobalAction = new AtomicInteger(NO_GLOBAL_ACTION);
+ private static final AtomicBoolean sOpenBlocked = new AtomicBoolean(false);
private static AccessibilityManager sAccessibilityManager;
private static PowerManager sPowerManager;
@@ -122,8 +123,6 @@
() -> sAccessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK).stream().filter(
info -> info.getId().contains(serviceName)).count() == 1);
-
- sLastGlobalAction = new AtomicInteger(NO_GLOBAL_ACTION);
context.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -131,20 +130,28 @@
sLastGlobalAction.set(
intent.getIntExtra(INTENT_GLOBAL_ACTION_EXTRA, NO_GLOBAL_ACTION));
}},
- new IntentFilter(PACKAGE_NAME + INTENT_GLOBAL_ACTION),
+ new IntentFilter(INTENT_GLOBAL_ACTION),
+ null, null, Context.RECEIVER_EXPORTED);
+
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Received notification that menu cannot be opened.");
+ sOpenBlocked.set(true);
+ }},
+ new IntentFilter(INTENT_OPEN_BLOCKED),
null, null, Context.RECEIVER_EXPORTED);
}
@AfterClass
- public static void classTeardown() throws Throwable {
- clearPin();
+ public static void classTeardown() {
Settings.Secure.putString(sInstrumentation.getTargetContext().getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
}
@Before
public void setup() throws Throwable {
- clearPin();
+ sOpenBlocked.set(false);
wakeUpScreen();
sUiAutomation.executeShellCommand("input keyevent KEYCODE_MENU");
openMenu();
@@ -154,20 +161,8 @@
public void tearDown() throws Throwable {
closeMenu();
sLastGlobalAction.set(NO_GLOBAL_ACTION);
- }
-
- private static void clearPin() throws Throwable {
- sUiAutomation.executeShellCommand("locksettings clear --old " + TEST_PIN);
- TestUtils.waitUntil("Device did not register as unlocked & insecure.",
- TIMEOUT_SERVICE_STATUS_CHANGE_S,
- () -> !sKeyguardManager.isDeviceSecure());
- }
-
- private static void setPin() throws Throwable {
- sUiAutomation.executeShellCommand("locksettings set-pin " + TEST_PIN);
- TestUtils.waitUntil("Device did not recognize as locked & secure.",
- TIMEOUT_SERVICE_STATUS_CHANGE_S,
- () -> sKeyguardManager.isDeviceSecure());
+ // dismisses screenshot popup if present.
+ sUiAutomation.executeShellCommand("input keyevent KEYCODE_BACK");
}
private static boolean isMenuVisible() {
@@ -184,7 +179,6 @@
private static void closeScreen() throws Throwable {
Display display = sDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
- setPin();
sUiAutomation.performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN);
TestUtils.waitUntil("Screen did not close.",
TIMEOUT_UI_CHANGE_S,
@@ -194,12 +188,20 @@
}
private static void openMenu() throws Throwable {
- Intent intent = new Intent(PACKAGE_NAME + INTENT_TOGGLE_MENU);
+ openMenu(false);
+ }
+
+ private static void openMenu(boolean abandonOnBlock) throws Throwable {
+ Intent intent = new Intent(INTENT_TOGGLE_MENU);
+ intent.setPackage(PACKAGE_NAME);
sInstrumentation.getContext().sendBroadcast(intent);
TestUtils.waitUntil("Timed out before menu could appear.",
TIMEOUT_UI_CHANGE_S,
() -> {
+ if (sOpenBlocked.get() && abandonOnBlock) {
+ throw new IllegalStateException();
+ }
if (isMenuVisible()) {
return true;
} else {
@@ -213,7 +215,8 @@
if (!isMenuVisible()) {
return;
}
- Intent intent = new Intent(PACKAGE_NAME + INTENT_HIDE_MENU);
+ Intent intent = new Intent(INTENT_HIDE_MENU);
+ intent.setPackage(PACKAGE_NAME);
sInstrumentation.getContext().sendBroadcast(intent);
TestUtils.waitUntil("Timed out before menu could close.",
TIMEOUT_UI_CHANGE_S, () -> !isMenuVisible());
@@ -444,13 +447,13 @@
closeScreen();
wakeUpScreen();
- boolean timedOut = false;
+ boolean blocked = false;
try {
- openMenu();
- } catch (AssertionError e) {
+ openMenu(true);
+ } catch (IllegalStateException e) {
// Expected
- timedOut = true;
+ blocked = true;
}
- assertThat(timedOut).isTrue();
+ assertThat(blocked).isTrue();
}
}
diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig
index d0e6b28..7bbe82c 100644
--- a/packages/SystemUI/aconfig/predictive_back.aconfig
+++ b/packages/SystemUI/aconfig/predictive_back.aconfig
@@ -4,26 +4,26 @@
name: "predictive_back_sysui"
namespace: "systemui"
description: "Predictive Back Dispatching for SysUI"
- bug: "309545085"
+ bug: "327737297"
}
flag {
name: "predictive_back_animate_shade"
namespace: "systemui"
description: "Enable Shade Animations"
- bug: "309545085"
+ bug: "327732946"
}
flag {
name: "predictive_back_animate_bouncer"
namespace: "systemui"
description: "Enable Predictive Back Animation in Bouncer"
- bug: "309545085"
+ bug: "327733487"
}
flag {
name: "predictive_back_animate_dialogs"
namespace: "systemui"
description: "Enable Predictive Back Animation for SysUI dialogs"
- bug: "309545085"
+ bug: "327721544"
}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 078da1c86..515c816 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -31,8 +31,8 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -93,6 +93,7 @@
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
@@ -118,6 +119,7 @@
import com.android.systemui.communal.ui.compose.extensions.observeTapsWithoutConsuming
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
+import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.res.R
import kotlinx.coroutines.launch
@@ -197,26 +199,37 @@
}
},
) {
- CommunalHubLazyGrid(
- communalContent = communalContent,
- viewModel = viewModel,
- contentPadding = contentPadding,
- contentOffset = contentOffset,
- setGridCoordinates = { gridCoordinates = it },
- updateDragPositionForRemove = { offset ->
- isDraggingToRemove =
- isPointerWithinCoordinates(
- offset = gridCoordinates?.let { it.positionInWindow() + offset },
- containerToCheck = removeButtonCoordinates
- )
- isDraggingToRemove
- },
- onOpenWidgetPicker = onOpenWidgetPicker,
- gridState = gridState,
- contentListState = contentListState,
- selectedKey = selectedKey,
- widgetConfigurator = widgetConfigurator,
- )
+ Column(Modifier.align(Alignment.TopStart)) {
+ CommunalHubLazyGrid(
+ communalContent = communalContent,
+ viewModel = viewModel,
+ contentPadding = contentPadding,
+ contentOffset = contentOffset,
+ setGridCoordinates = { gridCoordinates = it },
+ updateDragPositionForRemove = { offset ->
+ isDraggingToRemove =
+ isPointerWithinCoordinates(
+ offset = gridCoordinates?.let { it.positionInWindow() + offset },
+ containerToCheck = removeButtonCoordinates
+ )
+ isDraggingToRemove
+ },
+ onOpenWidgetPicker = onOpenWidgetPicker,
+ gridState = gridState,
+ contentListState = contentListState,
+ selectedKey = selectedKey,
+ widgetConfigurator = widgetConfigurator,
+ )
+ // TODO(b/326060686): Remove this once keyguard indication area can persist over hub
+ if (viewModel is CommunalViewModel) {
+ val isUnlocked by viewModel.deviceUnlocked.collectAsState(initial = false)
+ Spacer(Modifier.height(24.dp))
+ LockStateIcon(
+ isUnlocked = isUnlocked,
+ modifier = Modifier.align(Alignment.CenterHorizontally),
+ )
+ }
+ }
if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) {
Toolbar(
@@ -268,7 +281,7 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
-private fun BoxScope.CommunalHubLazyGrid(
+private fun ColumnScope.CommunalHubLazyGrid(
communalContent: List<CommunalContentModel>,
viewModel: BaseCommunalViewModel,
contentPadding: PaddingValues,
@@ -282,7 +295,7 @@
widgetConfigurator: WidgetConfigurator?,
) {
var gridModifier =
- Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) }
+ Modifier.align(Alignment.Start).onGloballyPositioned { setGridCoordinates(it) }
var list = communalContent
var dragDropState: GridDragDropState? = null
if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) {
@@ -364,6 +377,26 @@
}
}
+@Composable
+private fun LockStateIcon(
+ isUnlocked: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ val colors = LocalAndroidColorScheme.current
+ val resource =
+ if (isUnlocked) {
+ R.drawable.ic_unlocked
+ } else {
+ R.drawable.ic_lock
+ }
+ Icon(
+ painter = painterResource(id = resource),
+ contentDescription = null,
+ tint = colors.onPrimaryContainer,
+ modifier = modifier.size(52.dp)
+ )
+}
+
/**
* Toolbar that contains action buttons to
* 1) open the widget picker
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index 37b135e..ce6445b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -23,7 +23,6 @@
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dock.DockManager
import com.android.systemui.dock.dockManager
import com.android.systemui.dock.fakeDockManager
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -40,6 +39,7 @@
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
@@ -92,6 +92,7 @@
}
}
+ @Ignore("Ignored until custom animations are implemented in b/322787129")
@Test
fun deviceDocked_forceCommunalScene() =
with(kosmos) {
@@ -110,6 +111,23 @@
}
@Test
+ fun exitingDream_forceCommunalScene() =
+ with(kosmos) {
+ testScope.runTest {
+ val scene by collectLastValue(communalInteractor.desiredScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
+
+ updateDocked(true)
+ fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = this
+ )
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ }
+ }
+
+ @Test
fun deviceDocked_doesNotForceCommunalIfTransitioningFromCommunal() =
with(kosmos) {
testScope.runTest {
@@ -175,6 +193,7 @@
}
}
+ @Ignore("Ignored until custom animations are implemented in b/322787129")
@Test
fun dockingOnLockscreen_forcesCommunal() =
with(kosmos) {
@@ -196,6 +215,7 @@
}
}
+ @Ignore("Ignored until custom animations are implemented in b/322787129")
@Test
fun dockingOnLockscreen_doesNotForceCommunalIfDreamStarts() =
with(kosmos) {
@@ -230,7 +250,8 @@
with(kosmos) {
runCurrent()
fakeDockManager.setIsDocked(docked)
- fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
+ // TODO(b/322787129): uncomment once custom animations are in place
+ // fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
runCurrent()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 563aad1..8f802b8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -39,6 +39,7 @@
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -113,6 +114,7 @@
kosmos.communalInteractor,
kosmos.communalTutorialInteractor,
kosmos.shadeInteractor,
+ kosmos.deviceEntryInteractor,
mediaHost,
logcatLogBuffer("CommunalViewModelTest"),
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 19b80da..128b465 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.common.shared.model.Position
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback
@@ -151,6 +152,24 @@
}
@Test
+ fun clockPosition() =
+ testScope.runTest {
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 0))
+
+ underTest.setClockPosition(0, 1)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 1))
+
+ underTest.setClockPosition(1, 9)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 9))
+
+ underTest.setClockPosition(1, 0)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 0))
+
+ underTest.setClockPosition(3, 1)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(3, 1))
+ }
+
+ @Test
fun dozeTimeTick() =
testScope.runTest {
val lastDozeTimeTick by collectLastValue(underTest.dozeTimeTick)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 225b5b1..b0f59fe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -71,7 +71,7 @@
mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
MockitoAnnotations.initMocks(this)
- whenever(burnInInteractor.burnIn(anyInt(), anyInt())).thenReturn(burnInFlow)
+ whenever(burnInInteractor.keyguardBurnIn).thenReturn(burnInFlow)
kosmos.burnInInteractor = burnInInteractor
whenever(goneToAodTransitionViewModel.enterFromTopTranslationY(anyInt()))
.thenReturn(emptyFlow())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
deleted file mode 100644
index ce089b1..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2023 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.keyguard.ui.viewmodel
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.shared.model.BurnInModel
-import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class KeyguardIndicationAreaViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
-
- @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
- @Mock private lateinit var shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel
-
- @Mock private lateinit var burnInInteractor: BurnInInteractor
- private val burnInFlow = MutableStateFlow(BurnInModel())
-
- private lateinit var bottomAreaInteractor: KeyguardBottomAreaInteractor
- private lateinit var underTest: KeyguardIndicationAreaViewModel
- private lateinit var repository: FakeKeyguardRepository
-
- private val startButtonFlow =
- MutableStateFlow<KeyguardQuickAffordanceViewModel>(
- KeyguardQuickAffordanceViewModel(
- slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()
- )
- )
- private val endButtonFlow =
- MutableStateFlow<KeyguardQuickAffordanceViewModel>(
- KeyguardQuickAffordanceViewModel(
- slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId()
- )
- )
- private val alphaFlow = MutableStateFlow<Float>(1f)
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-
- whenever(burnInHelperWrapper.burnInOffset(anyInt(), any()))
- .thenReturn(RETURNED_BURN_IN_OFFSET)
- whenever(burnInInteractor.burnIn(anyInt(), anyInt())).thenReturn(burnInFlow)
-
- val withDeps = KeyguardInteractorFactory.create()
- val keyguardInteractor = withDeps.keyguardInteractor
- repository = withDeps.repository
-
- val bottomAreaViewModel: KeyguardBottomAreaViewModel = mock()
- whenever(bottomAreaViewModel.startButton).thenReturn(startButtonFlow)
- whenever(bottomAreaViewModel.endButton).thenReturn(endButtonFlow)
- whenever(bottomAreaViewModel.alpha).thenReturn(alphaFlow)
- bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository)
- underTest =
- KeyguardIndicationAreaViewModel(
- keyguardInteractor = keyguardInteractor,
- bottomAreaInteractor = bottomAreaInteractor,
- keyguardBottomAreaViewModel = bottomAreaViewModel,
- burnInHelperWrapper = burnInHelperWrapper,
- burnInInteractor = burnInInteractor,
- shortcutsCombinedViewModel = shortcutsCombinedViewModel,
- configurationInteractor = ConfigurationInteractor(FakeConfigurationRepository()),
- )
- }
-
- @Test
- fun alpha() =
- testScope.runTest {
- val value = collectLastValue(underTest.alpha)
-
- assertThat(value()).isEqualTo(1f)
- alphaFlow.value = 0.1f
- assertThat(value()).isEqualTo(0.1f)
- alphaFlow.value = 0.5f
- assertThat(value()).isEqualTo(0.5f)
- alphaFlow.value = 0.2f
- assertThat(value()).isEqualTo(0.2f)
- alphaFlow.value = 0f
- assertThat(value()).isEqualTo(0f)
- }
-
- @Test
- fun isIndicationAreaPadded() =
- testScope.runTest {
- repository.setKeyguardShowing(true)
- val value = collectLastValue(underTest.isIndicationAreaPadded)
-
- assertThat(value()).isFalse()
- startButtonFlow.value = startButtonFlow.value.copy(isVisible = true)
- assertThat(value()).isTrue()
- endButtonFlow.value = endButtonFlow.value.copy(isVisible = true)
- assertThat(value()).isTrue()
- startButtonFlow.value = startButtonFlow.value.copy(isVisible = false)
- assertThat(value()).isTrue()
- endButtonFlow.value = endButtonFlow.value.copy(isVisible = false)
- assertThat(value()).isFalse()
- }
-
- @Test
- fun indicationAreaTranslationX() =
- testScope.runTest {
- val value = collectLastValue(underTest.indicationAreaTranslationX)
-
- assertThat(value()).isEqualTo(0f)
- bottomAreaInteractor.setClockPosition(100, 100)
- assertThat(value()).isEqualTo(100f)
- bottomAreaInteractor.setClockPosition(200, 100)
- assertThat(value()).isEqualTo(200f)
- bottomAreaInteractor.setClockPosition(200, 200)
- assertThat(value()).isEqualTo(200f)
- bottomAreaInteractor.setClockPosition(300, 100)
- assertThat(value()).isEqualTo(300f)
- }
-
- @Test
- fun indicationAreaTranslationY() =
- testScope.runTest {
- val value =
- collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET))
-
- // Negative 0 - apparently there's a difference in floating point arithmetic - FML
- assertThat(value()).isEqualTo(-0f)
- val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f)
- assertThat(value()).isEqualTo(expected1)
- val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f)
- assertThat(value()).isEqualTo(expected2)
- val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f)
- assertThat(value()).isEqualTo(expected3)
- val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f)
- assertThat(value()).isEqualTo(expected4)
- }
-
- private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
- repository.setDozeAmount(dozeAmount)
- return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET)
- }
-
- companion object {
- private const val DEFAULT_BURN_IN_OFFSET = 5
- private const val RETURNED_BURN_IN_OFFSET = 3
- }
-}
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 68c8dd9..d8d2985 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -19,11 +19,11 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/half_shelf_dialog"
android:orientation="vertical"
- android:layout_width="wrap_content"
+ android:layout_width="@dimen/large_dialog_width"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
- android:paddingStart="4dp"
- android:paddingEnd="4dp">
+ android:paddingLeft="@dimen/dialog_side_padding"
+ android:paddingRight="@dimen/dialog_side_padding">
<LinearLayout
android:id="@+id/half_shelf"
diff --git a/packages/SystemUI/res/layout/scene_window_root.xml b/packages/SystemUI/res/layout/scene_window_root.xml
index bb8de4c..0dcd15b 100644
--- a/packages/SystemUI/res/layout/scene_window_root.xml
+++ b/packages/SystemUI/res/layout/scene_window_root.xml
@@ -24,7 +24,7 @@
android:id="@+id/scene_window_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="false">
+ android:fitsSystemWindows="true">
<include layout="@layout/super_notification_shade"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 035cfdc..71ae0d7 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -223,7 +223,6 @@
<item type="id" name="lock_icon" />
<item type="id" name="lock_icon_bg" />
<item type="id" name="burn_in_layer" />
- <item type="id" name="burn_in_layer_empty_view" />
<item type="id" name="communal_tutorial_indicator" />
<item type="id" name="nssl_placeholder_barrier_bottom" />
<item type="id" name="ambient_indication_container" />
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index c0ae4a1..9421f15 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -53,6 +53,9 @@
import com.android.systemui.animation.ViewHierarchyAnimator;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.TransitionState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.plugins.clocks.ClockController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.power.shared.model.ScreenPowerState;
@@ -101,6 +104,7 @@
private final Rect mClipBounds = new Rect();
private final KeyguardInteractor mKeyguardInteractor;
private final PowerInteractor mPowerInteractor;
+ private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final DozeParameters mDozeParameters;
private View mStatusArea = null;
@@ -108,6 +112,7 @@
private Boolean mSplitShadeEnabled = false;
private Boolean mStatusViewCentered = true;
+ private boolean mGoneToAodTransitionRunning = false;
private DumpManager mDumpManager;
private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener =
@@ -176,6 +181,7 @@
KeyguardLogger logger,
InteractionJankMonitor interactionJankMonitor,
KeyguardInteractor keyguardInteractor,
+ KeyguardTransitionInteractor keyguardTransitionInteractor,
DumpManager dumpManager,
PowerInteractor powerInteractor) {
super(keyguardStatusView);
@@ -191,6 +197,7 @@
mDumpManager = dumpManager;
mKeyguardInteractor = keyguardInteractor;
mPowerInteractor = powerInteractor;
+ mKeyguardTransitionInteractor = keyguardTransitionInteractor;
}
@Override
@@ -225,7 +232,6 @@
mDumpManager.registerDumpable(getInstanceName(), this);
if (migrateClocksToBlueprint()) {
startCoroutines(EmptyCoroutineContext.INSTANCE);
- mView.setVisibility(View.GONE);
}
}
@@ -241,6 +247,15 @@
dozeTimeTick();
}
}, context);
+
+ collectFlow(mView, mKeyguardTransitionInteractor.getGoneToAodTransition(),
+ (TransitionStep step) -> {
+ if (step.getTransitionState() == TransitionState.RUNNING) {
+ mGoneToAodTransitionRunning = true;
+ } else {
+ mGoneToAodTransitionRunning = false;
+ }
+ }, context);
}
public KeyguardStatusView getView() {
@@ -311,7 +326,7 @@
* Set keyguard status view alpha.
*/
public void setAlpha(float alpha) {
- if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
+ if (!mKeyguardVisibilityHelper.isVisibilityAnimating() && !mGoneToAodTransitionRunning) {
mView.setAlpha(alpha);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 77054bd..2000028 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -88,10 +88,6 @@
boolean keyguardFadingAway,
boolean goingToFullShade,
int oldStatusBarState) {
- if (migrateClocksToBlueprint()) {
- log("Ignoring all of KeyguardVisibilityelper");
- return;
- }
Assert.isMainThread();
PropertyAnimator.cancelAnimation(mView, AnimatableProperty.ALPHA);
boolean isOccluded = mKeyguardStateController.isOccluded();
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index d5cb4d5..c3c7411 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -24,18 +24,15 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dock.DockManager
-import com.android.systemui.dock.retrieveIsDocked
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
@@ -66,19 +63,21 @@
.onEach { nextScene -> communalInteractor.onSceneChanged(nextScene) }
.launchIn(applicationScope)
+ // TODO(b/322787129): re-enable once custom animations are in place
// Handle automatically switching to communal when docked.
- dockManager
- .retrieveIsDocked()
- // Allow some time after docking to ensure the dream doesn't start. If the dream
- // starts, then we don't want to automatically transition to glanceable hub.
- .debounce(DOCK_DEBOUNCE_DELAY)
- .sample(keyguardTransitionInteractor.startedKeyguardState, ::Pair)
- .onEach { (docked, lastStartedState) ->
- if (docked && lastStartedState == KeyguardState.LOCKSCREEN) {
- communalInteractor.onSceneChanged(CommunalScenes.Communal)
- }
- }
- .launchIn(bgScope)
+ // dockManager
+ // .retrieveIsDocked()
+ // // Allow some time after docking to ensure the dream doesn't start. If the
+ // dream
+ // // starts, then we don't want to automatically transition to glanceable hub.
+ // .debounce(DOCK_DEBOUNCE_DELAY)
+ // .sample(keyguardTransitionInteractor.startedKeyguardState, ::Pair)
+ // .onEach { (docked, lastStartedState) ->
+ // if (docked && lastStartedState == KeyguardState.LOCKSCREEN) {
+ // communalInteractor.onSceneChanged(CommunalScenes.Communal)
+ // }
+ // }
+ // .launchIn(bgScope)
}
private suspend fun determineSceneAfterTransition(
@@ -89,7 +88,7 @@
val docked = dockManager.isDocked
return when {
- docked && to == KeyguardState.LOCKSCREEN && from != KeyguardState.GLANCEABLE_HUB -> {
+ docked && to == KeyguardState.LOCKSCREEN && from == KeyguardState.DREAMING -> {
CommunalScenes.Communal
}
to == KeyguardState.GONE -> CommunalScenes.Blank
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index fc9a7df..35b27aa 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -21,6 +21,7 @@
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
@@ -46,6 +47,7 @@
import kotlinx.coroutines.launch
/** The default view model used for showing the communal hub. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class CommunalViewModel
@Inject
@@ -54,6 +56,7 @@
private val communalInteractor: CommunalInteractor,
tutorialInteractor: CommunalTutorialInteractor,
shadeInteractor: ShadeInteractor,
+ deviceEntryInteractor: DeviceEntryInteractor,
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
@CommunalLog logBuffer: LogBuffer,
) : BaseCommunalViewModel(communalInteractor, mediaHost) {
@@ -87,6 +90,8 @@
/** Whether touches should be disabled in communal */
val touchesAllowed: Flow<Boolean> = not(shadeInteractor.isAnyFullyExpanded)
+ val deviceUnlocked: Flow<Boolean> = deviceEntryInteractor.isUnlocked
+
init {
// Initialize our media host for the UMO. This only needs to happen once and must be done
// before the MediaHierarchyManager attempts to move the UMO to the hub.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index ad0c3fb..64e2870 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -24,6 +24,7 @@
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -79,6 +80,12 @@
val keyguardAlpha: StateFlow<Float>
/**
+ * Observable of the relative offset of the lock-screen clock from its natural position on the
+ * screen.
+ */
+ val clockPosition: StateFlow<Position>
+
+ /**
* Observable for whether the keyguard is showing.
*
* Note: this is also `true` when the lock-screen is occluded with an `Activity` "above" it in
@@ -233,6 +240,11 @@
fun setKeyguardAlpha(alpha: Float)
/**
+ * Sets the relative offset of the lock-screen clock from its natural position on the screen.
+ */
+ fun setClockPosition(x: Int, y: Int)
+
+ /**
* Returns whether the keyguard bottom area should be constrained to the top of the lock icon
*/
fun isUdfpsSupported(): Boolean
@@ -311,6 +323,9 @@
private val _keyguardAlpha = MutableStateFlow(1f)
override val keyguardAlpha = _keyguardAlpha.asStateFlow()
+ private val _clockPosition = MutableStateFlow(Position(0, 0))
+ override val clockPosition = _clockPosition.asStateFlow()
+
private val _clockShouldBeCentered = MutableStateFlow(true)
override val clockShouldBeCentered: Flow<Boolean> = _clockShouldBeCentered.asStateFlow()
@@ -662,6 +677,10 @@
_keyguardAlpha.value = alpha
}
+ override fun setClockPosition(x: Int, y: Int) {
+ _clockPosition.value = Position(x, y)
+ }
+
override fun isUdfpsSupported(): Boolean = keyguardUpdateMonitor.isUdfpsSupported
override fun setQuickSettingsVisible(isVisible: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index ee892a8..7ae70a9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -63,19 +63,18 @@
burnInHelperWrapper.burnInProgressOffset()
)
- /** Given the max x,y dimens, determine the current translation shifts. */
- fun burnIn(xDimenResourceId: Int, yDimenResourceId: Int): Flow<BurnInModel> {
- return combine(
- burnInOffset(xDimenResourceId, isXAxis = true),
- burnInOffset(yDimenResourceId, isXAxis = false).map {
- it * 2 - context.resources.getDimensionPixelSize(yDimenResourceId)
+ val keyguardBurnIn: Flow<BurnInModel> =
+ combine(
+ burnInOffset(R.dimen.burn_in_prevention_offset_x, isXAxis = true),
+ burnInOffset(R.dimen.burn_in_prevention_offset_y, isXAxis = false).map {
+ it * 2 -
+ context.resources.getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y)
}
) { translationX, translationY ->
BurnInModel(translationX, translationY, burnInHelperWrapper.burnInScale())
}
.distinctUntilChanged()
.stateIn(scope, SharingStarted.Lazily, BurnInModel())
- }
/**
* Use for max burn-in offsets that are NOT specified in pixels. This flow will recalculate the
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
index e5bb5a0..d2a7486 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBottomAreaInteractor.kt
@@ -22,8 +22,6 @@
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
/** Encapsulates business-logic specifically related to the keyguard bottom area. */
@SysUISingleton
@@ -37,12 +35,10 @@
/** The amount of alpha for the UI components of the bottom area. */
val alpha: Flow<Float> = repository.bottomAreaAlpha
/** The position of the keyguard clock. */
- private val _clockPosition = MutableStateFlow(Position(0, 0))
- @Deprecated("with migrateClocksToBlueprint()")
- val clockPosition: Flow<Position> = _clockPosition.asStateFlow()
+ val clockPosition: Flow<Position> = repository.clockPosition
fun setClockPosition(x: Int, y: Int) {
- _clockPosition.value = Position(x, y)
+ repository.setClockPosition(x, y)
}
fun setAlpha(alpha: Float) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index fbf1e22..5410b10 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -27,6 +27,7 @@
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.NotificationContainerBounds
+import com.android.systemui.common.shared.model.Position
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -231,6 +232,9 @@
/** The approximate location on the screen of the face unlock sensor, if one is available. */
val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation
+ /** The position of the keyguard clock. */
+ val clockPosition: Flow<Position> = repository.clockPosition
+
@Deprecated("Use the relevant TransitionViewModel")
val keyguardAlpha: Flow<Float> = repository.keyguardAlpha
@@ -338,6 +342,10 @@
repository.setQuickSettingsVisible(isVisible)
}
+ fun setClockPosition(x: Int, y: Int) {
+ repository.setClockPosition(x, y)
+ }
+
fun setAlpha(alpha: Float) {
repository.setKeyguardAlpha(alpha)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 462d5e6..4812e03 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -42,7 +42,7 @@
import kotlinx.coroutines.launch
private const val TAG = "KeyguardBlueprintViewBinder"
-private const val DEBUG = true
+private const val DEBUG = false
@SysUISingleton
class KeyguardBlueprintViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
index e67a324..98bebd0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
@@ -21,8 +21,6 @@
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.KeyguardRootView
@@ -39,18 +37,13 @@
private val clockViewModel: KeyguardClockViewModel,
) : KeyguardSection() {
private lateinit var burnInLayer: AodBurnInLayer
- private lateinit var emptyView: View
override fun addViews(constraintLayout: ConstraintLayout) {
if (!migrateClocksToBlueprint()) {
return
}
// The burn-in layer requires at least 1 view at all times
- emptyView =
- View(context, null).apply {
- id = R.id.burn_in_layer_empty_view
- visibility = View.GONE
- }
+ val emptyView = View(context, null).apply { id = View.generateViewId() }
constraintLayout.addView(emptyView)
burnInLayer =
AodBurnInLayer(context).apply {
@@ -77,13 +70,6 @@
if (!migrateClocksToBlueprint()) {
return
}
-
- constraintSet.apply {
- // The empty view should not occupy any space
- constrainHeight(R.id.burn_in_layer_empty_view, 1)
- constrainWidth(R.id.burn_in_layer_empty_view, 0)
- connect(R.id.burn_in_layer_empty_view, BOTTOM, PARENT_ID, BOTTOM)
- }
}
override fun removeViews(constraintLayout: ConstraintLayout) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 4ddd039..6184c82 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -199,7 +199,7 @@
private val TRANSITION_PROPERTIES =
arrayOf(PROP_VISIBILITY, PROP_ALPHA, PROP_BOUNDS, SMARTSPACE_BOUNDS)
- private val DEBUG = true
+ private val DEBUG = false
private val TAG = VisibilityBoundsTransition::class.simpleName!!
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 62fc1da..7be390a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import android.util.Log
import android.util.MathUtils
import com.android.app.animation.Interpolators
import com.android.keyguard.KeyguardClockSwitch
@@ -63,8 +62,6 @@
private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
private val keyguardClockViewModel: KeyguardClockViewModel,
) {
- private val TAG = "AodBurnInViewModel"
-
/** Horizontal translation for elements that need to apply anti-burn-in tactics. */
fun translationX(
params: BurnInParameters,
@@ -74,17 +71,8 @@
/** Vertical translation for elements that need to apply anti-burn-in tactics. */
fun translationY(
- burnInParams: BurnInParameters,
+ params: BurnInParameters,
): Flow<Float> {
- val params =
- if (burnInParams.minViewY < burnInParams.topInset) {
- // minViewY should never be below the inset. Correct it if needed
- Log.w(TAG, "minViewY is below topInset: $burnInParams")
- burnInParams.copy(minViewY = burnInParams.topInset)
- } else {
- burnInParams
- }
-
return configurationInteractor
.dimensionPixelSize(R.dimen.keyguard_enter_from_top_translation_y)
.flatMapLatest { enterFromTopAmount ->
@@ -137,10 +125,7 @@
keyguardTransitionInteractor.dozeAmountTransition.map {
Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value)
},
- burnInInteractor.burnIn(
- xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
- yDimenResourceId = R.dimen.burn_in_prevention_offset_y
- )
+ burnInInteractor.keyguardBurnIn,
) { interpolated, burnIn ->
val useScaleOnly =
(clockController(params.clockControllerProvider)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 31e8093..bd19c80 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -180,7 +180,7 @@
}
private val isUnlocked: Flow<Boolean> =
- deviceEntryInteractor.isUnlocked.flatMapLatest { isUnlocked ->
+ keyguardInteractor.isKeyguardDismissible.flatMapLatest { isUnlocked ->
if (!isUnlocked) {
flowOf(false)
} else {
@@ -197,7 +197,7 @@
val iconType: Flow<DeviceEntryIconView.IconType> =
combine(
deviceEntryUdfpsInteractor.isListeningForUdfps,
- keyguardInteractor.isKeyguardDismissible,
+ isUnlocked,
) { isListeningForUdfps, isUnlocked ->
if (isListeningForUdfps) {
DeviceEntryIconView.IconType.FINGERPRINT
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
index e35e065..6458eda 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -17,14 +17,10 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.Flags.keyguardBottomAreaRefactor
-import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.shared.model.BurnInModel
-import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -36,10 +32,9 @@
@Inject
constructor(
private val keyguardInteractor: KeyguardInteractor,
- private val bottomAreaInteractor: KeyguardBottomAreaInteractor,
+ bottomAreaInteractor: KeyguardBottomAreaInteractor,
keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
private val burnInHelperWrapper: BurnInHelperWrapper,
- private val burnInInteractor: BurnInInteractor,
private val shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
configurationInteractor: ConfigurationInteractor,
) {
@@ -68,37 +63,24 @@
}
.distinctUntilChanged()
}
-
- private val burnIn: Flow<BurnInModel> =
- burnInInteractor
- .burnIn(
- xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
- yDimenResourceId = R.dimen.default_burn_in_prevention_offset,
- )
- .distinctUntilChanged()
-
/** An observable for the x-offset by which the indication area should be translated. */
val indicationAreaTranslationX: Flow<Float> =
- if (migrateClocksToBlueprint() || keyguardBottomAreaRefactor()) {
- burnIn.map { it.translationX.toFloat() }
+ if (keyguardBottomAreaRefactor()) {
+ keyguardInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
} else {
bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
}
/** Returns an observable for the y-offset by which the indication area should be translated. */
fun indicationAreaTranslationY(defaultBurnInOffset: Int): Flow<Float> {
- return if (migrateClocksToBlueprint()) {
- burnIn.map { it.translationY.toFloat() }
- } else {
- keyguardInteractor.dozeAmount
- .map { dozeAmount ->
- dozeAmount *
- (burnInHelperWrapper.burnInOffset(
- /* amplitude = */ defaultBurnInOffset * 2,
- /* xAxis= */ false,
- ) - defaultBurnInOffset)
- }
- .distinctUntilChanged()
- }
+ return keyguardInteractor.dozeAmount
+ .map { dozeAmount ->
+ dozeAmount *
+ (burnInHelperWrapper.burnInOffset(
+ /* amplitude = */ defaultBurnInOffset * 2,
+ /* xAxis= */ false,
+ ) - defaultBurnInOffset)
+ }
+ .distinctUntilChanged()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 38d5e0f..1760b92 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -75,6 +75,7 @@
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
private val dozingToGoneTransitionViewModel: DozingToGoneTransitionViewModel,
private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
+ private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
private val glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel,
private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
@@ -171,6 +172,7 @@
aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
dozingToGoneTransitionViewModel.lockscreenAlpha(viewState),
dozingToLockscreenTransitionViewModel.lockscreenAlpha,
+ dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
glanceableHubToLockscreenTransitionViewModel.keyguardAlpha,
goneToAodTransitionViewModel.enterFromTopAnimationAlpha,
goneToDozingTransitionViewModel.lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 306aad1..75bf131 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -156,6 +156,7 @@
* desired scene. Once enough of the transition has occurred, the [currentScene] will become
* [toScene] (unless the transition is canceled by user action or another call to this method).
*/
+ @JvmOverloads
fun changeScene(
toScene: SceneKey,
loggingReason: String,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
index 22645c4..ea19020 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
@@ -71,7 +71,6 @@
override fun onApplyWindowInsets(windowInsets: WindowInsets): WindowInsets? {
val insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars())
- val displayCutout = rootWindowInsets.displayCutout
if (fitsSystemWindows) {
val paddingChanged = insets.top != paddingTop || insets.bottom != paddingBottom
@@ -79,23 +78,22 @@
if (paddingChanged) {
setPadding(0, 0, 0, 0)
}
-
- val pairInsets: Pair<Int, Int> =
- layoutInsetsController.getinsets(windowInsets, displayCutout)
- leftInset = pairInsets.first
- rightInset = pairInsets.second
- applyMargins()
} else {
val changed =
paddingLeft != 0 || paddingRight != 0 || paddingTop != 0 || paddingBottom != 0
if (changed) {
setPadding(0, 0, 0, 0)
}
-
- leftInset = 0
- rightInset = 0
}
+ leftInset = 0
+ rightInset = 0
+ val displayCutout = rootWindowInsets.displayCutout
+ val pairInsets: Pair<Int, Int> =
+ layoutInsetsController.getinsets(windowInsets, displayCutout)
+ leftInset = pairInsets.first
+ rightInset = pairInsets.second
+ applyMargins()
return windowInsets
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index cf7c3e0..1566de5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1288,9 +1288,10 @@
mView.getContext().getDisplay());
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
+ }
- mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
- mKeyguardStatusViewController.getView().addOnLayoutChangeListener(
+ mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
+ mKeyguardStatusViewController.getView().addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
int oldHeight = oldBottom - oldTop;
if (v.getHeight() != oldHeight) {
@@ -1298,8 +1299,7 @@
}
});
- updateClockAppearance();
- }
+ updateClockAppearance();
}
@Override
@@ -1326,9 +1326,7 @@
private void onSplitShadeEnabledChanged() {
mShadeLog.logSplitShadeChanged(mSplitShadeEnabled);
- if (!migrateClocksToBlueprint()) {
- mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
- }
+ mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
// Reset any left over overscroll state. It is a rare corner case but can happen.
mQsController.setOverScrollAmount(0);
mScrimController.setNotificationsOverScrollAmount(0);
@@ -1443,13 +1441,11 @@
mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
mStatusBarStateController.getInterpolatedDozeAmount());
- if (!migrateClocksToBlueprint()) {
- mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
- mBarState,
- false,
- false,
- mBarState);
- }
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ mBarState,
+ false,
+ false,
+ mBarState);
if (mKeyguardQsUserSwitchController != null) {
mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
mBarState,
@@ -1669,11 +1665,13 @@
mKeyguardStatusViewController.setLockscreenClockY(
mClockPositionAlgorithm.getExpandedPreferredClockY());
}
- if (!(migrateClocksToBlueprint() || keyguardBottomAreaRefactor())) {
+ if (keyguardBottomAreaRefactor()) {
+ mKeyguardInteractor.setClockPosition(
+ mClockPositionResult.clockX, mClockPositionResult.clockY);
+ } else {
mKeyguardBottomAreaInteractor.setClockPosition(
mClockPositionResult.clockX, mClockPositionResult.clockY);
}
-
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
@@ -1751,11 +1749,13 @@
}
private void updateKeyguardStatusViewAlignment(boolean animate) {
- if (migrateClocksToBlueprint()) {
- return;
- }
boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
- ConstraintLayout layout = mNotificationContainerParent;
+ ConstraintLayout layout;
+ if (migrateClocksToBlueprint()) {
+ layout = mKeyguardViewConfigurator.getKeyguardRootView();
+ } else {
+ layout = mNotificationContainerParent;
+ }
mKeyguardStatusViewController.updateAlignment(
layout, mSplitShadeEnabled, shouldBeCentered, animate);
mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered));
@@ -3316,9 +3316,6 @@
/** Updates the views to the initial state for the fold to AOD animation. */
@Override
public void prepareFoldToAodAnimation() {
- if (migrateClocksToBlueprint()) {
- return;
- }
// Force show AOD UI even if we are not locked
showAodUi();
@@ -3340,9 +3337,6 @@
@Override
public void startFoldToAodAnimation(Runnable startAction, Runnable endAction,
Runnable cancelAction) {
- if (migrateClocksToBlueprint()) {
- return;
- }
final ViewPropertyAnimator viewAnimator = mView.animate();
viewAnimator.cancel();
viewAnimator
@@ -3378,9 +3372,6 @@
/** Cancels fold to AOD transition and resets view state. */
@Override
public void cancelFoldToAodAnimation() {
- if (migrateClocksToBlueprint()) {
- return;
- }
cancelAnimation();
resetAlpha();
resetTranslation();
@@ -4455,13 +4446,11 @@
}
}
- if (!migrateClocksToBlueprint()) {
- mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
- statusBarState,
- keyguardFadingAway,
- goingToFullShade,
- mBarState);
- }
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ statusBarState,
+ keyguardFadingAway,
+ goingToFullShade,
+ mBarState);
if (!keyguardBottomAreaRefactor()) {
setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 8f9cef3..f7fed53 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -60,7 +60,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.scene.ui.view.WindowRootViewComponent;
import com.android.systemui.settings.UserTracker;
@@ -507,7 +506,7 @@
private void applyFitsSystemWindows(NotificationShadeWindowState state) {
boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded();
- if (!SceneContainerFlag.isEnabled() && mWindowRootView != null
+ if (mWindowRootView != null
&& mWindowRootView.getFitsSystemWindows() != fitsSystemWindows) {
mWindowRootView.setFitsSystemWindows(fitsSystemWindows);
mWindowRootView.requestApplyInsets();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index d1055c7..ee84434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -81,6 +81,9 @@
import com.android.systemui.navigationbar.TaskbarDelegate;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionListener;
@@ -157,6 +160,7 @@
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
private final BouncerView mPrimaryBouncerView;
private final Lazy<ShadeController> mShadeController;
+ private final Lazy<SceneInteractor> mSceneInteractorLazy;
// Local cache of expansion events, to avoid duplicates
private float mFraction = -1f;
@@ -381,7 +385,8 @@
Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy,
SelectedUserInteractor selectedUserInteractor,
Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor,
- JavaAdapter javaAdapter
+ JavaAdapter javaAdapter,
+ Lazy<SceneInteractor> sceneInteractorLazy
) {
mContext = context;
mViewMediatorCallback = callback;
@@ -415,6 +420,7 @@
mSelectedUserInteractor = selectedUserInteractor;
mSurfaceBehindInteractor = surfaceBehindInteractor;
mJavaAdapter = javaAdapter;
+ mSceneInteractorLazy = sceneInteractorLazy;
}
KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -633,8 +639,11 @@
public void show(Bundle options) {
Trace.beginSection("StatusBarKeyguardViewManager#show");
mNotificationShadeWindowController.setKeyguardShowing(true);
- mKeyguardStateController.notifyKeyguardState(true,
- mKeyguardStateController.isOccluded());
+ if (SceneContainerFlag.isEnabled()) {
+ mSceneInteractorLazy.get().changeScene(
+ Scenes.Lockscreen, "StatusBarKeyguardViewManager.show");
+ }
+ mKeyguardStateController.notifyKeyguardState(true, mKeyguardStateController.isOccluded());
reset(true /* hideBouncerWhenShowing */);
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
index d19a336..5c53ff9 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimation.kt
@@ -16,8 +16,6 @@
package com.android.systemui.unfold
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.annotation.BinderThread
import android.content.Context
@@ -25,6 +23,7 @@
import android.os.SystemProperties
import android.util.Log
import android.view.animation.DecelerateInterpolator
+import androidx.core.animation.addListener
import com.android.internal.foldables.FoldLockSettingAvailabilityProvider
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DeviceStateRepository
@@ -37,25 +36,17 @@
import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
import javax.inject.Inject
-import kotlin.coroutines.resume
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.android.asCoroutineDispatcher
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.launch
-import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeout
-@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
class FoldLightRevealOverlayAnimation
@Inject
constructor(
@@ -70,9 +61,6 @@
private val revealProgressValueAnimator: ValueAnimator =
ValueAnimator.ofFloat(ALPHA_OPAQUE, ALPHA_TRANSPARENT)
- private val areAnimationEnabled: Flow<Boolean>
- get() = animationStatusRepository.areAnimationsEnabled()
-
private lateinit var controller: FullscreenLightRevealAnimationController
@Volatile private var readyCallback: CompletableDeferred<Runnable>? = null
@@ -101,30 +89,33 @@
applicationScope.launch(bgHandler.asCoroutineDispatcher()) {
deviceStateRepository.state
- .map { it == DeviceStateRepository.DeviceState.FOLDED }
+ .map { it != DeviceStateRepository.DeviceState.FOLDED }
.distinctUntilChanged()
- .flatMapLatest { isFolded ->
- flow<Nothing> {
- if (!areAnimationEnabled.first() || !isFolded) {
- return@flow
- }
- withTimeout(WAIT_FOR_ANIMATION_TIMEOUT_MS) {
- readyCallback = CompletableDeferred()
- val onReady = readyCallback?.await()
- readyCallback = null
- controller.addOverlay(ALPHA_OPAQUE, onReady)
- waitForScreenTurnedOn()
- }
+ .filter { isUnfolded -> isUnfolded }
+ .collect { controller.ensureOverlayRemoved() }
+ }
+
+ applicationScope.launch(bgHandler.asCoroutineDispatcher()) {
+ deviceStateRepository.state
+ .filter {
+ animationStatusRepository.areAnimationsEnabled().first() &&
+ it == DeviceStateRepository.DeviceState.FOLDED
+ }
+ .collect {
+ try {
+ withTimeout(WAIT_FOR_ANIMATION_TIMEOUT_MS) {
+ readyCallback = CompletableDeferred()
+ val onReady = readyCallback?.await()
+ readyCallback = null
+ controller.addOverlay(ALPHA_OPAQUE, onReady)
+ waitForScreenTurnedOn()
playFoldLightRevealOverlayAnimation()
}
- .catchTimeoutAndLog()
- .onCompletion {
- val onReady = readyCallback?.takeIf { it.isCompleted }?.getCompleted()
- onReady?.run()
- readyCallback = null
- }
+ } catch (e: TimeoutCancellationException) {
+ Log.e(TAG, "Fold light reveal animation timed out")
+ ensureOverlayRemovedInternal()
+ }
}
- .collect {}
}
}
@@ -137,34 +128,19 @@
powerInteractor.screenPowerState.filter { it == ScreenPowerState.SCREEN_ON }.first()
}
- private suspend fun playFoldLightRevealOverlayAnimation() {
+ private fun ensureOverlayRemovedInternal() {
+ revealProgressValueAnimator.cancel()
+ controller.ensureOverlayRemoved()
+ }
+
+ private fun playFoldLightRevealOverlayAnimation() {
revealProgressValueAnimator.duration = ANIMATION_DURATION
revealProgressValueAnimator.interpolator = DecelerateInterpolator()
revealProgressValueAnimator.addUpdateListener { animation ->
controller.updateRevealAmount(animation.animatedFraction)
}
- revealProgressValueAnimator.startAndAwaitCompletion()
- }
-
- private suspend fun ValueAnimator.startAndAwaitCompletion(): Unit =
- suspendCancellableCoroutine { continuation ->
- val listener =
- object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- continuation.resume(Unit)
- removeListener(this)
- }
- }
- addListener(listener)
- continuation.invokeOnCancellation { removeListener(listener) }
- start()
- }
-
- private fun <T> Flow<T>.catchTimeoutAndLog() = catch { exception ->
- when (exception) {
- is TimeoutCancellationException -> Log.e(TAG, "Fold light reveal animation timed out")
- else -> throw exception
- }
+ revealProgressValueAnimator.addListener(onEnd = { controller.ensureOverlayRemoved() })
+ revealProgressValueAnimator.start()
}
private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index 90587d7..13fb42c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -30,6 +32,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.power.data.repository.FakePowerRepository;
import com.android.systemui.power.domain.interactor.PowerInteractorFactory;
import com.android.systemui.res.R;
@@ -59,6 +62,7 @@
@Mock protected KeyguardStatusViewController mControllerMock;
@Mock protected InteractionJankMonitor mInteractionJankMonitor;
@Mock protected ViewTreeObserver mViewTreeObserver;
+ @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock protected DumpManager mDumpManager;
protected FakeKeyguardRepository mFakeKeyguardRepository;
protected FakePowerRepository mFakePowerRepository;
@@ -89,6 +93,7 @@
mKeyguardLogger,
mInteractionJankMonitor,
deps.getKeyguardInteractor(),
+ mKeyguardTransitionInteractor,
mDumpManager,
PowerInteractorFactory.create(
mFakePowerRepository
@@ -105,6 +110,7 @@
when(mKeyguardStatusView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
when(mKeyguardClockSwitchController.getView()).thenReturn(mKeyguardClockSwitch);
+ when(mKeyguardTransitionInteractor.getGoneToAodTransition()).thenReturn(emptyFlow());
when(mKeyguardStatusView.findViewById(R.id.keyguard_status_area))
.thenReturn(mKeyguardStatusAreaView);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
index d0b1dd5..df52265 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
@@ -122,13 +122,7 @@
testScope.runTest {
whenever(burnInHelperWrapper.burnInScale()).thenReturn(0.5f)
- val burnInModel by
- collectLastValue(
- underTest.burnIn(
- xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
- yDimenResourceId = R.dimen.burn_in_prevention_offset_y
- )
- )
+ val burnInModel by collectLastValue(underTest.keyguardBurnIn)
// After time tick, returns the configured values
fakeKeyguardRepository.dozeTimeTick(10)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
index 02bd810..4bb0d47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
@@ -27,11 +27,14 @@
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel.Companion.UNLOCKED_DELAY_MS
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.runner.RunWith
@@ -71,7 +74,10 @@
fun isLongPressEnabled_unlocked() =
testScope.runTest {
val isLongPressEnabled by collectLastValue(underTest.isLongPressEnabled)
+ fingerprintPropertyRepository.supportsUdfps()
keyguardRepository.setKeyguardDismissible(true)
+ advanceTimeBy(UNLOCKED_DELAY_MS * 2) // wait for unlocked delay
+ runCurrent()
assertThat(isLongPressEnabled).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
new file mode 100644
index 0000000..864acfb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardIndicationAreaViewModelTest : SysuiTestCase() {
+
+ @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
+ @Mock private lateinit var shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel
+
+ private lateinit var underTest: KeyguardIndicationAreaViewModel
+ private lateinit var repository: FakeKeyguardRepository
+
+ private val startButtonFlow =
+ MutableStateFlow<KeyguardQuickAffordanceViewModel>(
+ KeyguardQuickAffordanceViewModel(
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()
+ )
+ )
+ private val endButtonFlow =
+ MutableStateFlow<KeyguardQuickAffordanceViewModel>(
+ KeyguardQuickAffordanceViewModel(
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId()
+ )
+ )
+ private val alphaFlow = MutableStateFlow<Float>(1f)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
+
+ whenever(burnInHelperWrapper.burnInOffset(anyInt(), any()))
+ .thenReturn(RETURNED_BURN_IN_OFFSET)
+
+ val withDeps = KeyguardInteractorFactory.create()
+ val keyguardInteractor = withDeps.keyguardInteractor
+ repository = withDeps.repository
+
+ val bottomAreaViewModel: KeyguardBottomAreaViewModel = mock()
+ whenever(bottomAreaViewModel.startButton).thenReturn(startButtonFlow)
+ whenever(bottomAreaViewModel.endButton).thenReturn(endButtonFlow)
+ whenever(bottomAreaViewModel.alpha).thenReturn(alphaFlow)
+ underTest =
+ KeyguardIndicationAreaViewModel(
+ keyguardInteractor = keyguardInteractor,
+ bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
+ keyguardBottomAreaViewModel = bottomAreaViewModel,
+ burnInHelperWrapper = burnInHelperWrapper,
+ shortcutsCombinedViewModel = shortcutsCombinedViewModel,
+ configurationInteractor = ConfigurationInteractor(FakeConfigurationRepository()),
+ )
+ }
+
+ @Test
+ fun alpha() = runTest {
+ val value = collectLastValue(underTest.alpha)
+
+ assertThat(value()).isEqualTo(1f)
+ alphaFlow.value = 0.1f
+ assertThat(value()).isEqualTo(0.1f)
+ alphaFlow.value = 0.5f
+ assertThat(value()).isEqualTo(0.5f)
+ alphaFlow.value = 0.2f
+ assertThat(value()).isEqualTo(0.2f)
+ alphaFlow.value = 0f
+ assertThat(value()).isEqualTo(0f)
+ }
+
+ @Test
+ fun isIndicationAreaPadded() = runTest {
+ repository.setKeyguardShowing(true)
+ val value = collectLastValue(underTest.isIndicationAreaPadded)
+
+ assertThat(value()).isFalse()
+ startButtonFlow.value = startButtonFlow.value.copy(isVisible = true)
+ assertThat(value()).isTrue()
+ endButtonFlow.value = endButtonFlow.value.copy(isVisible = true)
+ assertThat(value()).isTrue()
+ startButtonFlow.value = startButtonFlow.value.copy(isVisible = false)
+ assertThat(value()).isTrue()
+ endButtonFlow.value = endButtonFlow.value.copy(isVisible = false)
+ assertThat(value()).isFalse()
+ }
+
+ @Test
+ fun indicationAreaTranslationX() = runTest {
+ val value = collectLastValue(underTest.indicationAreaTranslationX)
+
+ assertThat(value()).isEqualTo(0f)
+ repository.setClockPosition(100, 100)
+ assertThat(value()).isEqualTo(100f)
+ repository.setClockPosition(200, 100)
+ assertThat(value()).isEqualTo(200f)
+ repository.setClockPosition(200, 200)
+ assertThat(value()).isEqualTo(200f)
+ repository.setClockPosition(300, 100)
+ assertThat(value()).isEqualTo(300f)
+ }
+
+ @Test
+ fun indicationAreaTranslationY() = runTest {
+ val value = collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET))
+
+ // Negative 0 - apparently there's a difference in floating point arithmetic - FML
+ assertThat(value()).isEqualTo(-0f)
+ val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f)
+ assertThat(value()).isEqualTo(expected1)
+ val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f)
+ assertThat(value()).isEqualTo(expected2)
+ val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f)
+ assertThat(value()).isEqualTo(expected3)
+ val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f)
+ assertThat(value()).isEqualTo(expected4)
+ }
+
+ private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
+ repository.setDozeAmount(dozeAmount)
+ return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET)
+ }
+
+ companion object {
+ private const val DEFAULT_BURN_IN_OFFSET = 5
+ private const val RETURNED_BURN_IN_OFFSET = 3
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index f8771b2..fd7b139 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -461,6 +461,7 @@
mKeyguardLogger,
mInteractionJankMonitor,
mKeyguardInteractor,
+ mKeyguardTransitionInteractor,
mDumpManager,
mPowerInteractor));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 3666248..f050857 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -86,6 +86,7 @@
import com.android.systemui.navigationbar.TaskbarDelegate;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
@@ -224,7 +225,8 @@
() -> mock(KeyguardDismissActionInteractor.class),
mSelectedUserInteractor,
() -> mock(KeyguardSurfaceBehindInteractor.class),
- mock(JavaAdapter.class)) {
+ mock(JavaAdapter.class),
+ () -> mock(SceneInteractor.class)) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
@@ -733,7 +735,8 @@
() -> mock(KeyguardDismissActionInteractor.class),
mSelectedUserInteractor,
() -> mock(KeyguardSurfaceBehindInteractor.class),
- mock(JavaAdapter.class)) {
+ mock(JavaAdapter.class),
+ () -> mock(SceneInteractor.class)) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 1e305d6..793e2d7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.data.repository
import android.graphics.Point
+import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
@@ -57,6 +58,9 @@
private val _bottomAreaAlpha = MutableStateFlow(1f)
override val bottomAreaAlpha: StateFlow<Float> = _bottomAreaAlpha
+ private val _clockPosition = MutableStateFlow(Position(0, 0))
+ override val clockPosition: StateFlow<Position> = _clockPosition
+
private val _isKeyguardShowing = MutableStateFlow(false)
override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
@@ -145,6 +149,10 @@
_bottomAreaAlpha.value = alpha
}
+ override fun setClockPosition(x: Int, y: Int) {
+ _clockPosition.value = Position(x, y)
+ }
+
fun setKeyguardShowing(isShowing: Boolean) {
_isKeyguardShowing.value = isShowing
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index d5d357f..c2300a1e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -43,6 +43,7 @@
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
dozingToGoneTransitionViewModel = dozingToGoneTransitionViewModel,
dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
+ dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel,
goneToAodTransitionViewModel = goneToAodTransitionViewModel,
goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index f4ea754..279bd72 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -1004,17 +1004,19 @@
&& overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
- } // TODO(b/319537921): should there be an else here?
- //Primary pointer is swiping, so transit to PanningScalingState
- transitToPanningScalingStateAndClear();
+ } else {
+ //Primary pointer is swiping, so transit to PanningScalingState
+ transitToPanningScalingStateAndClear();
+ }
} else if (mIsSinglePanningEnabled
&& isActivated()
&& event.getPointerCount() == 1) {
if (overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
- } // TODO(b/319537921): should there be an else here?
- transitToSinglePanningStateAndClear();
+ } else {
+ transitToSinglePanningStateAndClear();
+ }
} else if (!mIsTwoFingerCountReached) {
// If it is a two-finger gesture, do not transition to the
// delegating state to ensure the reachability of
@@ -1257,17 +1259,19 @@
&& overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
+ } else {
+ //Primary pointer is swiping, so transit to PanningScalingState
+ transitToPanningScalingStateAndClear();
}
- //Primary pointer is swiping, so transit to PanningScalingState
- transitToPanningScalingStateAndClear();
} else if (mIsSinglePanningEnabled
&& isActivated()
&& event.getPointerCount() == 1) {
if (overscrollState(event, mFirstPointerDownLocation)
== OVERSCROLL_VERTICAL_EDGE) {
transitionToDelegatingStateAndClear();
+ } else {
+ transitToSinglePanningStateAndClear();
}
- transitToSinglePanningStateAndClear();
} else {
transitionToDelegatingStateAndClear();
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 6f45f60..b932743 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2074,6 +2074,7 @@
private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks,
int appWidgetId, int viewId, long requestId) {
try {
+ Slog.d(TAG, "Trying to notify widget view data changed");
callbacks.viewDataChanged(appWidgetId, viewId);
host.lastWidgetUpdateSequenceNo = requestId;
} catch (RemoteException re) {
@@ -2158,6 +2159,9 @@
private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks,
int appWidgetId, RemoteViews views, long requestId) {
try {
+ Slog.d(TAG, "Trying to notify widget update for package "
+ + (views == null ? "null" : views.getPackage())
+ + " with widget id: " + appWidgetId);
callbacks.updateAppWidget(appWidgetId, views);
host.lastWidgetUpdateSequenceNo = requestId;
} catch (RemoteException re) {
@@ -2196,6 +2200,7 @@
private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks,
int appWidgetId, AppWidgetProviderInfo info, long requestId) {
try {
+ Slog.d(TAG, "Trying to notify provider update");
callbacks.providerChanged(appWidgetId, info);
host.lastWidgetUpdateSequenceNo = requestId;
} catch (RemoteException re) {
@@ -2239,6 +2244,7 @@
private void handleNotifyAppWidgetRemoved(Host host, IAppWidgetHost callbacks, int appWidgetId,
long requestId) {
try {
+ Slog.d(TAG, "Trying to notify widget removed");
callbacks.appWidgetRemoved(appWidgetId);
host.lastWidgetUpdateSequenceNo = requestId;
} catch (RemoteException re) {
@@ -2286,6 +2292,7 @@
private void handleNotifyProvidersChanged(Host host, IAppWidgetHost callbacks) {
try {
+ Slog.d(TAG, "Trying to notify widget providers changed");
callbacks.providersChanged();
} catch (RemoteException re) {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 9189ea7..e1d7be1 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -348,6 +348,9 @@
// marked as stopped by the system
@NonNull private final Set<String> mInitialNonStoppedSystemPackages = new ArraySet<>();
+ // Which packages (key) are allowed to join particular SharedUid (value).
+ @NonNull private final Map<String, String> mPackageToSharedUidAllowList = new ArrayMap<>();
+
// A map of preloaded package names and the path to its app metadata file path.
private final ArrayMap<String, String> mAppMetadataFilePaths = new ArrayMap<>();
@@ -567,6 +570,11 @@
return mInitialNonStoppedSystemPackages;
}
+ @NonNull
+ public Map<String, String> getPackageToSharedUidAllowList() {
+ return mPackageToSharedUidAllowList;
+ }
+
public ArrayMap<String, String> getAppMetadataFilePaths() {
return mAppMetadataFilePaths;
}
@@ -1563,6 +1571,19 @@
mInitialNonStoppedSystemPackages.add(pkgName);
}
} break;
+ case "allow-package-shareduid": {
+ String pkgName = parser.getAttributeValue(null, "package");
+ String sharedUid = parser.getAttributeValue(null, "shareduid");
+ if (TextUtils.isEmpty(pkgName)) {
+ Slog.w(TAG, "<" + name + "> without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else if (TextUtils.isEmpty(sharedUid)) {
+ Slog.w(TAG, "<" + name + "> without shareduid in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mPackageToSharedUidAllowList.put(pkgName, sharedUid);
+ }
+ }
case "asl-file": {
String packageName = parser.getAttributeValue(null, "package");
String path = parser.getAttributeValue(null, "path");
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 5e9d1cb..8dc15ad 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -163,6 +163,9 @@
}
],
"file_patterns": ["PinnerService\\.java"]
+ },
+ {
+ "name": "FrameworksVpnTests"
}
]
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b8e09cc..258f53d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -372,6 +372,15 @@
@Overridable
public static final long FGS_BOOT_COMPLETED_RESTRICTIONS = 296558535L;
+ /**
+ * Disables foreground service background starts in System Alert Window for all types
+ * unless it already has a System Overlay Window.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = VERSION_CODES.VANILLA_ICE_CREAM)
+ @Overridable
+ public static final long FGS_SAW_RESTRICTIONS = 319471980L;
+
final ActivityManagerService mAm;
// Maximum number of services that we allow to start in the background
@@ -8526,10 +8535,31 @@
}
}
+ // The flag being enabled isn't enough to deny background start: we need to also check
+ // if there is a system alert UI present.
if (ret == REASON_DENIED) {
- if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
- callingPackage)) {
- ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
+ // Flag check: are we disabling SAW FGS background starts?
+ final boolean shouldDisableSaw = Flags.fgsDisableSaw()
+ && CompatChanges.isChangeEnabled(FGS_BOOT_COMPLETED_RESTRICTIONS, callingUid);
+ if (shouldDisableSaw) {
+ final ProcessRecord processRecord = mAm
+ .getProcessRecordLocked(targetService.processName,
+ targetService.appInfo.uid);
+ if (processRecord != null) {
+ if (processRecord.mState.hasOverlayUi()) {
+ if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
+ callingPackage)) {
+ ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
+ }
+ }
+ } else {
+ Slog.e(TAG, "Could not find process record for SAW check");
+ }
+ } else {
+ if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
+ callingPackage)) {
+ ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
+ }
}
}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index c06bdf9..b1823b4 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -23,6 +23,13 @@
}
flag {
+ name: "fgs_disable_saw"
+ namespace: "backstage_power"
+ description: "Disable System Alert Window FGS start"
+ bug: "296558535"
+}
+
+flag {
name: "bfgs_managed_network_access"
namespace: "backstage_power"
description: "Restrict network access for certain applications in BFGS process state"
diff --git a/services/core/java/com/android/server/connectivity/TEST_MAPPING b/services/core/java/com/android/server/connectivity/TEST_MAPPING
index 687d4b0..f508319 100644
--- a/services/core/java/com/android/server/connectivity/TEST_MAPPING
+++ b/services/core/java/com/android/server/connectivity/TEST_MAPPING
@@ -7,7 +7,7 @@
"exclude-annotation": "com.android.testutils.SkipPresubmit"
}
],
- "file_patterns": ["Vpn\\.java", "VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
+ "file_patterns": ["VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
}
],
"presubmit-large": [
@@ -26,5 +26,10 @@
],
"file_patterns": ["Vpn\\.java", "VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
}
+ ],
+ "postsubmit":[
+ {
+ "name":"FrameworksVpnTests"
+ }
]
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/flags/services.aconfig b/services/core/java/com/android/server/flags/services.aconfig
new file mode 100644
index 0000000..10b5eff
--- /dev/null
+++ b/services/core/java/com/android/server/flags/services.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.flags"
+
+flag {
+ namespace: "wear_frameworks"
+ name: "enable_odp_feature_guard"
+ description: "Enable guard based on system feature to prevent OnDevicePersonalization service from starting on form factors."
+ bug: "322249125"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index a79e771..05b1cb69 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -54,6 +54,7 @@
import android.hardware.input.InputSensorInfo;
import android.hardware.input.InputSettings;
import android.hardware.input.KeyboardLayout;
+import android.hardware.input.KeyboardLayoutSelectionResult;
import android.hardware.input.TouchCalibration;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
@@ -1244,9 +1245,9 @@
}
@Override // Binder call
- public String getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- @UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
- @Nullable InputMethodSubtype imeSubtype) {
+ public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice(
+ InputDeviceIdentifier identifier, @UserIdInt int userId,
+ @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) {
return mKeyboardLayoutManager.getKeyboardLayoutForInputDevice(identifier, userId,
imeInfo, imeSubtype);
}
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 46668de..283e692 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -16,10 +16,11 @@
package com.android.server.input;
-import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT;
-import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE;
-import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER;
-import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
+import static android.hardware.input.KeyboardLayoutSelectionResult.FAILED;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT;
import android.annotation.AnyThread;
import android.annotation.MainThread;
@@ -46,6 +47,7 @@
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.KeyboardLayout;
+import android.hardware.input.KeyboardLayoutSelectionResult;
import android.icu.lang.UScript;
import android.icu.util.ULocale;
import android.os.Bundle;
@@ -79,7 +81,6 @@
import com.android.server.LocalServices;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.input.KeyboardMetricsCollector.KeyboardConfigurationEvent;
-import com.android.server.input.KeyboardMetricsCollector.LayoutSelectionCriteria;
import com.android.server.inputmethod.InputMethodManagerInternal;
import libcore.io.Streams;
@@ -130,7 +131,8 @@
// This cache stores "best-matched" layouts so that we don't need to run the matching
// algorithm repeatedly.
@GuardedBy("mKeyboardLayoutCache")
- private final Map<String, KeyboardLayoutInfo> mKeyboardLayoutCache = new ArrayMap<>();
+ private final Map<String, KeyboardLayoutSelectionResult> mKeyboardLayoutCache =
+ new ArrayMap<>();
private final Object mImeInfoLock = new Object();
@Nullable
@GuardedBy("mImeInfoLock")
@@ -222,17 +224,17 @@
} else {
Set<String> selectedLayouts = new HashSet<>();
List<ImeInfo> imeInfoList = getImeInfoListForLayoutMapping();
- List<KeyboardLayoutInfo> layoutInfoList = new ArrayList<>();
+ List<KeyboardLayoutSelectionResult> resultList = new ArrayList<>();
boolean hasMissingLayout = false;
for (ImeInfo imeInfo : imeInfoList) {
// Check if the layout has been previously configured
- KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
+ KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal(
keyboardIdentifier, imeInfo);
- boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null;
+ boolean noLayoutFound = result.getLayoutDescriptor() == null;
if (!noLayoutFound) {
- selectedLayouts.add(layoutInfo.mDescriptor);
+ selectedLayouts.add(result.getLayoutDescriptor());
}
- layoutInfoList.add(layoutInfo);
+ resultList.add(result);
hasMissingLayout |= noLayoutFound;
}
@@ -260,7 +262,7 @@
}
if (shouldLogConfiguration) {
- logKeyboardConfigurationEvent(inputDevice, imeInfoList, layoutInfoList,
+ logKeyboardConfigurationEvent(inputDevice, imeInfoList, resultList,
!mDataStore.hasInputDeviceEntry(key));
}
} finally {
@@ -757,10 +759,10 @@
String keyboardLayoutDescriptor;
if (useNewSettingsUi()) {
synchronized (mImeInfoLock) {
- KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
+ KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal(
new KeyboardIdentifier(identifier, languageTag, layoutType),
mCurrentImeInfo);
- keyboardLayoutDescriptor = layoutInfo == null ? null : layoutInfo.mDescriptor;
+ keyboardLayoutDescriptor = result.getLayoutDescriptor();
}
} else {
keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier);
@@ -788,26 +790,26 @@
}
@AnyThread
- @Nullable
- public String getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- @UserIdInt int userId, @NonNull InputMethodInfo imeInfo,
- @Nullable InputMethodSubtype imeSubtype) {
+ @NonNull
+ public KeyboardLayoutSelectionResult getKeyboardLayoutForInputDevice(
+ InputDeviceIdentifier identifier, @UserIdInt int userId,
+ @NonNull InputMethodInfo imeInfo, @Nullable InputMethodSubtype imeSubtype) {
if (!useNewSettingsUi()) {
Slog.e(TAG, "getKeyboardLayoutForInputDevice() API not supported");
- return null;
+ return FAILED;
}
InputDevice inputDevice = getInputDevice(identifier);
if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
- return null;
+ return FAILED;
}
KeyboardIdentifier keyboardIdentifier = new KeyboardIdentifier(inputDevice);
- KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(
+ KeyboardLayoutSelectionResult result = getKeyboardLayoutForInputDeviceInternal(
keyboardIdentifier, new ImeInfo(userId, imeInfo, imeSubtype));
if (DEBUG) {
Slog.d(TAG, "getKeyboardLayoutForInputDevice() " + identifier.toString() + ", userId : "
- + userId + ", subtype = " + imeSubtype + " -> " + layoutInfo);
+ + userId + ", subtype = " + imeSubtype + " -> " + result);
}
- return layoutInfo != null ? layoutInfo.mDescriptor : null;
+ return result;
}
@AnyThread
@@ -942,13 +944,13 @@
}
@Nullable
- private KeyboardLayoutInfo getKeyboardLayoutForInputDeviceInternal(
+ private KeyboardLayoutSelectionResult getKeyboardLayoutForInputDeviceInternal(
KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo) {
String layoutKey = new LayoutKey(keyboardIdentifier, imeInfo).toString();
synchronized (mDataStore) {
String layout = mDataStore.getKeyboardLayout(keyboardIdentifier.toString(), layoutKey);
if (layout != null) {
- return new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER);
+ return new KeyboardLayoutSelectionResult(layout, LAYOUT_SELECTION_CRITERIA_USER);
}
}
@@ -961,17 +963,17 @@
KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal(
keyboardIdentifier, imeInfo);
// Call auto-matching algorithm to find the best matching layout
- KeyboardLayoutInfo layoutInfo =
+ KeyboardLayoutSelectionResult result =
getDefaultKeyboardLayoutBasedOnImeInfo(keyboardIdentifier, imeInfo,
layoutList);
- mKeyboardLayoutCache.put(layoutKey, layoutInfo);
- return layoutInfo;
+ mKeyboardLayoutCache.put(layoutKey, result);
+ return result;
}
}
}
- @Nullable
- private static KeyboardLayoutInfo getDefaultKeyboardLayoutBasedOnImeInfo(
+ @NonNull
+ private static KeyboardLayoutSelectionResult getDefaultKeyboardLayoutBasedOnImeInfo(
KeyboardIdentifier keyboardIdentifier, @Nullable ImeInfo imeInfo,
KeyboardLayout[] layoutList) {
Arrays.sort(layoutList);
@@ -986,7 +988,7 @@
+ "vendor and product Ids. " + keyboardIdentifier
+ " : " + layout.getDescriptor());
}
- return new KeyboardLayoutInfo(layout.getDescriptor(),
+ return new KeyboardLayoutSelectionResult(layout.getDescriptor(),
LAYOUT_SELECTION_CRITERIA_DEVICE);
}
}
@@ -1004,13 +1006,14 @@
+ "HW information (Language tag and Layout type). "
+ keyboardIdentifier + " : " + layoutDesc);
}
- return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_DEVICE);
+ return new KeyboardLayoutSelectionResult(layoutDesc,
+ LAYOUT_SELECTION_CRITERIA_DEVICE);
}
}
if (imeInfo == null || imeInfo.mImeSubtypeHandle == null || imeInfo.mImeSubtype == null) {
// Can't auto select layout based on IME info is null
- return null;
+ return FAILED;
}
InputMethodSubtype subtype = imeInfo.mImeSubtype;
@@ -1027,9 +1030,10 @@
+ layoutDesc);
}
if (layoutDesc != null) {
- return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD);
+ return new KeyboardLayoutSelectionResult(layoutDesc,
+ LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD);
}
- return null;
+ return FAILED;
}
@Nullable
@@ -1246,21 +1250,23 @@
}
private void logKeyboardConfigurationEvent(@NonNull InputDevice inputDevice,
- @NonNull List<ImeInfo> imeInfoList, @NonNull List<KeyboardLayoutInfo> layoutInfoList,
+ @NonNull List<ImeInfo> imeInfoList,
+ @NonNull List<KeyboardLayoutSelectionResult> resultList,
boolean isFirstConfiguration) {
- if (imeInfoList.isEmpty() || layoutInfoList.isEmpty()) {
+ if (imeInfoList.isEmpty() || resultList.isEmpty()) {
return;
}
KeyboardConfigurationEvent.Builder configurationEventBuilder =
new KeyboardConfigurationEvent.Builder(inputDevice).setIsFirstTimeConfiguration(
isFirstConfiguration);
for (int i = 0; i < imeInfoList.size(); i++) {
- KeyboardLayoutInfo layoutInfo = layoutInfoList.get(i);
+ KeyboardLayoutSelectionResult result = resultList.get(i);
String layoutName = null;
int layoutSelectionCriteria = LAYOUT_SELECTION_CRITERIA_DEFAULT;
- if (layoutInfo != null && layoutInfo.mDescriptor != null) {
- layoutSelectionCriteria = layoutInfo.mSelectionCriteria;
- KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(layoutInfo.mDescriptor);
+ if (result != null && result.getLayoutDescriptor() != null) {
+ layoutSelectionCriteria = result.getSelectionCriteria();
+ KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(
+ result.getLayoutDescriptor());
if (d != null) {
layoutName = d.keyboardLayoutName;
}
@@ -1477,33 +1483,6 @@
}
}
- private static class KeyboardLayoutInfo {
- @Nullable
- private final String mDescriptor;
- @LayoutSelectionCriteria
- private final int mSelectionCriteria;
-
- private KeyboardLayoutInfo(@Nullable String descriptor,
- @LayoutSelectionCriteria int selectionCriteria) {
- mDescriptor = descriptor;
- mSelectionCriteria = selectionCriteria;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof KeyboardLayoutInfo) {
- return Objects.equals(mDescriptor, ((KeyboardLayoutInfo) obj).mDescriptor)
- && mSelectionCriteria == ((KeyboardLayoutInfo) obj).mSelectionCriteria;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return 31 * mSelectionCriteria + mDescriptor.hashCode();
- }
- }
-
private interface KeyboardLayoutVisitor {
void visitKeyboardLayout(Resources resources,
int keyboardLayoutResId, KeyboardLayout layout);
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index f53b941..b8ae737 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -16,14 +16,18 @@
package com.android.server.input;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT;
+import static android.hardware.input.KeyboardLayoutSelectionResult.layoutSelectionCriteriaToString;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.role.RoleManager;
import android.content.Intent;
import android.hardware.input.KeyboardLayout;
+import android.hardware.input.KeyboardLayoutSelectionResult.LayoutSelectionCriteria;
import android.icu.util.ULocale;
import android.text.TextUtils;
import android.util.Log;
@@ -40,7 +44,6 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.policy.ModifierShortcutManager;
-import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -57,32 +60,6 @@
// (requires restart)
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- @Retention(SOURCE)
- @IntDef(prefix = {"LAYOUT_SELECTION_CRITERIA_"}, value = {
- LAYOUT_SELECTION_CRITERIA_UNSPECIFIED,
- LAYOUT_SELECTION_CRITERIA_USER,
- LAYOUT_SELECTION_CRITERIA_DEVICE,
- LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
- LAYOUT_SELECTION_CRITERIA_DEFAULT
- })
- public @interface LayoutSelectionCriteria {
- }
-
- /** Unspecified layout selection criteria */
- public static final int LAYOUT_SELECTION_CRITERIA_UNSPECIFIED = 0;
-
- /** Manual selection by user */
- public static final int LAYOUT_SELECTION_CRITERIA_USER = 1;
-
- /** Auto-detection based on device provided language tag and layout type */
- public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 2;
-
- /** Auto-detection based on IME provided language tag and layout type */
- public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 3;
-
- /** Default selection */
- public static final int LAYOUT_SELECTION_CRITERIA_DEFAULT = 4;
-
@VisibleForTesting
static final String DEFAULT_LAYOUT_NAME = "Default";
@@ -629,30 +606,16 @@
@Override
public String toString() {
- return "{keyboardLanguageTag = " + keyboardLanguageTag + " keyboardLayoutType = "
+ return "{keyboardLanguageTag = " + keyboardLanguageTag
+ + " keyboardLayoutType = "
+ KeyboardLayout.LayoutType.getLayoutNameFromValue(keyboardLayoutType)
- + " keyboardLayoutName = " + keyboardLayoutName + " layoutSelectionCriteria = "
- + getStringForSelectionCriteria(layoutSelectionCriteria)
- + "imeLanguageTag = " + imeLanguageTag + " imeLayoutType = "
- + KeyboardLayout.LayoutType.getLayoutNameFromValue(imeLayoutType) + "}";
- }
- }
-
- private static String getStringForSelectionCriteria(
- @LayoutSelectionCriteria int layoutSelectionCriteria) {
- switch (layoutSelectionCriteria) {
- case LAYOUT_SELECTION_CRITERIA_UNSPECIFIED:
- return "LAYOUT_SELECTION_CRITERIA_UNSPECIFIED";
- case LAYOUT_SELECTION_CRITERIA_USER:
- return "LAYOUT_SELECTION_CRITERIA_USER";
- case LAYOUT_SELECTION_CRITERIA_DEVICE:
- return "LAYOUT_SELECTION_CRITERIA_DEVICE";
- case LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD:
- return "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD";
- case LAYOUT_SELECTION_CRITERIA_DEFAULT:
- return "LAYOUT_SELECTION_CRITERIA_DEFAULT";
- default:
- return "INVALID_CRITERIA";
+ + " keyboardLayoutName = " + keyboardLayoutName
+ + " layoutSelectionCriteria = "
+ + layoutSelectionCriteriaToString(layoutSelectionCriteria)
+ + " imeLanguageTag = " + imeLanguageTag
+ + " imeLayoutType = " + KeyboardLayout.LayoutType.getLayoutNameFromValue(
+ imeLayoutType)
+ + "}";
}
}
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index 8826e3d..73f1aad 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -91,10 +91,10 @@
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_SHOW_IME,
statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE,
- Objects.toString(mService.mCurFocusedWindow),
+ Objects.toString(mService.mImeBindingState.mFocusedWindow),
InputMethodDebug.softInputDisplayReasonToString(reason),
InputMethodDebug.softInputModeToString(
- mService.mCurFocusedWindowSoftInputMode));
+ mService.mImeBindingState.mFocusedWindowSoftInputMode));
}
mService.onShowHideSoftInputRequested(true /* show */, showInputToken, reason,
statsToken);
@@ -122,10 +122,10 @@
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_HIDE_IME,
statsToken != null ? statsToken.getTag() : ImeTracker.TOKEN_NONE,
- Objects.toString(mService.mCurFocusedWindow),
+ Objects.toString(mService.mImeBindingState.mFocusedWindow),
InputMethodDebug.softInputDisplayReasonToString(reason),
InputMethodDebug.softInputModeToString(
- mService.mCurFocusedWindowSoftInputMode));
+ mService.mImeBindingState.mFocusedWindowSoftInputMode));
}
mService.onShowHideSoftInputRequested(false /* show */, hideInputToken, reason,
statsToken);
@@ -207,7 +207,8 @@
@Override
public boolean removeImeScreenshot(int displayId) {
if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) {
- mService.onShowHideSoftInputRequested(false /* show */, mService.mCurFocusedWindow,
+ mService.onShowHideSoftInputRequested(false /* show */,
+ mService.mImeBindingState.mFocusedWindow,
REMOVE_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */);
return true;
}
diff --git a/services/core/java/com/android/server/inputmethod/ImeBindingState.java b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
new file mode 100644
index 0000000..4c20c3b
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
@@ -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.server.inputmethod;
+
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_NAME;
+import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
+
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+import android.view.inputmethod.EditorInfo;
+
+import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.server.wm.WindowManagerInternal;
+
+/**
+ * Stores information related to one active IME client on one display.
+ */
+final class ImeBindingState {
+
+ /**
+ * The last window token that we confirmed to be focused. This is always updated upon
+ * reports from the input method client. If the window state is already changed before the
+ * report is handled, this field just keeps the last value.
+ */
+ @Nullable
+ final IBinder mFocusedWindow;
+
+ /**
+ * {@link WindowManager.LayoutParams#softInputMode} of {@link #mFocusedWindow}.
+ *
+ * @see #mFocusedWindow
+ */
+ @SoftInputModeFlags
+ final int mFocusedWindowSoftInputMode;
+
+ /**
+ * The client by which {@link #mFocusedWindow} was reported. This gets updated whenever
+ * an
+ * IME-focusable window gained focus (without necessarily starting an input connection),
+ * while {@link InputMethodManagerService#mClient} only gets updated when we actually start an
+ * input connection.
+ *
+ * @see #mFocusedWindow
+ */
+ @Nullable
+ final ClientState mFocusedWindowClient;
+
+ /**
+ * The editor info by which {@link #mFocusedWindow} was reported. This differs from
+ * {@link InputMethodManagerService#mCurEditorInfo} the same way {@link #mFocusedWindowClient}
+ * differs from {@link InputMethodManagerService#mCurClient}.
+ *
+ * @see #mFocusedWindow
+ */
+ @Nullable
+ final EditorInfo mFocusedWindowEditorInfo;
+
+ void dumpDebug(ProtoOutputStream proto, WindowManagerInternal windowManagerInternal) {
+ proto.write(CUR_FOCUSED_WINDOW_NAME,
+ windowManagerInternal.getWindowName(mFocusedWindow));
+ proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE,
+ InputMethodDebug.softInputModeToString(mFocusedWindowSoftInputMode));
+ }
+
+ void dump(String prefix, Printer p) {
+ p.println(prefix + "mFocusedWindow()=" + mFocusedWindow);
+ p.println(prefix + "softInputMode=" + InputMethodDebug.softInputModeToString(
+ mFocusedWindowSoftInputMode));
+ p.println(prefix + "mFocusedWindowClient=" + mFocusedWindowClient);
+ }
+
+ static ImeBindingState newEmptyState() {
+ return new ImeBindingState(
+ /*focusedWindow=*/ null,
+ /*focusedWindowSoftInputMode=*/ SOFT_INPUT_STATE_UNSPECIFIED,
+ /*focusedWindowClient=*/ null,
+ /*focusedWindowEditorInfo=*/ null
+ );
+ }
+
+ ImeBindingState(@Nullable IBinder focusedWindow,
+ @SoftInputModeFlags int focusedWindowSoftInputMode,
+ @Nullable ClientState focusedWindowClient,
+ @Nullable EditorInfo focusedWindowEditorInfo) {
+ mFocusedWindow = focusedWindow;
+ mFocusedWindowSoftInputMode = focusedWindowSoftInputMode;
+ mFocusedWindowClient = focusedWindowClient;
+ mFocusedWindowEditorInfo = focusedWindowEditorInfo;
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index 743b8e3..cdfde87 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -548,7 +548,7 @@
}
}
// Fallback to the focused window for some edge cases (e.g. relaunching the activity)
- return mService.mCurFocusedWindow;
+ return mService.mImeBindingState.mFocusedWindow;
}
IBinder getWindowTokenFrom(ImeTargetWindowState windowState) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1564b2f..fee0342 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -28,7 +28,6 @@
import static android.server.inputmethod.InputMethodManagerServiceProto.BOUND_TO_METHOD;
import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_ATTRIBUTE;
import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_CLIENT;
-import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_NAME;
import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE;
import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_ID;
import static android.server.inputmethod.InputMethodManagerServiceProto.CUR_METHOD_ID;
@@ -423,6 +422,11 @@
private final ClientController mClientController;
/**
+ * Holds the current IME binding state info.
+ */
+ ImeBindingState mImeBindingState;
+
+ /**
* Set once the system is ready to run third party code.
*/
boolean mSystemReady;
@@ -482,13 +486,6 @@
private ClientState mCurClient;
/**
- * The last window token that we confirmed to be focused. This is always updated upon reports
- * from the input method client. If the window state is already changed before the report is
- * handled, this field just keeps the last value.
- */
- IBinder mCurFocusedWindow;
-
- /**
* The last window token that we confirmed that IME started talking to. This is always updated
* upon reports from the input method. If the window state is already changed before the report
* is handled, this field just keeps the last value.
@@ -496,34 +493,6 @@
IBinder mLastImeTargetWindow;
/**
- * {@link LayoutParams#softInputMode} of {@link #mCurFocusedWindow}.
- *
- * @see #mCurFocusedWindow
- */
- @SoftInputModeFlags
- int mCurFocusedWindowSoftInputMode;
-
- /**
- * The client by which {@link #mCurFocusedWindow} was reported. This gets updated whenever an
- * IME-focusable window gained focus (without necessarily starting an input connection),
- * while {@link #mCurClient} only gets updated when we actually start an input connection.
- *
- * @see #mCurFocusedWindow
- */
- @Nullable
- ClientState mCurFocusedWindowClient;
-
- /**
- * The editor info by which {@link #mCurFocusedWindow} was reported. This differs from
- * {@link #mCurEditorInfo} the same way {@link #mCurFocusedWindowClient} differs
- * from {@link #mCurClient}.
- *
- * @see #mCurFocusedWindow
- */
- @Nullable
- EditorInfo mCurFocusedWindowEditorInfo;
-
- /**
* The {@link IRemoteInputConnection} last provided by the current client.
*/
IRemoteInputConnection mCurInputConnection;
@@ -1131,10 +1100,11 @@
mVisibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard(
accessibilitySoftKeyboardSetting);
if (mVisibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) {
- hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE);
} else if (isShowRequestedForCurrentWindow()) {
- showCurrentInputLocked(mCurFocusedWindow, InputMethodManager.SHOW_IMPLICIT,
+ showCurrentInputLocked(mImeBindingState.mFocusedWindow,
+ InputMethodManager.SHOW_IMPLICIT,
SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE);
}
} else if (stylusHandwritingEnabledUri.equals(uri)) {
@@ -1629,7 +1599,7 @@
}
// Hide soft input before user switch task since switch task may block main handler a while
// and delayed the hideCurrentInputLocked().
- hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_SWITCH_USER);
final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
clientToBeReset);
@@ -1704,6 +1674,7 @@
mClientController = new ClientController(mPackageManagerInternal);
synchronized (ImfLock.class) {
mClientController.addClientControllerCallback(c -> onClientRemoved(c));
+ mImeBindingState = ImeBindingState.newEmptyState();
}
mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
@@ -2218,7 +2189,7 @@
clearClientSessionLocked(client);
clearClientSessionForAccessibilityLocked(client);
if (mCurClient == client) {
- hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
if (mBoundToMethod) {
mBoundToMethod = false;
@@ -2232,9 +2203,8 @@
}
mBoundToAccessibility = false;
mCurClient = null;
- if (mCurFocusedWindowClient == client) {
- mCurFocusedWindowClient = null;
- mCurFocusedWindowEditorInfo = null;
+ if (mImeBindingState.mFocusedWindowClient == client) {
+ mImeBindingState = ImeBindingState.newEmptyState();
}
}
}
@@ -2283,7 +2253,7 @@
@GuardedBy("ImfLock.class")
void onUnbindCurrentMethodByReset() {
final ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull(
- mCurFocusedWindow);
+ mImeBindingState.mFocusedWindow);
if (winState != null && !winState.isRequestedImeVisible()
&& !mVisibilityStateComputer.isInputShown()) {
// Normally, the focus window will apply the IME visibility state to
@@ -2295,7 +2265,8 @@
// binding states in the first place.
final var statsToken = createStatsTokenForFocusedClient(false /* show */,
SoftInputShowHideReason.UNBIND_CURRENT_METHOD);
- mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, statsToken, STATE_HIDE_IME);
+ mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken,
+ STATE_HIDE_IME);
}
}
@@ -2328,7 +2299,7 @@
@GuardedBy("ImfLock.class")
private boolean isShowRequestedForCurrentWindow() {
final ImeTargetWindowState state = mVisibilityStateComputer.getWindowStateOrNull(
- mCurFocusedWindow);
+ mImeBindingState.mFocusedWindow);
return state != null && state.isRequestedImeVisible();
}
@@ -2346,10 +2317,9 @@
getCurTokenLocked(),
mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting,
UserHandle.getUserId(mCurClient.mUid),
- mCurClient.mSelfReportedDisplayId,
- mCurFocusedWindow, mCurEditorInfo, mCurFocusedWindowSoftInputMode,
- getSequenceNumberLocked());
- mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
+ mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo,
+ mImeBindingState.mFocusedWindowSoftInputMode, getSequenceNumberLocked());
+ mImeTargetWindowMap.put(startInputToken, mImeBindingState.mFocusedWindow);
mStartInputHistory.addEntry(info);
// Seems that PackageManagerInternal#grantImplicitAccess() doesn't handle cross-user
@@ -2376,7 +2346,7 @@
: createStatsTokenForFocusedClient(true /* show */,
SoftInputShowHideReason.ATTACH_NEW_INPUT);
mCurStatsToken = null;
- showCurrentInputLocked(mCurFocusedWindow, statsToken,
+ showCurrentInputLocked(mImeBindingState.mFocusedWindow, statsToken,
mVisibilityStateComputer.getShowFlags(), MotionEvent.TOOL_TYPE_UNKNOWN,
null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
@@ -2450,7 +2420,7 @@
// Compute the final shown display ID with validated cs.selfReportedDisplayId for this
// session & other conditions.
ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull(
- mCurFocusedWindow);
+ mImeBindingState.mFocusedWindow);
if (winState == null) {
return InputBindResult.NOT_IME_TARGET_WINDOW;
}
@@ -2469,7 +2439,7 @@
}
if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) {
- hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE);
return InputBindResult.NO_IME;
}
@@ -3638,7 +3608,8 @@
Binder.withCleanCallingIdentity(() -> {
Objects.requireNonNull(windowToken, "windowToken must not be null");
synchronized (ImfLock.class) {
- if (mCurFocusedWindow != windowToken || mCurPerceptible == perceptible) {
+ if (mImeBindingState.mFocusedWindow != windowToken
+ || mCurPerceptible == perceptible) {
return;
}
mCurPerceptible = perceptible;
@@ -3732,7 +3703,7 @@
super.hideSoftInputFromServerForTest_enforcePermission();
synchronized (ImfLock.class) {
- hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_SOFT_INPUT);
}
}
@@ -3907,7 +3878,8 @@
final boolean shouldClearFlag =
mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.mUid);
final boolean showForced = mVisibilityStateComputer.mShowForced;
- if (mCurFocusedWindow != windowToken && showForced && shouldClearFlag) {
+ if (mImeBindingState.mFocusedWindow != windowToken
+ && showForced && shouldClearFlag) {
mVisibilityStateComputer.mShowForced = false;
}
@@ -3925,7 +3897,7 @@
Slog.w(TAG, "If you need to impersonate a foreground user/profile from"
+ " a background user, use EditorInfo.targetInputMethodUser with"
+ " INTERACT_ACROSS_USERS_FULL permission.");
- hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_INVALID_USER);
return InputBindResult.INVALID_USER;
}
@@ -3986,7 +3958,7 @@
+ " cs=" + cs);
}
- final boolean sameWindowFocused = mCurFocusedWindow == windowToken;
+ final boolean sameWindowFocused = mImeBindingState.mFocusedWindow == windowToken;
final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
final boolean startInputByWinGainedFocus =
(startInputFlags & StartInputFlags.WINDOW_GAINED_FOCUS) != 0;
@@ -4017,10 +3989,7 @@
null, null, null, null, -1, false);
}
- mCurFocusedWindow = windowToken;
- mCurFocusedWindowSoftInputMode = softInputMode;
- mCurFocusedWindowClient = cs;
- mCurFocusedWindowEditorInfo = editorInfo;
+ mImeBindingState = new ImeBindingState(windowToken, softInputMode, cs, editorInfo);
mCurPerceptible = true;
// We want to start input before showing the IME, but after closing
@@ -4051,9 +4020,8 @@
break;
}
final var statsToken = createStatsTokenForFocusedClient(isShow, imeVisRes.getReason());
- mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, statsToken,
+ mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken,
imeVisRes.getState(), imeVisRes.getReason());
-
if (imeVisRes.getReason() == SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW) {
// If focused display changed, we should unbind current method
// to make app window in previous display relayout after Ime
@@ -4092,7 +4060,7 @@
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN);
- if (!isImeClientFocused(mCurFocusedWindow, cs)) {
+ if (!isImeClientFocused(mImeBindingState.mFocusedWindow, cs)) {
Slog.w(TAG, String.format("Ignoring %s of uid %d : %s", methodName, uid, client));
return false;
}
@@ -4104,8 +4072,8 @@
@GuardedBy("ImfLock.class")
private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
final int uid = Binder.getCallingUid();
- if (mCurFocusedWindowClient != null && client != null
- && mCurFocusedWindowClient.mClient.asBinder() == client.asBinder()) {
+ if (mImeBindingState.mFocusedWindowClient != null && client != null
+ && mImeBindingState.mFocusedWindowClient.mClient.asBinder() == client.asBinder()) {
return true;
}
if (mSettings.getUserId() != UserHandle.getUserId(uid)) {
@@ -4728,12 +4696,11 @@
proto.write(CUR_METHOD_ID, getSelectedMethodIdLocked());
proto.write(CUR_SEQ, getSequenceNumberLocked());
proto.write(CUR_CLIENT, Objects.toString(mCurClient));
- proto.write(CUR_FOCUSED_WINDOW_NAME,
- mWindowManagerInternal.getWindowName(mCurFocusedWindow));
+ mImeBindingState.dumpDebug(proto, mWindowManagerInternal);
proto.write(LAST_IME_TARGET_WINDOW_NAME,
mWindowManagerInternal.getWindowName(mLastImeTargetWindow));
- proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE,
- InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode));
+ proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE, InputMethodDebug.softInputModeToString(
+ mImeBindingState.mFocusedWindowSoftInputMode));
if (mCurEditorInfo != null) {
mCurEditorInfo.dumpDebug(proto, CUR_ATTRIBUTE);
}
@@ -4855,12 +4822,12 @@
final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(requestImeToken);
final WindowManagerInternal.ImeTargetInfo info =
mWindowManagerInternal.onToggleImeRequested(
- show, mCurFocusedWindow, requestToken, mCurTokenDisplayId);
+ show, mImeBindingState.mFocusedWindow, requestToken, mCurTokenDisplayId);
mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
- mCurFocusedWindowClient, mCurFocusedWindowEditorInfo, info.focusedWindowName,
- mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
- info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName,
- info.imeSurfaceParentName));
+ mImeBindingState.mFocusedWindowClient, mImeBindingState.mFocusedWindowEditorInfo,
+ info.focusedWindowName, mImeBindingState.mFocusedWindowSoftInputMode, reason,
+ mInFullscreenMode, info.requestWindowName, info.imeControlTargetName,
+ info.imeLayerTargetName, info.imeSurfaceParentName));
if (statsToken != null) {
mImeTrackerService.onImmsUpdate(statsToken, info.requestWindowName);
@@ -5031,8 +4998,7 @@
case MSG_HIDE_ALL_INPUT_METHODS:
synchronized (ImfLock.class) {
@SoftInputShowHideReason final int reason = (int) msg.obj;
- hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */, reason);
-
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, reason);
}
return true;
case MSG_REMOVE_IME_SURFACE: {
@@ -5051,7 +5017,7 @@
IBinder windowToken = (IBinder) msg.obj;
synchronized (ImfLock.class) {
try {
- if (windowToken == mCurFocusedWindow
+ if (windowToken == mImeBindingState.mFocusedWindow
&& mEnabledSession != null && mEnabledSession.mSession != null) {
mEnabledSession.mSession.removeImeSurface();
}
@@ -5126,7 +5092,7 @@
case MSG_START_HANDWRITING:
synchronized (ImfLock.class) {
IInputMethodInvoker curMethod = getCurMethodLocked();
- if (curMethod == null || mCurFocusedWindow == null) {
+ if (curMethod == null || mImeBindingState.mFocusedWindow == null) {
return true;
}
final HandwritingModeController.HandwritingSession session =
@@ -5134,7 +5100,7 @@
msg.arg1 /*requestId*/,
msg.arg2 /*pid*/,
mBindingController.getCurMethodUid(),
- mCurFocusedWindow);
+ mImeBindingState.mFocusedWindow);
if (session == null) {
Slog.e(TAG,
"Failed to start handwriting session for requestId: " + msg.arg1);
@@ -5187,11 +5153,11 @@
// Handle IME visibility when interactive changed before finishing the input to
// ensure we preserve the last state as possible.
final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged(
- mCurFocusedWindow, interactive);
+ mImeBindingState.mFocusedWindow, interactive);
if (imeVisRes != null) {
// Pass in a null statsToken as the IME snapshot is not tracked by ImeTracker.
- mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null /* statsToken */,
- imeVisRes.getState(), imeVisRes.getReason());
+ mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow,
+ null /* statsToken */, imeVisRes.getState(), imeVisRes.getReason());
}
// Eligible IME processes use new "setInteractive" protocol.
mCurClient.mClient.setInteractive(mIsInteractive, mInFullscreenMode);
@@ -5881,7 +5847,7 @@
@Override
public void reportImeControl(@Nullable IBinder windowToken) {
synchronized (ImfLock.class) {
- if (mCurFocusedWindow != windowToken) {
+ if (mImeBindingState.mFocusedWindow != windowToken) {
// mCurPerceptible was set by the focused window, but it is no longer in
// control, so we reset mCurPerceptible.
mCurPerceptible = true;
@@ -5895,7 +5861,7 @@
// Hide the IME method menu only when the IME surface parent is changed by the
// input target changed, in case seeing the dialog dismiss flickering during
// the next focused window starting the input connection.
- if (mLastImeTargetWindow != mCurFocusedWindow) {
+ if (mLastImeTargetWindow != mImeBindingState.mFocusedWindow) {
mMenuController.hideInputMethodMenuLocked();
}
}
@@ -6189,11 +6155,7 @@
client = mCurClient;
p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked());
p.println(" mCurPerceptible=" + mCurPerceptible);
- p.println(" mCurFocusedWindow=" + mCurFocusedWindow
- + " softInputMode="
- + InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)
- + " client=" + mCurFocusedWindowClient);
- focusedWindowClient = mCurFocusedWindowClient;
+ mImeBindingState.dump(" ", p);
p.println(" mCurId=" + getCurIdLocked() + " mHaveConnection=" + hasConnectionLocked()
+ " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
+ mBindingController.isVisibleBound());
@@ -6244,7 +6206,8 @@
p.println("No input method client.");
}
- if (focusedWindowClient != null && client != focusedWindowClient) {
+ if (mImeBindingState.mFocusedWindowClient != null
+ && client != mImeBindingState.mFocusedWindowClient) {
p.println(" ");
p.println("Warning: Current input method client doesn't match the last focused. "
+ "window.");
@@ -6252,7 +6215,8 @@
p.println(" ");
pw.flush();
try {
- TransferPipe.dumpAsync(focusedWindowClient.mClient.asBinder(), fd, args);
+ TransferPipe.dumpAsync(
+ mImeBindingState.mFocusedWindowClient.mClient.asBinder(), fd, args);
} catch (IOException | RemoteException e) {
p.println("Failed to dump input method client in focused window: " + e);
}
@@ -6698,7 +6662,7 @@
final String nextIme;
final List<InputMethodInfo> nextEnabledImes;
if (userId == mSettings.getUserId()) {
- hideCurrentInputLocked(mCurFocusedWindow, 0 /* flags */,
+ hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
mBindingController.unbindCurrentMethod();
@@ -6832,11 +6796,11 @@
@NonNull
private ImeTracker.Token createStatsTokenForFocusedClient(boolean show,
@SoftInputShowHideReason int reason) {
- final int uid = mCurFocusedWindowClient != null
- ? mCurFocusedWindowClient.mUid
+ final int uid = mImeBindingState.mFocusedWindowClient != null
+ ? mImeBindingState.mFocusedWindowClient.mUid
: -1;
- final var packageName = mCurFocusedWindowEditorInfo != null
- ? mCurFocusedWindowEditorInfo.packageName
+ final var packageName = mImeBindingState.mFocusedWindowEditorInfo != null
+ ? mImeBindingState.mFocusedWindowEditorInfo.packageName
: "uid(" + uid + ")";
return ImeTracker.forLogging().onStart(packageName, uid,
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 186cf5e..4bfd077 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1080,7 +1080,7 @@
reconciledPackages = ReconcilePackageUtils.reconcilePackages(
requests, Collections.unmodifiableMap(mPm.mPackages),
versionInfos, mSharedLibraries, mPm.mSettings.getKeySetManagerService(),
- mPm.mSettings);
+ mPm.mSettings, mPm.mInjector.getSystemConfig());
} catch (ReconcileFailure e) {
for (InstallRequest request : requests) {
request.setError("Reconciliation failed...", e);
@@ -3810,7 +3810,7 @@
mPm.mPackages, Collections.singletonMap(pkgName,
mPm.getSettingsVersionForPackage(parsedPackage)),
mSharedLibraries, mPm.mSettings.getKeySetManagerService(),
- mPm.mSettings);
+ mPm.mSettings, mPm.mInjector.getSystemConfig());
if ((scanFlags & SCAN_AS_APEX) == 0) {
appIdCreated = optimisticallyRegisterAppId(installRequest);
} else {
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 9a7916a..90d6adc 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY;
@@ -25,6 +26,7 @@
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
import static com.android.server.pm.PackageManagerService.TAG;
+import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
@@ -36,6 +38,7 @@
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.SystemConfig;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.utils.WatchedLongSparseArray;
@@ -53,14 +56,17 @@
* as install) led to the request.
*/
final class ReconcilePackageUtils {
- private static final boolean ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE = Build.IS_DEBUGGABLE || true;
+ // TODO(b/308573259): with allow-list, we should be able to disallow such installs even in
+ // debuggable builds.
+ private static final boolean ALLOW_NON_PRELOADS_SYSTEM_SHAREDUIDS = Build.IS_DEBUGGABLE
+ || !Flags.restrictNonpreloadsSystemShareduids();
public static List<ReconciledPackage> reconcilePackages(
List<InstallRequest> installRequests,
Map<String, AndroidPackage> allPackages,
Map<String, Settings.VersionInfo> versionInfos,
SharedLibrariesImpl sharedLibraries,
- KeySetManagerService ksms, Settings settings)
+ KeySetManagerService ksms, Settings settings, SystemConfig systemConfig)
throws ReconcileFailure {
final List<ReconciledPackage> result = new ArrayList<>(installRequests.size());
@@ -187,11 +193,19 @@
SigningDetails.CertCapabilities.PERMISSION)) {
Slog.d(TAG, "Non-preload app associated with system signature: "
+ signatureCheckPs.getPackageName());
- if (!ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE) {
- throw new ReconcileFailure(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- "Non-preload app associated with system signature: "
- + signatureCheckPs.getPackageName());
+ if (sharedUserSetting != null && !ALLOW_NON_PRELOADS_SYSTEM_SHAREDUIDS) {
+ // Check the allow-list.
+ var allowList = systemConfig.getPackageToSharedUidAllowList();
+ var sharedUidName = allowList.get(signatureCheckPs.getPackageName());
+ if (sharedUidName == null
+ || !sharedUserSetting.name.equals(sharedUidName)) {
+ var msg = "Non-preload app " + signatureCheckPs.getPackageName()
+ + " signed with platform signature and joining shared uid: "
+ + sharedUserSetting.name;
+ Slog.e(TAG, msg + ", allowList: " + allowList);
+ throw new ReconcileFailure(
+ INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID, msg);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 47032ea..754b141 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -408,6 +408,9 @@
/**
* Gets the permission states for requested package, persistent device and user.
+ * <p>
+ * <strong>Note: </strong>Default device permissions are not inherited in this API. Returns the
+ * exact permission states for the requested device.
*
* @param packageName name of the package you are checking against
* @param deviceId id of the persistent device you are checking against
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 32a21c5..cebf7fb 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -21,7 +21,6 @@
import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
import static android.app.AppOpsManager.OP_NONE;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
@@ -148,7 +147,7 @@
pkg.hasPreserveLegacyExternalStorage();
targetSDK = getMinimumTargetSDK(context, appInfo, user);
- shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ shouldApplyRestriction = !isWhiteListed;
isForcedScopedStorage = sForcedScopedStorageAppWhitelist
.contains(appInfo.packageName);
} else {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 7d5aa96d..d30a216 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -553,7 +553,6 @@
boolean launchFailed; // set if a launched failed, to abort on 2nd try
boolean delayedResume; // not yet resumed because of stopped app switches?
boolean finishing; // activity in pending finish list?
- int configChangeFlags; // which config values have changed
private boolean keysPaused; // has key dispatching been paused for it?
int launchMode; // the launch mode activity attribute.
int lockTaskLaunchMode; // the lockTaskMode manifest attribute, subject to override
@@ -1295,10 +1294,6 @@
if (mDeferHidingClient) {
pw.println(prefix + "mDeferHidingClient=" + mDeferHidingClient);
}
- if (configChangeFlags != 0) {
- pw.print(prefix); pw.print(" configChangeFlags=");
- pw.println(Integer.toHexString(configChangeFlags));
- }
if (mServiceConnectionsHolder != null) {
pw.print(prefix); pw.print("connections="); pw.println(mServiceConnectionsHolder);
}
@@ -4064,7 +4059,7 @@
try {
if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + this);
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
- DestroyActivityItem.obtain(token, finishing, configChangeFlags));
+ DestroyActivityItem.obtain(token, finishing));
} catch (Exception e) {
// We can just ignore exceptions here... if the process has crashed, our death
// notification will clean things up.
@@ -4106,8 +4101,6 @@
}
}
- configChangeFlags = 0;
-
return removedFromHistory;
}
@@ -5026,7 +5019,7 @@
return PauseActivityItem.obtain(token);
case STOPPING:
case STOPPED:
- return StopActivityItem.obtain(token, configChangeFlags);
+ return StopActivityItem.obtain(token);
default:
// Do not send a result immediately if the activity is in state INITIALIZING,
// RESTARTING_PROCESS, FINISHING, DESTROYING, or DESTROYED.
@@ -6300,7 +6293,7 @@
try {
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
PauseActivityItem.obtain(token, finishing, false /* userLeaving */,
- configChangeFlags, false /* dontReport */, mAutoEnteringPip));
+ false /* dontReport */, mAutoEnteringPip));
} catch (Exception e) {
Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e);
}
@@ -6614,7 +6607,7 @@
EventLogTags.writeWmStopActivity(
mUserId, System.identityHashCode(this), shortComponentName);
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
- StopActivityItem.obtain(token, configChangeFlags));
+ StopActivityItem.obtain(token));
mAtmService.mH.postDelayed(mStopTimeoutRunnable, STOP_TIMEOUT);
} catch (Exception e) {
@@ -9858,7 +9851,6 @@
if (shouldRelaunchLocked(changes, mTmpConfig)) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
- configChangeFlags |= changes;
if (mVisible && mAtmService.mTmpUpdateConfigurationResult.mIsUpdating
&& !mTransitionController.isShellTransitionsEnabled()) {
startFreezingScreenLocked(app, mAtmService.mTmpUpdateConfigurationResult.changes);
@@ -9887,7 +9879,7 @@
ProtoLog.v(WM_DEBUG_STATES, "Config is relaunching invisible "
+ "activity %s called by %s", this, Debug.getCallers(4));
}
- relaunchActivityLocked(preserveWindow);
+ relaunchActivityLocked(preserveWindow, changes);
// All done... tell the caller we weren't able to keep this activity around.
return false;
@@ -10029,9 +10021,8 @@
| CONFIG_SCREEN_LAYOUT)) != 0;
}
- void relaunchActivityLocked(boolean preserveWindow) {
+ void relaunchActivityLocked(boolean preserveWindow, int configChangeFlags) {
if (mAtmService.mSuppressResizeConfigChanges && preserveWindow) {
- configChangeFlags = 0;
return;
}
if (!preserveWindow) {
@@ -10104,8 +10095,6 @@
// The activity may be waiting for stop, but that is no longer appropriate for it.
mTaskSupervisor.mStoppingActivities.remove(this);
-
- configChangeFlags = 0;
}
/**
@@ -10174,7 +10163,7 @@
// {@link ActivityTaskManagerService.activityStopped}).
try {
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
- StopActivityItem.obtain(token, 0 /* configChanges */));
+ StopActivityItem.obtain(token));
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown during restart " + this, e);
}
diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java
index 64d8c75..8680436 100644
--- a/services/core/java/com/android/server/wm/SnapshotCache.java
+++ b/services/core/java/com/android/server/wm/SnapshotCache.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
import android.annotation.Nullable;
-import android.hardware.HardwareBuffer;
import android.util.ArrayMap;
import android.window.TaskSnapshot;
@@ -93,10 +92,6 @@
if (entry != null) {
mAppIdMap.remove(entry.topApp);
mRunningCache.remove(id);
- final HardwareBuffer buffer = entry.snapshot.getHardwareBuffer();
- if (buffer != null) {
- buffer.close();
- }
}
}
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 85d81c4..78ababc 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1881,7 +1881,7 @@
mAtmService.getLifecycleManager().scheduleTransactionItem(prev.app.getThread(),
PauseActivityItem.obtain(prev.token, prev.finishing, userLeaving,
- prev.configChangeFlags, pauseImmediately, autoEnteringPip));
+ pauseImmediately, autoEnteringPip));
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 027337f..f5e1e41 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -113,8 +113,8 @@
/** Creates intent that is ot be invoked to cancel an in-progress UI session. */
public Intent createCancelIntent(IBinder requestId, String packageName) {
- return IntentFactory.createCancelUiIntent(requestId, /*shouldShowCancellationUi=*/ true,
- packageName);
+ return IntentFactory.createCancelUiIntent(mContext, requestId,
+ /*shouldShowCancellationUi=*/ true, packageName);
}
/**
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 12f4407..6aeb4fd5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -225,7 +225,7 @@
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingEnabled() && false) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
policyDefinition, userId)) {
return;
@@ -350,7 +350,7 @@
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingEnabled() && false) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
}
@@ -496,7 +496,7 @@
synchronized (mLock) {
PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingEnabled() && false) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
policyDefinition, UserHandle.USER_ALL)) {
return;
@@ -568,7 +568,7 @@
synchronized (mLock) {
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingEnabled() && false) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
decreasePolicySizeForAdmin(policyState, enforcingAdmin);
}
@@ -1892,7 +1892,7 @@
private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (Flags.devicePolicySizeTrackingEnabled() && false) {
+ if (Flags.devicePolicySizeTrackingInternalEnabled()) {
if (mAdminPolicySize != null) {
for (int i = 0; i < mAdminPolicySize.size(); i++) {
int userId = mAdminPolicySize.keyAt(i);
@@ -1916,7 +1916,7 @@
private void writeMaxPolicySizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
return;
}
serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
@@ -2081,7 +2081,7 @@
private void readMaxPolicySizeInner(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
- if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
return;
}
mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0f97f4a..80046b60 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -27,6 +27,7 @@
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUTOFILL;
@@ -83,7 +84,6 @@
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_TIME;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER;
-import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_VPN;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WALLPAPER;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI;
@@ -196,11 +196,11 @@
import static android.app.admin.DevicePolicyManager.STATUS_DEVICE_ADMIN_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.STATUS_HAS_DEVICE_OWNER;
import static android.app.admin.DevicePolicyManager.STATUS_HAS_PAIRED;
+import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_ONLY_SYSTEM_USER;
import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.STATUS_MANAGED_USERS_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyManager.STATUS_NONSYSTEM_USER_EXISTS;
import static android.app.admin.DevicePolicyManager.STATUS_NOT_SYSTEM_USER;
-import static android.app.admin.DevicePolicyManager.STATUS_HEADLESS_ONLY_SYSTEM_USER;
import static android.app.admin.DevicePolicyManager.STATUS_OK;
import static android.app.admin.DevicePolicyManager.STATUS_PROVISIONING_NOT_ALLOWED_FOR_NON_DEVELOPER_USERS;
import static android.app.admin.DevicePolicyManager.STATUS_SYSTEM_USER;
@@ -12062,7 +12062,7 @@
}
if (packageList != null) {
- if (!Flags.devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
for (String pkg : packageList) {
PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
}
@@ -13771,7 +13771,7 @@
return;
}
- if (!Flags.devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(accountType, "account type");
}
@@ -14385,7 +14385,7 @@
public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
throws SecurityException {
Objects.requireNonNull(packages, "packages is null");
- if (!Flags.devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
for (String pkg : packages) {
PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
}
@@ -24235,7 +24235,7 @@
@Override
public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
- if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
return;
}
CallerIdentity caller = getCallerIdentity(callerPackageName);
@@ -24247,7 +24247,7 @@
@Override
public int getMaxPolicyStorageLimit(String callerPackageName) {
- if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
return -1;
}
CallerIdentity caller = getCallerIdentity(callerPackageName);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e19f08c..9d95c5b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2774,9 +2774,12 @@
t.traceEnd();
// OnDevicePersonalizationSystemService
- t.traceBegin("StartOnDevicePersonalizationSystemService");
- mSystemServiceManager.startService(ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE_CLASS);
- t.traceEnd();
+ if (!com.android.server.flags.Flags.enableOdpFeatureGuard()
+ || SystemProperties.getBoolean("ro.system_settings.service.odp_enabled", true)) {
+ t.traceBegin("StartOnDevicePersonalizationSystemService");
+ mSystemServiceManager.startService(ON_DEVICE_PERSONALIZATION_SYSTEM_SERVICE_CLASS);
+ t.traceEnd();
+ }
// Profiling
if (android.server.Flags.telemetryApisService()) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 4b086b3..67df67f 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -227,25 +227,59 @@
if (isRequestedBySystemPackage) {
return@forEach
}
- val oldFlags = getPermissionFlags(appId, userId, permissionName)
- var newFlags = oldFlags andInv PermissionFlags.UPGRADE_EXEMPT
- val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
- newFlags =
- if (permission.isHardRestricted && !isExempt) {
- newFlags or PermissionFlags.RESTRICTION_REVOKED
- } else {
- newFlags andInv PermissionFlags.RESTRICTION_REVOKED
- }
- newFlags =
- if (permission.isSoftRestricted && !isExempt) {
- newFlags or PermissionFlags.SOFT_RESTRICTED
- } else {
- newFlags andInv PermissionFlags.SOFT_RESTRICTED
- }
- setPermissionFlags(appId, userId, permissionName, newFlags)
+ updatePermissionExemptFlags(
+ appId,
+ userId,
+ permission,
+ PermissionFlags.UPGRADE_EXEMPT,
+ 0
+ )
}
}
+ fun MutateStateScope.updatePermissionExemptFlags(
+ appId: Int,
+ userId: Int,
+ permission: Permission,
+ exemptFlagMask: Int,
+ exemptFlagValues: Int
+ ) {
+ val permissionName = permission.name
+ val oldFlags = getPermissionFlags(appId, userId, permissionName)
+ var newFlags = (oldFlags andInv exemptFlagMask) or (exemptFlagValues and exemptFlagMask)
+ if (oldFlags == newFlags) {
+ return
+ }
+ val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
+ if (permission.isHardRestricted && !isExempt) {
+ newFlags = newFlags or PermissionFlags.RESTRICTION_REVOKED
+ // If the permission was policy fixed as granted but it is no longer on any of the
+ // allowlists we need to clear the policy fixed flag as allowlisting trumps policy i.e.
+ // policy cannot grant a non grantable permission.
+ if (PermissionFlags.isPermissionGranted(oldFlags)) {
+ newFlags = newFlags andInv PermissionFlags.POLICY_FIXED
+ }
+ } else {
+ newFlags = newFlags andInv PermissionFlags.RESTRICTION_REVOKED
+ }
+ newFlags =
+ if (
+ permission.isSoftRestricted && !isExempt &&
+ !anyPackageInAppId(appId) {
+ permissionName in it.androidPackage!!.requestedPermissions &&
+ isSoftRestrictedPermissionExemptForPackage(it, permissionName)
+ }
+ ) {
+ newFlags or PermissionFlags.SOFT_RESTRICTED
+ } else {
+ newFlags andInv PermissionFlags.SOFT_RESTRICTED
+ }
+ if (oldFlags == newFlags) {
+ return
+ }
+ setPermissionFlags(appId, userId, permissionName, newFlags)
+ }
+
override fun MutateStateScope.onPackageUninstalled(
packageName: String,
appId: Int,
@@ -1118,7 +1152,12 @@
newFlags andInv PermissionFlags.RESTRICTION_REVOKED
}
newFlags =
- if (permission.isSoftRestricted && !isExempt) {
+ if (
+ permission.isSoftRestricted && !isExempt &&
+ !requestingPackageStates.anyIndexed { _, it ->
+ isSoftRestrictedPermissionExemptForPackage(it, permissionName)
+ }
+ ) {
newFlags or PermissionFlags.SOFT_RESTRICTED
} else {
newFlags andInv PermissionFlags.SOFT_RESTRICTED
@@ -1398,6 +1437,17 @@
}
}
+ // See also SoftRestrictedPermissionPolicy.mayGrantPermission()
+ private fun isSoftRestrictedPermissionExemptForPackage(
+ packageState: PackageState,
+ permissionName: String
+ ): Boolean =
+ when (permissionName) {
+ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE ->
+ packageState.androidPackage!!.targetSdkVersion >= Build.VERSION_CODES.Q
+ else -> false
+ }
+
private inline fun MutateStateScope.anyPackageInAppId(
appId: Int,
state: AccessState = newState,
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
index 28889de..c5c921d 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
@@ -346,9 +346,18 @@
return flags.hasBits(RUNTIME_GRANTED)
}
- fun isAppOpGranted(flags: Int): Boolean =
- isPermissionGranted(flags) && !flags.hasBits(RESTRICTION_REVOKED) &&
- !flags.hasBits(APP_OP_REVOKED)
+ fun isAppOpGranted(flags: Int): Boolean {
+ if (!isPermissionGranted(flags)) {
+ return false
+ }
+ if (flags.hasAnyBit(MASK_RESTRICTED)) {
+ return false
+ }
+ if (flags.hasBits(APP_OP_REVOKED)) {
+ return false
+ }
+ return true
+ }
fun toApiFlags(flags: Int): Int {
var apiFlags = 0
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 0704c8f..1f65463 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -88,7 +88,6 @@
import com.android.server.pm.PackageManagerLocal
import com.android.server.pm.UserManagerInternal
import com.android.server.pm.UserManagerService
-import com.android.server.pm.parsing.pkg.AndroidPackageUtils
import com.android.server.pm.permission.LegacyPermission
import com.android.server.pm.permission.LegacyPermissionSettings
import com.android.server.pm.permission.LegacyPermissionState
@@ -97,7 +96,6 @@
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageState
-import com.android.server.policy.SoftRestrictedPermissionPolicy
import java.io.FileDescriptor
import java.io.PrintWriter
import java.util.concurrent.CompletableFuture
@@ -1006,25 +1004,14 @@
}
if (isGranted && oldFlags.hasBits(PermissionFlags.SOFT_RESTRICTED)) {
- // TODO: Refactor SoftRestrictedPermissionPolicy.
- val softRestrictedPermissionPolicy =
- SoftRestrictedPermissionPolicy.forPermission(
- context,
- AndroidPackageUtils.generateAppInfoWithoutState(androidPackage),
- androidPackage,
- UserHandle.of(userId),
- permissionName
+ if (reportError) {
+ Slog.e(
+ LOG_TAG,
+ "$methodName: Cannot grant soft-restricted non-exempt permission" +
+ " $permissionName to package $packageName"
)
- if (!softRestrictedPermissionPolicy.mayGrantPermission()) {
- if (reportError) {
- Slog.e(
- LOG_TAG,
- "$methodName: Cannot grant soft-restricted non-exempt permission" +
- " $permissionName to package $packageName"
- )
- }
- return
}
+ return
}
val newFlags = PermissionFlags.updateRuntimePermissionGranted(oldFlags, isGranted)
@@ -1135,25 +1122,23 @@
return emptyMap()
}
- val permissionFlagsMap =
- service.getState {
+ service.getState {
+ val permissionFlags =
if (deviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) {
with(policy) { getAllPermissionFlags(packageState.appId, userId) }
} else {
with(devicePolicy) {
getAllPermissionFlags(packageState.appId, deviceId, userId)
}
- }
+ } ?: return emptyMap()
+ val permissionStates = ArrayMap<String, PermissionState>()
+ permissionFlags.forEachIndexed { _, permissionName, flags ->
+ val granted = isPermissionGranted(packageState, userId, permissionName, deviceId)
+ val apiFlags = PermissionFlags.toApiFlags(flags)
+ permissionStates[permissionName] = PermissionState(granted, apiFlags)
}
- ?: return emptyMap()
-
- val permissionStates = ArrayMap<String, PermissionState>()
- permissionFlagsMap.forEachIndexed { _, permissionName, flags ->
- val granted = PermissionFlags.isPermissionGranted(flags)
- val apiFlags = PermissionFlags.toApiFlags(flags)
- permissionStates[permissionName] = PermissionState(granted, apiFlags)
+ return permissionStates
}
- return permissionStates
}
override fun isPermissionRevokedByPolicy(
@@ -1852,10 +1837,19 @@
allowlistedFlags: Int,
userId: Int
) {
+ var exemptMask = 0
+ if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM)) {
+ exemptMask = exemptMask or PermissionFlags.SYSTEM_EXEMPT
+ }
+ if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE)) {
+ exemptMask = exemptMask or PermissionFlags.UPGRADE_EXEMPT
+ }
+ if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) {
+ exemptMask = exemptMask or PermissionFlags.INSTALLER_EXEMPT
+ }
+
service.mutateState {
with(policy) {
- val permissionsFlags = getUidPermissionFlags(appId, userId) ?: return@mutateState
-
val permissions = getPermissions()
androidPackage.requestedPermissions.forEachIndexed { _, requestedPermission ->
val permission = permissions[requestedPermission]
@@ -1863,81 +1857,8 @@
return@forEachIndexed
}
- val oldFlags = permissionsFlags[requestedPermission] ?: 0
- val wasGranted = PermissionFlags.isPermissionGranted(oldFlags)
-
- var newFlags = oldFlags
- var mask = 0
- var allowlistFlagsCopy = allowlistedFlags
- while (allowlistFlagsCopy != 0) {
- val flag = 1 shl allowlistFlagsCopy.countTrailingZeroBits()
- allowlistFlagsCopy = allowlistFlagsCopy and flag.inv()
- when (flag) {
- PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM -> {
- mask = mask or PermissionFlags.SYSTEM_EXEMPT
- newFlags =
- if (permissionNames.contains(requestedPermission)) {
- newFlags or PermissionFlags.SYSTEM_EXEMPT
- } else {
- newFlags andInv PermissionFlags.SYSTEM_EXEMPT
- }
- }
- PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE -> {
- mask = mask or PermissionFlags.UPGRADE_EXEMPT
- newFlags =
- if (permissionNames.contains(requestedPermission)) {
- newFlags or PermissionFlags.UPGRADE_EXEMPT
- } else {
- newFlags andInv PermissionFlags.UPGRADE_EXEMPT
- }
- }
- PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER -> {
- mask = mask or PermissionFlags.INSTALLER_EXEMPT
- newFlags =
- if (permissionNames.contains(requestedPermission)) {
- newFlags or PermissionFlags.INSTALLER_EXEMPT
- } else {
- newFlags andInv PermissionFlags.INSTALLER_EXEMPT
- }
- }
- }
- }
-
- if (oldFlags == newFlags) {
- return@forEachIndexed
- }
-
- val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
-
- // If the permission is policy fixed as granted but it is no longer
- // on any of the allowlists we need to clear the policy fixed flag
- // as allowlisting trumps policy i.e. policy cannot grant a non
- // grantable permission.
- if (oldFlags.hasBits(PermissionFlags.POLICY_FIXED)) {
- if (!isExempt && wasGranted) {
- mask = mask or PermissionFlags.POLICY_FIXED
- newFlags = newFlags andInv PermissionFlags.POLICY_FIXED
- }
- }
-
- newFlags =
- if (permission.isHardRestricted && !isExempt) {
- newFlags or PermissionFlags.RESTRICTION_REVOKED
- } else {
- newFlags andInv PermissionFlags.RESTRICTION_REVOKED
- }
- newFlags =
- if (permission.isSoftRestricted && !isExempt) {
- newFlags or PermissionFlags.SOFT_RESTRICTED
- } else {
- newFlags andInv PermissionFlags.SOFT_RESTRICTED
- }
- mask =
- mask or
- PermissionFlags.RESTRICTION_REVOKED or
- PermissionFlags.SOFT_RESTRICTED
-
- updatePermissionFlags(appId, userId, requestedPermission, mask, newFlags)
+ var exemptFlags = if (requestedPermission in permissionNames) exemptMask else 0
+ updatePermissionExemptFlags(appId, userId, permission, exemptMask, exemptFlags)
}
}
}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
index cde46ab..96753b6 100644
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
@@ -233,24 +233,6 @@
.isEqualTo(expectedNewFlags)
}
- @Test
- fun testOnPackageInstalled_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
- val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.INSTALLER_EXEMPT
- testOnPackageInstalled(
- oldFlags,
- permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
- ) {}
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.INSTALLER_EXEMPT
- assertWithMessage(
- "After onPackageInstalled() is called for a non-system app that requests a runtime" +
- " soft restricted permission that is exempted. The actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
private fun testOnPackageInstalled(
oldFlags: Int,
permissionInfoFlags: Int = 0,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 6013063..da11e6a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -554,7 +554,7 @@
mRootWindowContainer.applySleepTokens(true);
// The display orientation should be changed by the activity so there is no relaunch.
- verify(activity, never()).relaunchActivityLocked(anyBoolean());
+ verify(activity, never()).relaunchActivityLocked(anyBoolean(), anyInt());
assertEquals(rotatedConfig.orientation, display.getConfiguration().orientation);
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 9792cdd..048b1b2 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1434,11 +1434,14 @@
}
/**
- * This API will return all {@link PhoneAccount}s registered via
- * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}. If a {@link PhoneAccount} appears
- * to be missing from the list, Telecom has either unregistered the {@link PhoneAccount}
- * or the caller registered the {@link PhoneAccount} under a different user and does not
- * have the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+ * This API will return all {@link PhoneAccount}s the caller registered via
+ * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}. If a {@link PhoneAccount} appears
+ * to be missing from the list, Telecom has either unregistered the {@link PhoneAccount} (for
+ * cleanup purposes) or the caller registered the {@link PhoneAccount} under a different user
+ * and does not have the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+ * <b>Note:</b> This API will only return {@link PhoneAccount}s registered by the same app. For
+ * system Dialers that need all the {@link PhoneAccount}s registered by every application, see
+ * {@link TelecomManager#getAllPhoneAccounts()}.
*
* @return all the {@link PhoneAccount}s registered by the caller.
*/
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index 7343ba1c..e60764f 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
+import android.hardware.input.KeyboardLayoutSelectionResult
import android.hardware.input.IInputManager
import android.hardware.input.InputManager
import android.hardware.input.InputManagerGlobal
@@ -525,13 +526,13 @@
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
ENGLISH_UK_LAYOUT_DESCRIPTOR
)
- val keyboardLayout =
+ assertEquals(
+ "Default UI: getKeyboardLayoutForInputDevice API should always return " +
+ "KeyboardLayoutSelectionResult.FAILED",
+ KeyboardLayoutSelectionResult.FAILED,
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
)
- assertNull(
- "Default UI: getKeyboardLayoutForInputDevice API should always return null",
- keyboardLayout
)
}
}
@@ -545,12 +546,14 @@
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
ENGLISH_UK_LAYOUT_DESCRIPTOR
)
- assertEquals(
- "New UI: getKeyboardLayoutForInputDevice API should return the set layout",
- ENGLISH_UK_LAYOUT_DESCRIPTOR,
+ var result =
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
)
+ assertEquals(
+ "New UI: getKeyboardLayoutForInputDevice API should return the set layout",
+ ENGLISH_UK_LAYOUT_DESCRIPTOR,
+ result.layoutDescriptor
)
// This should replace previously set layout
@@ -558,12 +561,14 @@
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
ENGLISH_US_LAYOUT_DESCRIPTOR
)
- assertEquals(
- "New UI: getKeyboardLayoutForInputDevice API should return the last set layout",
- ENGLISH_US_LAYOUT_DESCRIPTOR,
+ result =
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
)
+ assertEquals(
+ "New UI: getKeyboardLayoutForInputDevice API should return the last set layout",
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
+ result.layoutDescriptor
)
}
}
@@ -734,17 +739,20 @@
createImeSubtypeForLanguageTag("ru"),
createLayoutDescriptor("keyboard_layout_russian")
)
- assertNull(
- "New UI: getDefaultKeyboardLayoutForInputDevice should return null when no " +
- "layout available",
+ assertEquals(
+ "New UI: getDefaultKeyboardLayoutForInputDevice should return " +
+ "KeyboardLayoutSelectionResult.FAILED when no layout available",
+ KeyboardLayoutSelectionResult.FAILED,
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo,
createImeSubtypeForLanguageTag("it")
)
)
- assertNull(
- "New UI: getDefaultKeyboardLayoutForInputDevice should return null when no " +
- "layout for script code is available",
+ assertEquals(
+ "New UI: getDefaultKeyboardLayoutForInputDevice should return " +
+ "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" +
+ "available",
+ KeyboardLayoutSelectionResult.FAILED,
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo,
createImeSubtypeForLanguageTag("en-Deva")
@@ -811,8 +819,10 @@
createImeSubtypeForLanguageTagAndLayoutType("ru", ""),
createLayoutDescriptor("keyboard_layout_russian")
)
- assertNull("New UI: getDefaultKeyboardLayoutForInputDevice should return null when " +
- "no layout for script code is available",
+ assertEquals("New UI: getDefaultKeyboardLayoutForInputDevice should return " +
+ "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" +
+ "available",
+ KeyboardLayoutSelectionResult.FAILED,
keyboardLayoutManager.getKeyboardLayoutForInputDevice(
keyboardDevice.identifier, USER_ID, imeInfo,
createImeSubtypeForLanguageTagAndLayoutType("en-Deva-US", "")
@@ -865,14 +875,16 @@
ArgumentMatchers.anyBoolean(),
ArgumentMatchers.eq(keyboardDevice.vendorId),
ArgumentMatchers.eq(keyboardDevice.productId),
- ArgumentMatchers.eq(createByteArray(
+ ArgumentMatchers.eq(
+ createByteArray(
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
LAYOUT_TYPE_DEFAULT,
GERMAN_LAYOUT_NAME,
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
"de-Latn",
- LAYOUT_TYPE_QWERTZ),
+ LAYOUT_TYPE_QWERTZ
),
+ ),
ArgumentMatchers.eq(keyboardDevice.deviceBus),
)
}
@@ -893,13 +905,16 @@
ArgumentMatchers.anyBoolean(),
ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId),
ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId),
- ArgumentMatchers.eq(createByteArray(
+ ArgumentMatchers.eq(
+ createByteArray(
"en",
LAYOUT_TYPE_QWERTY,
ENGLISH_US_LAYOUT_NAME,
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
"de-Latn",
- LAYOUT_TYPE_QWERTZ)),
+ LAYOUT_TYPE_QWERTZ
+ )
+ ),
ArgumentMatchers.eq(keyboardDevice.deviceBus),
)
}
@@ -918,14 +933,16 @@
ArgumentMatchers.anyBoolean(),
ArgumentMatchers.eq(keyboardDevice.vendorId),
ArgumentMatchers.eq(keyboardDevice.productId),
- ArgumentMatchers.eq(createByteArray(
+ ArgumentMatchers.eq(
+ createByteArray(
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
LAYOUT_TYPE_DEFAULT,
"Default",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT,
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
- LAYOUT_TYPE_DEFAULT),
+ LAYOUT_TYPE_DEFAULT
),
+ ),
ArgumentMatchers.eq(keyboardDevice.deviceBus),
)
}
@@ -998,12 +1015,13 @@
imeSubtype: InputMethodSubtype,
expectedLayout: String
) {
+ val result = keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ device.identifier, USER_ID, imeInfo, imeSubtype
+ )
assertEquals(
"New UI: getDefaultKeyboardLayoutForInputDevice should return $expectedLayout",
expectedLayout,
- keyboardLayoutManager.getKeyboardLayoutForInputDevice(
- device.identifier, USER_ID, imeInfo, imeSubtype
- )
+ result.layoutDescriptor
)
}
diff --git a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
index 89a47b9..0615941 100644
--- a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
@@ -17,6 +17,7 @@
package com.android.server.input
import android.hardware.input.KeyboardLayout
+import android.hardware.input.KeyboardLayoutSelectionResult
import android.icu.util.ULocale
import android.platform.test.annotations.Presubmit
import android.view.InputDevice
@@ -120,15 +121,15 @@
val event = builder.addLayoutSelection(
createImeSubtype(1, ULocale.forLanguageTag("en-US"), "qwerty"),
"English(US)(Qwerty)",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
).addLayoutSelection(
createImeSubtype(2, ULocale.forLanguageTag("en-US"), "azerty"),
null, // Default layout type
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER
).addLayoutSelection(
createImeSubtype(3, ULocale.forLanguageTag("en-US"), "qwerty"),
"German",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE
).setIsFirstTimeConfiguration(true).build()
assertEquals(
@@ -158,7 +159,7 @@
"de-CH",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
"English(US)(Qwerty)",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
"en-US",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
)
@@ -167,7 +168,7 @@
"de-CH",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
KeyboardMetricsCollector.DEFAULT_LAYOUT_NAME,
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER,
"en-US",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"),
)
@@ -176,7 +177,7 @@
"de-CH",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
"German",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
"en-US",
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
)
@@ -197,7 +198,7 @@
val event = builder.addLayoutSelection(
createImeSubtype(4, null, "qwerty"), // Default language tag
"German",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE
).build()
assertExpectedLayoutConfiguration(
@@ -205,7 +206,7 @@
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"),
"German",
- KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE,
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
)
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index b054a57..b4e2758 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -15,12 +15,7 @@
//
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"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
toolSources = [
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
deleted file mode 100644
index 15ae2ba..0000000
--- a/tools/aapt2/Android.mk
+++ /dev/null
@@ -1,4 +0,0 @@
-include $(CLEAR_VARS)
-aapt2_results := ./out/soong/.intermediates/frameworks/base/tools/aapt2/aapt2_results
-$(call declare-1p-target,$(aapt2_results))
-aapt2_results :=