Merge "Add aconfig flag for the supervision role permission update." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index ad84900..6393fdb 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -72,6 +72,7 @@
"android.service.dreams.flags-aconfig-java",
"android.service.notification.flags-aconfig-java",
"android.service.appprediction.flags-aconfig-java",
+ "android.service.quickaccesswallet.flags-aconfig-java",
"android.service.voice.flags-aconfig-java",
"android.speech.flags-aconfig-java",
"android.systemserver.flags-aconfig-java",
@@ -111,6 +112,7 @@
"framework_graphics_flags_java_lib",
"hwui_flags_java_lib",
"interaction_jank_monitor_flags_lib",
+ "keystore2_flags_java-framework",
"libcore_exported_aconfig_flags_lib",
"libcore_readonly_aconfig_flags_lib",
"libgui_flags_java_lib",
@@ -1774,3 +1776,18 @@
aconfig_declarations: "aconfig_settingslib_flags",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Quick Access Wallet
+aconfig_declarations {
+ name: "android.service.quickaccesswallet.flags-aconfig",
+ package: "android.service.quickaccesswallet",
+ exportable: true,
+ container: "system",
+ srcs: ["core/java/android/service/quickaccesswallet/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.service.quickaccesswallet.flags-aconfig-java",
+ aconfig_declarations: "android.service.quickaccesswallet.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index dc7ccd4..faa24bc6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -56907,6 +56907,7 @@
ctor public EditorInfo();
method public int describeContents();
method public void dump(android.util.Printer, String);
+ method @FlaggedApi("android.view.inputmethod.public_autofill_id_in_editorinfo") @Nullable public android.view.autofill.AutofillId getAutofillId();
method @Nullable public CharSequence getInitialSelectedText(int);
method @Nullable public android.view.inputmethod.SurroundingText getInitialSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method @Nullable public CharSequence getInitialTextAfterCursor(@IntRange(from=0) int, int);
@@ -56916,6 +56917,7 @@
method @NonNull public java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>> getSupportedHandwritingGestures();
method @FlaggedApi("android.view.inputmethod.editorinfo_handwriting_enabled") public boolean isStylusHandwritingEnabled();
method public final void makeCompatible(int);
+ method @FlaggedApi("android.view.inputmethod.public_autofill_id_in_editorinfo") public void setAutofillId(@Nullable android.view.autofill.AutofillId);
method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
method public void setInitialSurroundingText(@NonNull CharSequence);
method public void setInitialToolType(int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 62c8a34..ed95fdd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1378,7 +1378,8 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int);
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
- method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
+ method @Deprecated @FlaggedApi("android.app.admin.flags.secondary_lockscreen_api_enabled") public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
+ method @FlaggedApi("android.app.admin.flags.secondary_lockscreen_api_enabled") public void setSecondaryLockscreenEnabled(boolean, @Nullable android.os.PersistableBundle);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification();
field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
@@ -12552,8 +12553,19 @@
}
@FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionManager {
+ method @NonNull public android.content.Intent createSupportIntent(@NonNull String, @Nullable String);
method @NonNull @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public java.util.List<android.security.advancedprotection.AdvancedProtectionFeature> getAdvancedProtectionFeatures();
method @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean);
+ field @FlaggedApi("android.security.aapm_api") public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG = "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
+ field public static final String EXTRA_SUPPORT_DIALOG_FEATURE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
+ field public static final String EXTRA_SUPPORT_DIALOG_TYPE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE";
+ field public static final String FEATURE_ID_DISALLOW_CELLULAR_2G = "android.security.advancedprotection.feature_disallow_2g";
+ field public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = "android.security.advancedprotection.feature_disallow_install_unknown_sources";
+ field public static final String FEATURE_ID_DISALLOW_USB = "android.security.advancedprotection.feature_disallow_usb";
+ field public static final String FEATURE_ID_DISALLOW_WEP = "android.security.advancedprotection.feature_disallow_wep";
+ field public static final String FEATURE_ID_ENABLE_MTE = "android.security.advancedprotection.feature_enable_mte";
+ field public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION = "android.security.advancedprotection.type_blocked_interaction";
+ field public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING = "android.security.advancedprotection.type_disabled_setting";
}
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 0654ac2..9bb16ae 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -124,7 +124,7 @@
boolean onlyHasDefaultChannel(String pkg, int uid);
boolean areChannelsBypassingDnd();
ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int uid);
- List<String> getPackagesBypassingDnd(int userId, boolean includeConversationChannels);
+ ParceledListSlice getPackagesBypassingDnd(int userId);
boolean isPackagePaused(String pkg);
void deleteNotificationHistoryItem(String pkg, int uid, long postedTime);
boolean isPermissionFixed(String pkg, int userId);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0381ee0..3d9c55c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5003,7 +5003,7 @@
/**
* Sets a very short string summarizing the most critical information contained in the
- * notification. Suggested max length is 5 characters, and there is no guarantee how much or
+ * notification. Suggested max length is 7 characters, and there is no guarantee how much or
* how little of this text will be shown.
*/
@FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index a458b4e..f702b85 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -174,22 +174,54 @@
}
/**
- * Apply the registered library paths to the passed impl object
- * @return the hash code for the current version of the registered paths
+ * Apply the registered library paths to the passed AssetManager. If may create a new
+ * AssetManager if any changes are needed and it isn't allowed to reuse the old one.
+ *
+ * @return new AssetManager and the hash code for the current version of the registered paths
*/
- public int updateResourceImplWithRegisteredLibs(@NonNull ResourcesImpl impl) {
+ public @NonNull Pair<AssetManager, Integer> updateResourceImplAssetsWithRegisteredLibs(
+ @NonNull AssetManager assets, boolean reuseAssets) {
if (!Flags.registerResourcePaths()) {
- return 0;
+ return new Pair<>(assets, 0);
}
- final var collector = new PathCollector(null);
- final int size = mSharedLibAssetsMap.size();
- for (int i = 0; i < size; i++) {
- final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
- collector.appendKey(libraryKey);
+ final int size;
+ final PathCollector collector;
+
+ synchronized (mLock) {
+ size = mSharedLibAssetsMap.size();
+ if (assets == AssetManager.getSystem()) {
+ return new Pair<>(assets, size);
+ }
+ collector = new PathCollector(resourcesKeyFromAssets(assets));
+ for (int i = 0; i < size; i++) {
+ final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey();
+ collector.appendKey(libraryKey);
+ }
}
- impl.getAssets().addPresetApkKeys(extractApkKeys(collector.collectedKey()));
- return size;
+ if (collector.isSameAsOriginal()) {
+ return new Pair<>(assets, size);
+ }
+ if (reuseAssets) {
+ assets.addPresetApkKeys(extractApkKeys(collector.collectedKey()));
+ return new Pair<>(assets, size);
+ }
+ final var newAssetsBuilder = new AssetManager.Builder();
+ for (final var asset : assets.getApkAssets()) {
+ if (!asset.isForLoader()) {
+ newAssetsBuilder.addApkAssets(asset);
+ }
+ }
+ for (final var key : extractApkKeys(collector.collectedKey())) {
+ try {
+ final var asset = loadApkAssets(key);
+ newAssetsBuilder.addApkAssets(asset);
+ } catch (IOException e) {
+ Log.e(TAG, "Couldn't load assets for key " + key, e);
+ }
+ }
+ assets.getLoaders().forEach(newAssetsBuilder::addLoader);
+ return new Pair<>(newAssetsBuilder.build(), size);
}
public static class ApkKey {
@@ -624,6 +656,23 @@
return apkKeys;
}
+ private ResourcesKey resourcesKeyFromAssets(@NonNull AssetManager assets) {
+ final var libs = new ArrayList<String>();
+ final var overlays = new ArrayList<String>();
+ for (final ApkAssets asset : assets.getApkAssets()) {
+ if (asset.isSystem() || asset.isForLoader()) {
+ continue;
+ }
+ if (asset.isOverlay()) {
+ overlays.add(asset.getAssetPath());
+ } else if (asset.isSharedLib()) {
+ libs.add(asset.getAssetPath());
+ }
+ }
+ return new ResourcesKey(null, null, overlays.toArray(new String[0]),
+ libs.toArray(new String[0]), 0, null, null);
+ }
+
/**
* Creates an AssetManager from the paths within the ResourcesKey.
*
@@ -752,7 +801,7 @@
final Configuration config = generateConfig(key);
final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj);
- final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj);
+ final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj, true);
if (DEBUG) {
Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
@@ -1835,31 +1884,32 @@
for (int i = 0; i < resourcesCount; i++) {
final WeakReference<Resources> ref = mAllResourceReferences.get(i);
final Resources r = ref != null ? ref.get() : null;
- if (r != null) {
- final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
- if (key != null) {
- final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
- if (impl == null) {
- throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
- }
- r.setImpl(impl);
- } else {
- // ResourcesKey is null which means the ResourcesImpl could belong to a
- // Resources created by application through Resources constructor and was not
- // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to
- // have shared library asset paths appended if there are any.
- if (r.getImpl() != null) {
- final ResourcesImpl oldImpl = r.getImpl();
- final AssetManager oldAssets = oldImpl.getAssets();
- // ResourcesImpl constructor will help to append shared library asset paths.
- if (oldAssets != AssetManager.getSystem() && oldAssets.isUpToDate()) {
- final ResourcesImpl newImpl = new ResourcesImpl(oldAssets,
- oldImpl.getMetrics(), oldImpl.getConfiguration(),
- oldImpl.getDisplayAdjustments());
+ if (r == null) {
+ continue;
+ }
+ final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
+ if (key != null) {
+ final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key);
+ if (impl == null) {
+ throw new Resources.NotFoundException("failed to redirect ResourcesImpl");
+ }
+ r.setImpl(impl);
+ } else {
+ // ResourcesKey is null which means the ResourcesImpl could belong to a
+ // Resources created by application through Resources constructor and was not
+ // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to
+ // have shared library asset paths appended if there are any.
+ final ResourcesImpl oldImpl = r.getImpl();
+ if (oldImpl != null) {
+ final AssetManager oldAssets = oldImpl.getAssets();
+ // ResourcesImpl constructor will help to append shared library asset paths.
+ if (oldAssets != AssetManager.getSystem()) {
+ if (oldAssets.isUpToDate()) {
+ final ResourcesImpl newImpl = new ResourcesImpl(oldImpl);
r.setImpl(newImpl);
} else {
- Slog.w(TAG, "Skip appending shared library asset paths for the "
- + "Resource as its assets are not up to date.");
+ Slog.w(TAG, "Skip appending shared library asset paths for "
+ + "the Resources as its assets are not up to date.");
}
}
}
diff --git a/core/java/android/app/ZenBypassingApp.java b/core/java/android/app/ZenBypassingApp.java
new file mode 100644
index 0000000..89bcfa2
--- /dev/null
+++ b/core/java/android/app/ZenBypassingApp.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public final class ZenBypassingApp implements Parcelable {
+
+ @NonNull private String mPkg;
+ private boolean mAllChannelsBypass;
+
+
+ public ZenBypassingApp(@NonNull String pkg, boolean allChannelsBypass) {
+ mPkg = pkg;
+ mAllChannelsBypass = allChannelsBypass;
+ }
+
+ public ZenBypassingApp(Parcel source) {
+ mPkg = source.readString();
+ mAllChannelsBypass = source.readBoolean();
+ }
+
+ @NonNull
+ public String getPkg() {
+ return mPkg;
+ }
+
+ public boolean doAllChannelsBypass() {
+ return mAllChannelsBypass;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mPkg);
+ dest.writeBoolean(mAllChannelsBypass);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<ZenBypassingApp> CREATOR
+ = new Parcelable.Creator<ZenBypassingApp>() {
+ @Override
+ public ZenBypassingApp createFromParcel(Parcel source) {
+ return new ZenBypassingApp(source);
+ }
+ @Override
+ public ZenBypassingApp[] newArray(int size) {
+ return new ZenBypassingApp[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ZenBypassingApp)) return false;
+ ZenBypassingApp that = (ZenBypassingApp) o;
+ return mAllChannelsBypass == that.mAllChannelsBypass && Objects.equals(mPkg,
+ that.mPkg);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPkg, mAllChannelsBypass);
+ }
+
+ @Override
+ public String toString() {
+ return "ZenBypassingApp{" +
+ "mPkg='" + mPkg + '\'' +
+ ", mAllChannelsBypass=" + mAllChannelsBypass +
+ '}';
+ }
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6939bb6..bff77f9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -58,6 +58,7 @@
import static android.app.admin.flags.Flags.FLAG_REMOVE_MANAGED_PROFILE_ENABLED;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
+import static android.app.admin.flags.Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -12642,28 +12643,43 @@
* @param enabled Whether or not the lockscreen needs to be shown.
* @throws SecurityException if {@code admin} is not a device or profile owner.
* @see #isSecondaryLockscreenEnabled
+ * @deprecated Use {@link #setSecondaryLockscreenEnabled(boolean,PersistableBundle)} instead.
* @hide
- **/
+ */
+ @Deprecated
@SystemApi
+ @FlaggedApi(FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
public void setSecondaryLockscreenEnabled(@NonNull ComponentName admin, boolean enabled) {
- setSecondaryLockscreenEnabled(admin, enabled, null);
+ throwIfParentInstance("setSecondaryLockscreenEnabled");
+ if (mService != null) {
+ try {
+ mService.setSecondaryLockscreenEnabled(admin, enabled, null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
/**
* Called by the system supervision app to set whether a secondary lockscreen needs to be shown.
*
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
- * caller is not a device admin.
+ * <p>The secondary lockscreen will by displayed after the primary keyguard security screen
+ * requirements are met.
+ *
+ * <p>This API, and associated APIs, can only be called by the default supervision app.
+ *
* @param enabled Whether or not the lockscreen needs to be shown.
* @param options A {@link PersistableBundle} to supply options to the lock screen.
* @hide
*/
- public void setSecondaryLockscreenEnabled(@Nullable ComponentName admin, boolean enabled,
+ @SystemApi
+ @FlaggedApi(FLAG_SECONDARY_LOCKSCREEN_API_ENABLED)
+ public void setSecondaryLockscreenEnabled(boolean enabled,
@Nullable PersistableBundle options) {
throwIfParentInstance("setSecondaryLockscreenEnabled");
if (mService != null) {
try {
- mService.setSecondaryLockscreenEnabled(admin, enabled, options);
+ mService.setSecondaryLockscreenEnabled(null, enabled, options);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 5f868be..404471e 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -381,3 +381,11 @@
description: "Split up existing create and provision managed profile API."
bug: "375382324"
}
+
+flag {
+ name: "secondary_lockscreen_api_enabled"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Add new API for secondary lockscreen"
+ bug: "336297680"
+}
diff --git a/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig b/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
index 8b6441a..74a96c8 100644
--- a/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
+++ b/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
@@ -8,3 +8,10 @@
description: "Make methods on OnDeviceIntelligenceManager available for local inference."
bug: "304755128"
}
+flag {
+ name: "enable_on_device_intelligence_module"
+ is_exported: true
+ namespace: "ondeviceintelligence"
+ description: "Enable migration to mainline module and related changes."
+ bug: "376427781"
+}
\ No newline at end of file
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b776b59..8853304 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -87,7 +87,6 @@
import android.util.Log;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.modules.expresslog.Counter;
@@ -12304,7 +12303,6 @@
}
/** @hide */
- @VisibleForTesting
public Set<NestedIntentKey> getExtraIntentKeys() {
return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mNestedIntentKeys;
}
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 68b5d78..908999b 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -124,11 +124,13 @@
@Nullable
@GuardedBy("this")
- private final StringBlock mStringBlock; // null or closed if mNativePtr = 0.
+ private StringBlock mStringBlock; // null or closed if mNativePtr = 0.
@PropertyFlags
private final int mFlags;
+ private final boolean mIsOverlay;
+
@Nullable
private final AssetsProvider mAssets;
@@ -302,40 +304,43 @@
private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
+ this(format, flags, assets);
Objects.requireNonNull(path, "path");
- mFlags = flags;
mNativePtr = nativeLoad(format, path, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
- mAssets = assets;
}
private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
@NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)
throws IOException {
+ this(format, flags, assets);
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
- mFlags = flags;
mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
- mAssets = assets;
}
private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
@NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
@Nullable AssetsProvider assets) throws IOException {
+ this(format, flags, assets);
Objects.requireNonNull(fd, "fd");
Objects.requireNonNull(friendlyName, "friendlyName");
- mFlags = flags;
mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
- mAssets = assets;
}
private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
- mFlags = flags;
+ this(FORMAT_APK, flags, assets);
mNativePtr = nativeLoadEmpty(flags, assets);
mStringBlock = null;
+ }
+
+ private ApkAssets(@FormatType int format, @PropertyFlags int flags,
+ @Nullable AssetsProvider assets) {
+ mFlags = flags;
mAssets = assets;
+ mIsOverlay = format == FORMAT_IDMAP;
}
@UnsupportedAppUsage
@@ -425,6 +430,18 @@
}
}
+ public boolean isSystem() {
+ return (mFlags & PROPERTY_SYSTEM) != 0;
+ }
+
+ public boolean isSharedLib() {
+ return (mFlags & PROPERTY_DYNAMIC) != 0;
+ }
+
+ public boolean isOverlay() {
+ return mIsOverlay;
+ }
+
@Override
public String toString() {
return "ApkAssets{path=" + getDebugName() + "}";
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index e6b9342..bcaceb2 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -203,9 +203,25 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
@Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
- mAssets = assets;
- mAppliedSharedLibsHash =
- ResourcesManager.getInstance().updateResourceImplWithRegisteredLibs(this);
+ // Don't reuse assets by default as we have no control over whether they're already
+ // inside some other ResourcesImpl.
+ this(assets, metrics, config, displayAdjustments, false);
+ }
+
+ public ResourcesImpl(@NonNull ResourcesImpl orig) {
+ // We know for sure that the other assets are in use, so can't reuse the object here.
+ this(orig.getAssets(), orig.getMetrics(), orig.getConfiguration(),
+ orig.getDisplayAdjustments(), false);
+ }
+
+ public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
+ @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments,
+ boolean reuseAssets) {
+ final var assetsAndHash =
+ ResourcesManager.getInstance().updateResourceImplAssetsWithRegisteredLibs(assets,
+ reuseAssets);
+ mAssets = assetsAndHash.first;
+ mAppliedSharedLibsHash = assetsAndHash.second;
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index d243575..6c35d10 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -3,6 +3,16 @@
flag {
namespace: "credential_manager"
+ name: "ttl_fix_enabled"
+ description: "Enable fix for transaction too large issue"
+ bug: "371052524"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "credential_manager"
name: "settings_activity_enabled"
is_exported: true
description: "Enable the Credential Manager Settings Activity APIs"
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index e349b81..f00c3a5 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -23,6 +23,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Parcel;
import android.os.Parcelable;
@@ -150,6 +151,138 @@
}
}
+ /**
+ * Rearranges the topology toward the positions given for each display. The width and height of
+ * each display, as well as the primary display, are not changed by this call.
+ * <p>
+ * Upon returning, the topology will be valid and normalized with each display as close to the
+ * requested positions as possible.
+ *
+ * @param newPos the desired positions (upper-left corner) of each display. The keys in the map
+ * are the display IDs.
+ * @throws IllegalArgumentException if the keys in {@code positions} are not the exact display
+ * IDs in this topology, no more, no less
+ */
+ public void rearrange(Map<Integer, PointF> newPos) {
+ var availableParents = new ArrayList<TreeNode>();
+
+ availableParents.addLast(mRoot);
+
+ var needsParent = allNodesIdMap();
+
+ // In the case of missing items, if this check doesn't detect it, a NPE will be thrown
+ // later.
+ if (needsParent.size() != newPos.size()) {
+ throw new IllegalArgumentException("newPos has wrong number of entries: " + newPos);
+ }
+
+ mRoot.mChildren.clear();
+ for (TreeNode n : needsParent.values()) {
+ n.mChildren.clear();
+ }
+
+ needsParent.remove(mRoot.mDisplayId);
+ // Start with a root island and add children to it one-by-one until the island consists of
+ // all the displays. The root island begins with only the root node, which has no
+ // parent. Then we greedily choose an optimal pairing of two nodes, consisting of a node
+ // from the island and a node not yet in the island. This is repeating until all nodes are
+ // in the island.
+ //
+ // The optimal pair is the pair which has the smallest deviation. The deviation consists of
+ // an x-axis component and a y-axis component, called xDeviation and yDeviation.
+ //
+ // The deviations are like distances but a little different. They are calculated in two
+ // steps. The first step calculates both axes in a similar way. The next step compares the
+ // two values and chooses which axis to attach along. Depending on which axis is chosen,
+ // the deviation for one axis is updated. See below for details.
+ while (!needsParent.isEmpty()) {
+ double bestDist = Double.POSITIVE_INFINITY;
+ TreeNode bestChild = null, bestParent = null;
+
+ for (var child : needsParent.values()) {
+ PointF childPos = newPos.get(child.mDisplayId);
+ float childRight = childPos.x + child.getWidth();
+ float childBottom = childPos.y + child.getHeight();
+ for (var parent : availableParents) {
+ PointF parentPos = newPos.get(parent.mDisplayId);
+ float parentRight = parentPos.x + parent.getWidth();
+ float parentBottom = parentPos.y + parent.getHeight();
+
+ // This is the smaller of the two ranges minus the amount of overlap shared
+ // between them. The "amount of overlap" is negative if there is no overlap, but
+ // this does not make a parenting ineligible, because we allow for attaching at
+ // the corner and for floating point error. The overlap is more negative the
+ // farther apart the closest corner pair is.
+ //
+ // For each axis, this calculates (SmallerRange - Overlap). If one range lies
+ // completely in the other (or they are equal), the axis' deviation will be
+ // zero.
+ //
+ // The "SmallerRange," which refers to smaller of the widths of the two rects,
+ // or smaller of the heights of the two rects, is added to the deviation so that
+ // a maximum overlap results in a deviation of zero.
+ float xSmallerRange = Math.min(child.getWidth(), parent.getWidth());
+ float ySmallerRange = Math.min(child.getHeight(), parent.getHeight());
+ float xOverlap
+ = Math.min(parentRight, childRight)
+ - Math.max(parentPos.x, childPos.x);
+ float yOverlap
+ = Math.min(parentBottom, childBottom)
+ - Math.max(parentPos.y, childPos.y);
+ float xDeviation = xSmallerRange - xOverlap;
+ float yDeviation = ySmallerRange - yOverlap;
+
+ float offset;
+ int pos;
+ if (xDeviation <= yDeviation) {
+ if (childPos.y < parentPos.y) {
+ yDeviation = childBottom - parentPos.y;
+ pos = POSITION_TOP;
+ } else {
+ yDeviation = parentBottom - childPos.y;
+ pos = POSITION_BOTTOM;
+ }
+ offset = childPos.x - parentPos.x;
+ } else {
+ if (childPos.x < parentPos.x) {
+ xDeviation = childRight - parentPos.x;
+ pos = POSITION_LEFT;
+ } else {
+ xDeviation = parentRight - childPos.x;
+ pos = POSITION_RIGHT;
+ }
+ offset = childPos.y - parentPos.y;
+ }
+
+ double dist = Math.hypot(xDeviation, yDeviation);
+ if (dist >= bestDist) {
+ continue;
+ }
+
+ bestDist = dist;
+ bestChild = child;
+ bestParent = parent;
+ // Eagerly update the child's parenting info, even though we may not use it, in
+ // which case it will be overwritten later.
+ bestChild.mPosition = pos;
+ bestChild.mOffset = offset;
+ }
+ }
+
+ assert bestParent != null & bestChild != null;
+
+ bestParent.addChild(bestChild);
+ if (null == needsParent.remove(bestChild.mDisplayId)) {
+ throw new IllegalStateException("child not in pending set! " + bestChild);
+ }
+ availableParents.add(bestChild);
+ }
+
+ // The conversion may have introduced an intersection of two display rects. If they are
+ // bigger than our error tolerance, this function will remove them.
+ normalize();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -450,6 +583,20 @@
return a == b || (Float.isNaN(a) && Float.isNaN(b)) || Math.abs(a - b) < EPSILON;
}
+ private Map<Integer, TreeNode> allNodesIdMap() {
+ var pend = new ArrayDeque<TreeNode>();
+ var found = new HashMap<Integer, TreeNode>();
+
+ pend.push(mRoot);
+ do {
+ TreeNode node = pend.pop();
+ found.put(node.mDisplayId, node);
+ pend.addAll(node.mChildren);
+ } while (!pend.isEmpty());
+
+ return found;
+ }
+
public static final class TreeNode implements Parcelable {
public static final int POSITION_LEFT = 0;
public static final int POSITION_TOP = 1;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 8c3f0ef..70a1909 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -3436,7 +3436,7 @@
initialize();
mInlineSuggestionSessionController.notifyOnStartInput(
editorInfo == null ? null : editorInfo.packageName,
- editorInfo == null ? null : editorInfo.autofillId);
+ editorInfo == null ? null : editorInfo.getAutofillId());
if (DEBUG) Log.v(TAG, "CALL: onStartInput");
onStartInput(editorInfo, restarting);
if (mDecorViewVisible) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 4bc8fe0..9ab9228 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3928,9 +3928,9 @@
final int callingUid = Binder.getCallingUid();
final int processUid = Process.myUid();
- if (Build.isDebuggable() && callingUid != processUid) {
- Log.w(TAG, "Uid " + processUid + " is fetching a copy of UserProperties on"
- + " behalf of callingUid " + callingUid + ". Possibly"
+ if (processUid == Process.SYSTEM_UID && callingUid != processUid) {
+ Log.w(TAG, "The System (uid " + processUid + ") is fetching a copy of"
+ + " UserProperties on behalf of callingUid " + callingUid + ". Possibly"
+ " it should carefully first clearCallingIdentity or perhaps use"
+ " UserManagerInternal.getUserProperties() instead?",
new Throwable());
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 6f3e3d8..9fe0dda 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -16,20 +16,30 @@
package android.security.advancedprotection;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
+import android.content.Intent;
import android.os.Binder;
import android.os.RemoteException;
import android.security.Flags;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -45,6 +55,139 @@
public final class AdvancedProtectionManager {
private static final String TAG = "AdvancedProtectionMgr";
+ /**
+ * Advanced Protection's identifier for setting policies or restrictions in DevicePolicyManager.
+ *
+ * @hide */
+ public static final String ADVANCED_PROTECTION_SYSTEM_ENTITY =
+ "android.security.advancedprotection";
+
+ /**
+ * Feature identifier for disallowing 2G.
+ *
+ * @hide */
+ @SystemApi
+ public static final String FEATURE_ID_DISALLOW_CELLULAR_2G =
+ "android.security.advancedprotection.feature_disallow_2g";
+
+ /**
+ * Feature identifier for disallowing install of unknown sources.
+ *
+ * @hide */
+ @SystemApi
+ public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES =
+ "android.security.advancedprotection.feature_disallow_install_unknown_sources";
+
+ /**
+ * Feature identifier for disallowing USB.
+ *
+ * @hide */
+ @SystemApi
+ public static final String FEATURE_ID_DISALLOW_USB =
+ "android.security.advancedprotection.feature_disallow_usb";
+
+ /**
+ * Feature identifier for disallowing WEP.
+ *
+ * @hide */
+ @SystemApi
+ public static final String FEATURE_ID_DISALLOW_WEP =
+ "android.security.advancedprotection.feature_disallow_wep";
+
+ /**
+ * Feature identifier for enabling MTE.
+ *
+ * @hide */
+ @SystemApi
+ public static final String FEATURE_ID_ENABLE_MTE =
+ "android.security.advancedprotection.feature_enable_mte";
+
+ /** @hide */
+ @StringDef(prefix = { "FEATURE_ID_" }, value = {
+ FEATURE_ID_DISALLOW_CELLULAR_2G,
+ FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ FEATURE_ID_DISALLOW_USB,
+ FEATURE_ID_DISALLOW_WEP,
+ FEATURE_ID_ENABLE_MTE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FeatureId {}
+
+ private static final Set<String> ALL_FEATURE_IDS = Set.of(
+ FEATURE_ID_DISALLOW_CELLULAR_2G,
+ FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ FEATURE_ID_DISALLOW_USB,
+ FEATURE_ID_DISALLOW_WEP,
+ FEATURE_ID_ENABLE_MTE);
+
+ /**
+ * Activity Action: Show a dialog with disabled by advanced protection message.
+ * <p> If a user action or a setting toggle is disabled by advanced protection, this dialog can
+ * be triggered to let the user know about this.
+ * <p>
+ * Input:
+ * <p>{@link #EXTRA_SUPPORT_DIALOG_FEATURE}: The feature identifier.
+ * <p>{@link #EXTRA_SUPPORT_DIALOG_TYPE}: The type of the action.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide */
+ @SystemApi
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @FlaggedApi(android.security.Flags.FLAG_AAPM_API)
+ public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG =
+ "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG";
+
+ /**
+ * A string extra used with {@link #createSupportIntent} to identify the feature that needs to
+ * show a support dialog explaining it was disabled by advanced protection.
+ *
+ * @hide */
+ @FeatureId
+ @SystemApi
+ public static final String EXTRA_SUPPORT_DIALOG_FEATURE =
+ "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE";
+
+ /**
+ * A string extra used with {@link #createSupportIntent} to identify the type of the action that
+ * needs to be explained in the support dialog.
+ *
+ * @hide */
+ @SupportDialogType
+ @SystemApi
+ public static final String EXTRA_SUPPORT_DIALOG_TYPE =
+ "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE";
+
+ /**
+ * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user performed an action that was
+ * blocked by advanced protection.
+ *
+ * @hide */
+ @SystemApi
+ public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION =
+ "android.security.advancedprotection.type_blocked_interaction";
+
+ /**
+ * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user pressed on a setting toggle
+ * that was disabled by advanced protection.
+ *
+ * @hide */
+ @SystemApi
+ public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING =
+ "android.security.advancedprotection.type_disabled_setting";
+
+ /** @hide */
+ @StringDef(prefix = { "SUPPORT_DIALOG_TYPE_" }, value = {
+ SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
+ SUPPORT_DIALOG_TYPE_DISABLED_SETTING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SupportDialogType {}
+
+ private static final Set<String> ALL_SUPPORT_DIALOG_TYPES = Set.of(
+ SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION,
+ SUPPORT_DIALOG_TYPE_DISABLED_SETTING);
+
private final ConcurrentHashMap<Callback, IAdvancedProtectionCallback>
mCallbackMap = new ConcurrentHashMap<>();
@@ -164,6 +307,43 @@
}
/**
+ * Called by a feature to display a support dialog when a feature was disabled by advanced
+ * protection. This returns an intent that can be used with
+ * {@link Context#startActivity(Intent)} to display the dialog.
+ *
+ * <p>Note that this method doesn't check if the feature is actually disabled, i.e. this method
+ * will always return an intent.
+ *
+ * @param featureId The feature identifier.
+ * @param type The type of the feature describing the action that needs to be explained
+ * in the dialog or null for default explanation.
+ * @return Intent An intent to be used to start the dialog-activity that explains a feature was
+ * disabled by advanced protection.
+ * @hide
+ */
+ @SystemApi
+ public @NonNull Intent createSupportIntent(@NonNull @FeatureId String featureId,
+ @Nullable @SupportDialogType String type) {
+ Objects.requireNonNull(featureId);
+ if (!ALL_FEATURE_IDS.contains(featureId)) {
+ throw new IllegalArgumentException(featureId + " is not a valid feature ID. See"
+ + " FEATURE_ID_* APIs.");
+ }
+ if (type != null && !ALL_SUPPORT_DIALOG_TYPES.contains(type)) {
+ throw new IllegalArgumentException(type + " is not a valid type. See"
+ + " SUPPORT_DIALOG_TYPE_* APIs.");
+ }
+
+ Intent intent = new Intent(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG);
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(EXTRA_SUPPORT_DIALOG_FEATURE, featureId);
+ if (type != null) {
+ intent.putExtra(EXTRA_SUPPORT_DIALOG_TYPE, type);
+ }
+ return intent;
+ }
+
+ /**
* A callback class for monitoring changes to Advanced Protection state
*
* <p>To register a callback, implement this interface, and register it with
diff --git a/core/java/android/service/quickaccesswallet/flags.aconfig b/core/java/android/service/quickaccesswallet/flags.aconfig
new file mode 100644
index 0000000..07311d5
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.service.quickaccesswallet"
+container: "system"
+
+flag {
+ name: "launch_wallet_option_on_power_double_tap"
+ namespace: "wallet_integrations"
+ description: "Option to launch the Wallet app on double-tap of the power button"
+ bug: "378469025"
+}
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index fb3e083..a560339 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -24,6 +24,7 @@
import static android.view.inputmethod.EditorInfoProto.PRIVATE_IME_OPTIONS;
import static android.view.inputmethod.EditorInfoProto.TARGET_INPUT_METHOD_USER_ID;
import static android.view.inputmethod.Flags.FLAG_EDITORINFO_HANDWRITING_ENABLED;
+import static android.view.inputmethod.Flags.FLAG_PUBLIC_AUTOFILL_ID_IN_EDITORINFO;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -470,12 +471,10 @@
public String packageName;
/**
- * Autofill Id for the field that's currently on focus.
- *
- * <p> Marked as hide since it's only used by framework.</p>
- * @hide
+ * Autofill Id for the field that's currently on focus. See link {@link AutofillId} for more
+ * details. It is set by {@link View#getAutofillId()}
*/
- public AutofillId autofillId;
+ private AutofillId autofillId;
/**
* Identifier for the editor's field. This is optional, and may be
@@ -1200,6 +1199,28 @@
}
/**
+ * Returns the {@link AutofillId} of the view that this {@link EditorInfo} is associated with.
+ * The value is filled in with the result of {@link android.view.View#getAutofillId()
+ * View.getAutofillId()} on the view that is being edited.
+ *
+ * Note: For virtual view(e.g. Compose or Webview), default behavior is the autofillId is the id
+ * of the container view, unless the virtual view provider sets the virtual id when the
+ * InputMethodManager calls {@link android.view.View#onCreateInputConnection()} on the container
+ * view.
+ */
+ @FlaggedApi(FLAG_PUBLIC_AUTOFILL_ID_IN_EDITORINFO)
+ @Nullable
+ public AutofillId getAutofillId() {
+ return autofillId;
+ }
+
+ /** Sets the {@link AutofillId} of the view that this {@link EditorInfo} is associated with. */
+ @FlaggedApi(FLAG_PUBLIC_AUTOFILL_ID_IN_EDITORINFO)
+ public void setAutofillId(@Nullable AutofillId autofillId) {
+ this.autofillId = autofillId;
+ }
+
+ /**
* Export the state of {@link EditorInfo} into a protocol buffer output stream.
*
* @param proto Stream to write the state to
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 73f9d9f..6303c76 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2471,6 +2471,11 @@
return;
}
+ if (Flags.refactorInsetsController()) {
+ showSoftInput(rootView, statsToken, flags, resultReceiver, reason);
+ return;
+ }
+
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
// Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread.
@@ -5174,7 +5179,7 @@
// system can verify the consistency between the uid of this process and package name passed
// from here. See comment of Context#getOpPackageName() for details.
editorInfo.packageName = servedView.getContext().getOpPackageName();
- editorInfo.autofillId = servedView.getAutofillId();
+ editorInfo.setAutofillId(servedView.getAutofillId());
editorInfo.fieldId = servedView.getId();
final InputConnection ic = servedView.onCreateInputConnection(editorInfo);
if (DEBUG) Log.v(TAG, "Starting input: editorInfo=" + editorInfo + " ic=" + ic);
@@ -5183,7 +5188,7 @@
// This ensures that even disconnected EditorInfos have well-defined attributes,
// making them consistently and straightforwardly comparable.
if (ic == null) {
- editorInfo.autofillId = AutofillId.NO_AUTOFILL_ID;
+ editorInfo.setAutofillId(AutofillId.NO_AUTOFILL_ID);
editorInfo.fieldId = 0;
}
return new Pair<>(ic, editorInfo);
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index edd9d6c..e619ab0 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -165,4 +165,13 @@
description: "Writing tools API"
bug: "373788889"
is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+ name: "public_autofill_id_in_editorinfo"
+ is_exported: true
+ namespace: "input_method"
+ description: "Guarding public API autofillId in editor info"
+ bug: "342672560"
+ is_fixed_read_only: true
+}
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp
index 6c72544..76ead2a 100644
--- a/core/jni/com_android_internal_content_FileSystemUtils.cpp
+++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp
@@ -22,7 +22,6 @@
#include <android-base/hex.h>
#include <android-base/unique_fd.h>
#include <bionic/macros.h>
-#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -204,7 +203,8 @@
return true;
}
-bool punchHolesInElf64(const char *filePath, const uint64_t offset) {
+bool getLoadSegmentPhdrs(const char *filePath, const uint64_t offset,
+ std::vector<Elf64_Phdr> &programHeaders) {
// Open Elf file
Elf64_Ehdr ehdr;
std::ifstream inputStream(filePath, std::ifstream::in);
@@ -227,11 +227,6 @@
uint64_t programHeaderOffset = ehdr.e_phoff;
uint16_t programHeaderNum = ehdr.e_phnum;
- IF_ALOGD() {
- ALOGD("Punching holes in file: %s programHeaderOffset: %" PRIu64 " programHeaderNum: %hu",
- filePath, programHeaderOffset, programHeaderNum);
- }
-
// if this is a zip file, also consider elf offset inside a file
uint64_t phOffset;
if (__builtin_add_overflow(offset, programHeaderOffset, &phOffset)) {
@@ -240,7 +235,6 @@
}
inputStream.seekg(phOffset);
- std::vector<Elf64_Phdr> programHeaders;
for (int headerIndex = 0; headerIndex < programHeaderNum; headerIndex++) {
Elf64_Phdr header;
inputStream.read((char *)&header, sizeof(header));
@@ -254,6 +248,15 @@
programHeaders.push_back(header);
}
+ return true;
+}
+
+bool punchHolesInElf64(const char *filePath, const uint64_t offset) {
+ std::vector<Elf64_Phdr> programHeaders;
+ if (!getLoadSegmentPhdrs(filePath, offset, programHeaders)) {
+ ALOGE("Failed to read program headers from ELF file.");
+ return false;
+ }
return punchHoles(filePath, offset, programHeaders);
}
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.h b/core/jni/com_android_internal_content_FileSystemUtils.h
index 52445e2..4a95686c 100644
--- a/core/jni/com_android_internal_content_FileSystemUtils.h
+++ b/core/jni/com_android_internal_content_FileSystemUtils.h
@@ -15,8 +15,11 @@
*/
#pragma once
+#include <elf.h>
#include <sys/types.h>
+#include <vector>
+
namespace android {
/*
@@ -35,4 +38,11 @@
*/
bool punchHolesInZip(const char* filePath, uint64_t offset, uint16_t extraFieldLen);
+/*
+ * This function reads program headers from ELF file. ELF can be specified with file path directly
+ * or it should be at offset inside Apk. Program headers passed to function is populated.
+ */
+bool getLoadSegmentPhdrs(const char* filePath, const uint64_t offset,
+ std::vector<Elf64_Phdr>& programHeaders);
+
} // namespace android
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dc054a4..969ee2e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1849,6 +1849,10 @@
<item>-1</item>
</integer-array>
+ <!-- Specifies the delay in milliseconds after the last user input before turning off the
+ keyboard backlight.
+ -->
+ <integer name="config_keyboardBacklightTimeoutMs">30000</integer>
<!-- An array describing the screen's backlight values corresponding to the brightness
values in the config_screenBrightnessNits array.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index badb986..9dd3027 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2098,6 +2098,7 @@
<java-symbol type="integer" name="config_autoBrightnessDarkeningLightDebounce"/>
<java-symbol type="integer" name="config_autoBrightnessInitialLightSensorRate"/>
<java-symbol type="integer" name="config_autoBrightnessLightSensorRate"/>
+ <java-symbol type="integer" name="config_keyboardBacklightTimeoutMs" />
<java-symbol type="integer" name="config_carDockKeepsScreenOn" />
<java-symbol type="integer" name="config_criticalBatteryWarningLevel" />
<java-symbol type="integer" name="config_datause_notification_type" />
diff --git a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
index 1721e1e..9dd196d 100644
--- a/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/InputMethodCoreTests/src/android/view/inputmethod/EditorInfoTest.java
@@ -79,7 +79,7 @@
TEST_EDITOR_INFO.label = "testLabel";
TEST_EDITOR_INFO.packageName = "android.view.inputmethod";
TEST_EDITOR_INFO.fieldId = 0;
- TEST_EDITOR_INFO.autofillId = AutofillId.NO_AUTOFILL_ID;
+ TEST_EDITOR_INFO.setAutofillId(AutofillId.NO_AUTOFILL_ID);
TEST_EDITOR_INFO.fieldName = "testField";
TEST_EDITOR_INFO.extras = new Bundle();
TEST_EDITOR_INFO.extras.putString("testKey", "testValue");
@@ -531,7 +531,7 @@
info.setStylusHandwritingEnabled(true);
}
info.packageName = "android.view.inputmethod";
- info.autofillId = new AutofillId(123);
+ info.setAutofillId(new AutofillId(123));
info.fieldId = 456;
info.fieldName = "testField";
info.extras = new Bundle();
@@ -597,7 +597,7 @@
@Test
public void testKindofEqualsComparesAutofillId() {
final EditorInfo infoCopy = TEST_EDITOR_INFO.createCopyInternal();
- infoCopy.autofillId = new AutofillId(42);
+ infoCopy.setAutofillId(new AutofillId(42));
assertFalse(TEST_EDITOR_INFO.kindofEquals(infoCopy));
}
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
index a6de611..8969b2b 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
+++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
@@ -16,7 +16,10 @@
package android.hardware.display
+import android.graphics.PointF
+import android.graphics.RectF
import android.hardware.display.DisplayTopology.TreeNode.POSITION_BOTTOM
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_LEFT
import android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP
import android.hardware.display.DisplayTopology.TreeNode.POSITION_RIGHT
import android.view.Display
@@ -469,4 +472,205 @@
assertThat(actualDisplay4.offset).isEqualTo(-400f)
assertThat(actualDisplay4.children).isEmpty()
}
-}
\ No newline at end of file
+
+ @Test
+ fun rearrange_twoDisplays() {
+ val nodes = rearrangeRects(
+ // Arrange in staggered manner, connected vertically.
+ RectF(100f, 100f, 250f, 200f),
+ RectF(150f, 200f, 300f, 300f),
+ )
+
+ assertThat(nodes[0].children).containsExactly(nodes[1])
+ assertThat(nodes[1].children).isEmpty()
+ assertPositioning(nodes, Pair(POSITION_BOTTOM, 50f))
+ }
+
+ @Test
+ fun rearrange_reverseOrderOfSeveralDisplays() {
+ val nodes = rearrangeRects(
+ RectF(0f, 0f, 150f, 100f),
+ RectF(-150f, 0f, 0f, 100f),
+ RectF(-300f, 0f, -150f, 100f),
+ RectF(-450f, 0f, -300f, 100f),
+ )
+
+ assertPositioning(
+ nodes,
+ Pair(POSITION_LEFT, 0f),
+ Pair(POSITION_LEFT, 0f),
+ Pair(POSITION_LEFT, 0f),
+ )
+
+ assertThat(nodes[0].children).containsExactly(nodes[1])
+ assertThat(nodes[1].children).containsExactly(nodes[2])
+ assertThat(nodes[2].children).containsExactly(nodes[3])
+ assertThat(nodes[3].children).isEmpty()
+ }
+
+ @Test
+ fun rearrange_crossWithRootInCenter() {
+ val nodes = rearrangeRects(
+ RectF(0f, 0f, 150f, 100f),
+ RectF(-150f, 0f, 0f, 100f),
+ RectF(0f,-100f, 150f, 0f),
+ RectF(150f, 0f, 300f, 100f),
+ RectF(0f, 100f, 150f, 200f),
+ )
+
+ assertPositioning(
+ nodes,
+ Pair(POSITION_LEFT, 0f),
+ Pair(POSITION_TOP, 0f),
+ Pair(POSITION_RIGHT, 0f),
+ Pair(POSITION_BOTTOM, 0f),
+ )
+
+ assertThat(nodes[0].children)
+ .containsExactly(nodes[1], nodes[2], nodes[3], nodes[4])
+ }
+
+ @Test
+ fun rearrange_elbowArrangementDoesNotUseCornerAdjacency1() {
+ val nodes = rearrangeRects(
+ // 2
+ // |
+ // 0 - 1
+
+ RectF(0f, 0f, 100f, 100f),
+ RectF(100f, 0f, 200f, 100f),
+ RectF(100f, -100f, 200f, 0f),
+ )
+
+ assertThat(nodes[0].children).containsExactly(nodes[1])
+ assertThat(nodes[1].children).containsExactly(nodes[2])
+ assertThat(nodes[2].children).isEmpty()
+
+ assertPositioning(
+ nodes,
+ Pair(POSITION_RIGHT, 0f),
+ Pair(POSITION_TOP, 0f),
+ )
+ }
+
+ @Test
+ fun rearrange_elbowArrangementDoesNotUseCornerAdjacency2() {
+ val nodes = rearrangeRects(
+ // 0
+ // |
+ // 1
+ // |
+ // 3 - 2
+
+ RectF(0f, 0f, 100f, 100f),
+ RectF(0f, 100f, 100f, 200f),
+ RectF(0f, 200f, 100f, 300f),
+ RectF(-100f, 200f, 0f, 300f),
+ )
+
+ assertThat(nodes[0].children).containsExactly(nodes[1])
+ assertThat(nodes[1].children).containsExactly(nodes[2])
+ assertThat(nodes[2].children).containsExactly(nodes[3])
+ assertThat(nodes[3].children).isEmpty()
+
+ assertPositioning(
+ nodes,
+ Pair(POSITION_BOTTOM, 0f),
+ Pair(POSITION_BOTTOM, 0f),
+ Pair(POSITION_LEFT, 0f),
+ )
+ }
+
+ @Test
+ fun rearrange_useLargerEdge() {
+ val nodes = rearrangeRects(
+ //444111
+ //444111
+ //444111
+ // 000222
+ // 000222
+ // 000222
+ // 333
+ // 333
+ // 333
+ RectF(20f, 30f, 50f, 60f),
+ RectF(30f, 0f, 60f, 30f),
+ RectF(50f, 30f, 80f, 60f),
+ RectF(40f, 60f, 70f, 90f),
+ RectF(0f, 0f, 30f, 30f),
+ )
+
+ assertPositioning(
+ nodes,
+ Pair(POSITION_TOP, 10f),
+ Pair(POSITION_RIGHT, 0f),
+ Pair(POSITION_BOTTOM, -10f),
+ Pair(POSITION_LEFT, 0f),
+ )
+
+ assertThat(nodes[0].children).containsExactly(nodes[1], nodes[2])
+ assertThat(nodes[1].children).containsExactly(nodes[4])
+ assertThat(nodes[2].children).containsExactly(nodes[3])
+ (3..4).forEach { assertThat(nodes[it].children).isEmpty() }
+ }
+
+ @Test
+ fun rearrange_closeGaps() {
+ val nodes = rearrangeRects(
+ //000
+ //000 111
+ //000 111
+ // 111
+ //
+ // 222
+ // 222
+ // 222
+ RectF(0f, 0f, 30f, 30f),
+ RectF(40f, 10f, 70f, 40f),
+ RectF(80.5f, 50f, 110f, 80f), // left+=0.5 to cause a preference for TOP/BOTTOM attach
+ )
+
+ assertPositioning(
+ nodes,
+ // In the case of corner adjacency, we prefer a left/right attachment.
+ Pair(POSITION_RIGHT, 10f),
+ Pair(POSITION_BOTTOM, 40.5f), // TODO: fix implementation to remove this gap
+ )
+
+ assertThat(nodes[0].children).containsExactly(nodes[1])
+ assertThat(nodes[1].children).containsExactly(nodes[2])
+ assertThat(nodes[2].children).isEmpty()
+ }
+
+ /**
+ * Runs the rearrange algorithm and returns the resulting tree as a list of nodes, with the
+ * root at index 0. The number of nodes is inferred from the number of positions passed.
+ */
+ private fun rearrangeRects(vararg pos : RectF) : List<DisplayTopology.TreeNode> {
+ // Generates a linear sequence of nodes in order in the List from root to leaf,
+ // left-to-right. IDs are ascending from 0 to count - 1.
+
+ val nodes = pos.indices.map {
+ DisplayTopology.TreeNode(it, pos[it].width(), pos[it].height(), POSITION_RIGHT, 0f)
+ }
+
+ nodes.forEachIndexed { id, node ->
+ if (id > 0) {
+ nodes[id - 1].addChild(node)
+ }
+ }
+
+ DisplayTopology(nodes[0], 0).rearrange(pos.indices.associateWith {
+ PointF(pos[it].left, pos[it].top)
+ })
+
+ return nodes
+ }
+
+ private fun assertPositioning(
+ nodes : List<DisplayTopology.TreeNode>, vararg positions : Pair<Int, Float>) {
+ assertThat(nodes.drop(1).map { Pair(it.position, it.offset )})
+ .containsExactly(*positions)
+ .inOrder()
+ }
+}
diff --git a/data/sounds/Android.bp b/data/sounds/Android.bp
new file mode 100644
index 0000000..65d4872
--- /dev/null
+++ b/data/sounds/Android.bp
@@ -0,0 +1,304 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+phony {
+ name: "frameworks_sounds",
+ required: [
+ "frameworks_alarm_sounds",
+ "frameworks_notifications_sounds",
+ "frameworks_ringtones_sounds",
+ "frameworks_ui_sounds",
+ "frameworks_ui_48k_sounds",
+ ],
+}
+
+prebuilt_media {
+ name: "frameworks_alarm_sounds",
+ srcs: [
+ "Alarm_Beep_01.ogg",
+ "Alarm_Beep_02.ogg",
+ "Alarm_Beep_03.ogg",
+ "Alarm_Buzzer.ogg",
+ "Alarm_Classic.ogg",
+ "Alarm_Rooster_02.ogg",
+ "alarms/ogg/Argon.ogg",
+ "alarms/ogg/Barium.ogg",
+ "alarms/ogg/Carbon.ogg",
+ "alarms/ogg/Helium.ogg",
+ "alarms/ogg/Krypton.ogg",
+ "alarms/ogg/Neon.ogg",
+ "alarms/ogg/Neptunium.ogg",
+ "alarms/ogg/Osmium.ogg",
+ "alarms/ogg/Oxygen.ogg",
+ "alarms/ogg/Platinum.ogg",
+ "alarms/ogg/Promethium.ogg",
+ "alarms/ogg/Scandium.ogg",
+ ],
+ relative_install_path: "audio/alarms",
+ product_specific: true,
+ no_full_install: true,
+}
+
+prebuilt_media {
+ name: "frameworks_notifications_sounds",
+ srcs: [
+ "notifications/ogg/Adara.ogg",
+ "notifications/Aldebaran.ogg",
+ "notifications/Altair.ogg",
+ "notifications/ogg/Alya.ogg",
+ "notifications/Antares.ogg",
+ "notifications/ogg/Antimony.ogg",
+ "notifications/ogg/Arcturus.ogg",
+ "notifications/ogg/Argon.ogg",
+ "notifications/Beat_Box_Android.ogg",
+ "notifications/ogg/Bellatrix.ogg",
+ "notifications/ogg/Beryllium.ogg",
+ "notifications/Betelgeuse.ogg",
+ "newwavelabs/CaffeineSnake.ogg",
+ "notifications/Canopus.ogg",
+ "notifications/ogg/Capella.ogg",
+ "notifications/Castor.ogg",
+ "notifications/ogg/CetiAlpha.ogg",
+ "notifications/ogg/Cobalt.ogg",
+ "notifications/Cricket.ogg",
+ "newwavelabs/DearDeer.ogg",
+ "notifications/Deneb.ogg",
+ "notifications/Doink.ogg",
+ "newwavelabs/DontPanic.ogg",
+ "notifications/Drip.ogg",
+ "notifications/Electra.ogg",
+ "F1_MissedCall.ogg",
+ "F1_New_MMS.ogg",
+ "F1_New_SMS.ogg",
+ "notifications/ogg/Fluorine.ogg",
+ "notifications/Fomalhaut.ogg",
+ "notifications/ogg/Gallium.ogg",
+ "notifications/Heaven.ogg",
+ "notifications/ogg/Helium.ogg",
+ "newwavelabs/Highwire.ogg",
+ "notifications/ogg/Hojus.ogg",
+ "notifications/ogg/Iridium.ogg",
+ "notifications/ogg/Krypton.ogg",
+ "newwavelabs/KzurbSonar.ogg",
+ "notifications/ogg/Lalande.ogg",
+ "notifications/Merope.ogg",
+ "notifications/ogg/Mira.ogg",
+ "newwavelabs/OnTheHunt.ogg",
+ "notifications/ogg/Palladium.ogg",
+ "notifications/Plastic_Pipe.ogg",
+ "notifications/ogg/Polaris.ogg",
+ "notifications/ogg/Pollux.ogg",
+ "notifications/ogg/Procyon.ogg",
+ "notifications/ogg/Proxima.ogg",
+ "notifications/ogg/Radon.ogg",
+ "notifications/ogg/Rubidium.ogg",
+ "notifications/ogg/Selenium.ogg",
+ "notifications/ogg/Shaula.ogg",
+ "notifications/Sirrah.ogg",
+ "notifications/SpaceSeed.ogg",
+ "notifications/ogg/Spica.ogg",
+ "notifications/ogg/Strontium.ogg",
+ "notifications/ogg/Syrma.ogg",
+ "notifications/TaDa.ogg",
+ "notifications/ogg/Talitha.ogg",
+ "notifications/ogg/Tejat.ogg",
+ "notifications/ogg/Thallium.ogg",
+ "notifications/Tinkerbell.ogg",
+ "notifications/ogg/Upsilon.ogg",
+ "notifications/ogg/Vega.ogg",
+ "newwavelabs/Voila.ogg",
+ "notifications/ogg/Xenon.ogg",
+ "notifications/ogg/Zirconium.ogg",
+ "notifications/arcturus.ogg",
+ "notifications/moonbeam.ogg",
+ "notifications/pixiedust.ogg",
+ "notifications/pizzicato.ogg",
+ "notifications/regulus.ogg",
+ "notifications/sirius.ogg",
+ "notifications/tweeters.ogg",
+ "notifications/vega.ogg",
+ ],
+ relative_install_path: "audio/notifications",
+ product_specific: true,
+ no_full_install: true,
+}
+
+prebuilt_media {
+ name: "frameworks_ringtones_sounds",
+ srcs: [
+ "ringtones/ANDROMEDA.ogg",
+ "ringtones/ogg/Andromeda.ogg",
+ "ringtones/ogg/Aquila.ogg",
+ "ringtones/ogg/ArgoNavis.ogg",
+ "ringtones/ogg/Atria.ogg",
+ "ringtones/BOOTES.ogg",
+ "newwavelabs/Backroad.ogg",
+ "newwavelabs/BeatPlucker.ogg",
+ "newwavelabs/BentleyDubs.ogg",
+ "newwavelabs/Big_Easy.ogg",
+ "newwavelabs/BirdLoop.ogg",
+ "newwavelabs/Bollywood.ogg",
+ "newwavelabs/BussaMove.ogg",
+ "ringtones/CANISMAJOR.ogg",
+ "ringtones/CASSIOPEIA.ogg",
+ "newwavelabs/Cairo.ogg",
+ "newwavelabs/Calypso_Steel.ogg",
+ "ringtones/ogg/CanisMajor.ogg",
+ "newwavelabs/CaribbeanIce.ogg",
+ "ringtones/ogg/Carina.ogg",
+ "ringtones/ogg/Centaurus.ogg",
+ "newwavelabs/Champagne_Edition.ogg",
+ "newwavelabs/Club_Cubano.ogg",
+ "newwavelabs/CrayonRock.ogg",
+ "newwavelabs/CrazyDream.ogg",
+ "newwavelabs/CurveBall.ogg",
+ "ringtones/ogg/Cygnus.ogg",
+ "newwavelabs/DancinFool.ogg",
+ "newwavelabs/Ding.ogg",
+ "newwavelabs/DonMessWivIt.ogg",
+ "ringtones/ogg/Draco.ogg",
+ "newwavelabs/DreamTheme.ogg",
+ "newwavelabs/Eastern_Sky.ogg",
+ "newwavelabs/Enter_the_Nexus.ogg",
+ "ringtones/Eridani.ogg",
+ "newwavelabs/EtherShake.ogg",
+ "ringtones/FreeFlight.ogg",
+ "newwavelabs/FriendlyGhost.ogg",
+ "newwavelabs/Funk_Yall.ogg",
+ "newwavelabs/GameOverGuitar.ogg",
+ "newwavelabs/Gimme_Mo_Town.ogg",
+ "ringtones/ogg/Girtab.ogg",
+ "newwavelabs/Glacial_Groove.ogg",
+ "newwavelabs/Growl.ogg",
+ "newwavelabs/HalfwayHome.ogg",
+ "ringtones/ogg/Hydra.ogg",
+ "newwavelabs/InsertCoin.ogg",
+ "ringtones/ogg/Kuma.ogg",
+ "newwavelabs/LoopyLounge.ogg",
+ "newwavelabs/LoveFlute.ogg",
+ "ringtones/Lyra.ogg",
+ "ringtones/ogg/Machina.ogg",
+ "newwavelabs/MidEvilJaunt.ogg",
+ "newwavelabs/MildlyAlarming.ogg",
+ "newwavelabs/Nairobi.ogg",
+ "newwavelabs/Nassau.ogg",
+ "newwavelabs/NewPlayer.ogg",
+ "newwavelabs/No_Limits.ogg",
+ "newwavelabs/Noises1.ogg",
+ "newwavelabs/Noises2.ogg",
+ "newwavelabs/Noises3.ogg",
+ "newwavelabs/OrganDub.ogg",
+ "ringtones/ogg/Orion.ogg",
+ "ringtones/PERSEUS.ogg",
+ "newwavelabs/Paradise_Island.ogg",
+ "ringtones/ogg/Pegasus.ogg",
+ "ringtones/ogg/Perseus.ogg",
+ "newwavelabs/Playa.ogg",
+ "ringtones/ogg/Pyxis.ogg",
+ "ringtones/ogg/Rasalas.ogg",
+ "newwavelabs/Revelation.ogg",
+ "ringtones/ogg/Rigel.ogg",
+ "Ring_Classic_02.ogg",
+ "Ring_Digital_02.ogg",
+ "Ring_Synth_02.ogg",
+ "Ring_Synth_04.ogg",
+ "newwavelabs/Road_Trip.ogg",
+ "newwavelabs/RomancingTheTone.ogg",
+ "newwavelabs/Safari.ogg",
+ "newwavelabs/Savannah.ogg",
+ "ringtones/ogg/Scarabaeus.ogg",
+ "ringtones/ogg/Sceptrum.ogg",
+ "newwavelabs/Seville.ogg",
+ "newwavelabs/Shes_All_That.ogg",
+ "newwavelabs/SilkyWay.ogg",
+ "newwavelabs/SitarVsSitar.ogg",
+ "ringtones/ogg/Solarium.ogg",
+ "newwavelabs/SpringyJalopy.ogg",
+ "newwavelabs/Steppin_Out.ogg",
+ "newwavelabs/Terminated.ogg",
+ "ringtones/Testudo.ogg",
+ "ringtones/ogg/Themos.ogg",
+ "newwavelabs/Third_Eye.ogg",
+ "newwavelabs/Thunderfoot.ogg",
+ "newwavelabs/TwirlAway.ogg",
+ "ringtones/URSAMINOR.ogg",
+ "ringtones/ogg/UrsaMinor.ogg",
+ "newwavelabs/VeryAlarmed.ogg",
+ "ringtones/Vespa.ogg",
+ "newwavelabs/World.ogg",
+ "ringtones/ogg/Zeta.ogg",
+ "ringtones/hydra.ogg",
+ ],
+ relative_install_path: "audio/ringtones",
+ product_specific: true,
+ no_full_install: true,
+}
+
+prebuilt_media {
+ name: "frameworks_ui_48k_sounds",
+ srcs: [
+ "effects/ogg/Effect_Tick_48k.ogg",
+ "effects/ogg/KeypressDelete_120_48k.ogg",
+ "effects/ogg/KeypressReturn_120_48k.ogg",
+ "effects/ogg/KeypressSpacebar_120_48k.ogg",
+ "effects/ogg/KeypressStandard_120_48k.ogg",
+ "effects/ogg/KeypressInvalid_120_48k.ogg",
+ "effects/ogg/Trusted_48k.ogg",
+ "effects/ogg/VideoRecord_48k.ogg",
+ "effects/ogg/VideoStop_48k.ogg",
+ "effects/ogg/camera_click_48k.ogg",
+ ],
+ dsts: [
+ "Effect_Tick.ogg",
+ "KeypressDelete.ogg",
+ "KeypressReturn.ogg",
+ "KeypressSpacebar.ogg",
+ "KeypressStandard.ogg",
+ "KeypressInvalid.ogg",
+ "Trusted.ogg",
+ "VideoRecord.ogg",
+ "VideoStop.ogg",
+ "camera_click.ogg",
+ ],
+ relative_install_path: "audio/ui",
+ product_specific: true,
+ no_full_install: true,
+}
+
+prebuilt_media {
+ name: "frameworks_ui_sounds",
+ srcs: [
+ "effects/ogg/Dock.ogg",
+ "effects/ogg/Lock.ogg",
+ "effects/ogg/LowBattery.ogg",
+ "effects/ogg/Undock.ogg",
+ "effects/ogg/Unlock.ogg",
+ "effects/ogg/WirelessChargingStarted.ogg",
+ "effects/ogg/camera_focus.ogg",
+ "effects/ogg/ChargingStarted.ogg",
+ "effects/ogg/InCallNotification.ogg",
+ "effects/ogg/NFCFailure.ogg",
+ "effects/ogg/NFCInitiated.ogg",
+ "effects/ogg/NFCSuccess.ogg",
+ "effects/ogg/NFCTransferComplete.ogg",
+ "effects/ogg/NFCTransferInitiated.ogg",
+ ],
+ relative_install_path: "audio/ui",
+ product_specific: true,
+ no_full_install: true,
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
index cae5d8e..35b2375 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
@@ -96,7 +96,7 @@
}
if (WRITE_PARCELABLE.matches(tree, state)) {
return buildDescription(tree)
- .setMessage("Recommended to use 'item.writeToParcel()' to improve "
+ .setMessage("Recommended to use 'writeTypedObject()' to improve "
+ "efficiency; saves overhead of Parcelable class name")
.build();
}
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 7446b88..6928c25 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
@@ -1179,7 +1179,7 @@
val userContext =
context.createContextAsUser(userHandle, /* flags= */ 0)
val intent = Intent(userContext, DesktopWallpaperActivity::class.java)
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId)
val options =
ActivityOptions.makeBasic().apply {
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 5ffc64f..79a9ce5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -47,7 +47,6 @@
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipMenuController;
-import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.DefaultMixedHandler;
@@ -312,10 +311,10 @@
}
/** Whether a particular package is same as current pip package. */
- public boolean isPackageActiveInPip(String packageName) {
- final TaskInfo inPipTask = mPipOrganizer.getTaskInfo();
- return packageName != null && inPipTask != null && mPipOrganizer.isInPip()
- && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent));
+ public boolean isPackageActiveInPip(@Nullable String packageName) {
+ return packageName != null
+ && mPipBoundsState.getLastPipComponentName() != null
+ && packageName.equals(mPipBoundsState.getLastPipComponentName().getPackageName());
}
/** Add PiP-related changes to `outWCT` for the given request. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 02f5955..d415c10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -420,7 +420,8 @@
}
// Update the src-rect-hint in params in place, to set up initial animator transform.
- params.getSourceRectHint().set(adjustedSourceRectHint);
+ params.copyOnlySet(new PictureInPictureParams.Builder()
+ .setSourceRectHint(adjustedSourceRectHint).build());
// Config-at-end transitions need to have their activities transformed before starting
// the animation; this makes the buffer seem like it's been updated to final size.
diff --git a/media/java/android/media/quality/AmbientBacklightEvent.java b/media/java/android/media/quality/AmbientBacklightEvent.java
index 3bc6b86..5c11def 100644
--- a/media/java/android/media/quality/AmbientBacklightEvent.java
+++ b/media/java/android/media/quality/AmbientBacklightEvent.java
@@ -16,9 +16,11 @@
package android.media.quality;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.tv.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,8 +29,10 @@
import java.util.Objects;
/**
+ * Ambient backlight event
* @hide
*/
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
public final class AmbientBacklightEvent implements Parcelable {
/** @hide */
@@ -64,6 +68,9 @@
@Nullable
private final AmbientBacklightMetadata mMetadata;
+ /**
+ * Constructor of AmbientBacklightEvent.
+ */
public AmbientBacklightEvent(int eventType,
@Nullable AmbientBacklightMetadata metadata) {
mEventType = eventType;
@@ -75,10 +82,19 @@
mMetadata = in.readParcelable(AmbientBacklightMetadata.class.getClassLoader());
}
+ /**
+ * Gets event type.
+ */
public int getEventType() {
return mEventType;
}
+ /**
+ * Gets ambient backlight metadata.
+ *
+ * @return the metadata of the event. It's non-null only for
+ * {@link #AMBIENT_BACKLIGHT_EVENT_METADATA}.
+ */
@Nullable
public AmbientBacklightMetadata getMetadata() {
return mMetadata;
@@ -95,7 +111,8 @@
return 0;
}
- public static final @NonNull Parcelable.Creator<AmbientBacklightEvent> CREATOR =
+ @NonNull
+ public static final Parcelable.Creator<AmbientBacklightEvent> CREATOR =
new Parcelable.Creator<AmbientBacklightEvent>() {
public AmbientBacklightEvent createFromParcel(Parcel in) {
return new AmbientBacklightEvent(in);
diff --git a/media/java/android/media/quality/AmbientBacklightMetadata.java b/media/java/android/media/quality/AmbientBacklightMetadata.java
index fc77934..9c11f9a 100644
--- a/media/java/android/media/quality/AmbientBacklightMetadata.java
+++ b/media/java/android/media/quality/AmbientBacklightMetadata.java
@@ -16,6 +16,10 @@
package android.media.quality;
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
+import android.graphics.PixelFormat;
+import android.media.tv.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,9 +28,11 @@
import java.util.Arrays;
/**
+ * Metadata of ambient backlight.
* @hide
*/
-public class AmbientBacklightMetadata implements Parcelable {
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+public final class AmbientBacklightMetadata implements Parcelable {
@NonNull
private final String mPackageName;
private final int mCompressAlgorithm;
@@ -37,6 +43,9 @@
@NonNull
private final int[] mZonesColors;
+ /**
+ * Constructor of AmbientBacklightMetadata.
+ */
public AmbientBacklightMetadata(@NonNull String packageName, int compressAlgorithm,
int source, int colorFormat, int horizontalZonesNumber, int verticalZonesNumber,
@NonNull int[] zonesColors) {
@@ -59,31 +68,58 @@
mZonesColors = in.createIntArray();
}
+ /**
+ * Gets package name.
+ * @hide
+ */
@NonNull
public String getPackageName() {
return mPackageName;
}
+ /**
+ * Gets compress algorithm.
+ */
+ @AmbientBacklightSettings.CompressAlgorithm
public int getCompressAlgorithm() {
return mCompressAlgorithm;
}
+ /**
+ * Gets source of ambient backlight detection.
+ */
+ @AmbientBacklightSettings.Source
public int getSource() {
return mSource;
}
+ /**
+ * Gets color format.
+ */
+ @PixelFormat.Format
public int getColorFormat() {
return mColorFormat;
}
+ /**
+ * Gets the number of lights in each horizontal zone.
+ */
+ @IntRange(from = 0)
public int getHorizontalZonesNumber() {
return mHorizontalZonesNumber;
}
+ /**
+ * Gets the number of lights in each vertical zone.
+ */
+ @IntRange(from = 0)
public int getVerticalZonesNumber() {
return mVerticalZonesNumber;
}
+ /**
+ * @hide
+ */
@NonNull
public int[] getZonesColors() {
return mZonesColors;
diff --git a/media/java/android/media/quality/AmbientBacklightSettings.java b/media/java/android/media/quality/AmbientBacklightSettings.java
index bb782bf..4ed7bc7 100644
--- a/media/java/android/media/quality/AmbientBacklightSettings.java
+++ b/media/java/android/media/quality/AmbientBacklightSettings.java
@@ -16,7 +16,11 @@
package android.media.quality;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.graphics.PixelFormat;
+import android.media.tv.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,7 +33,8 @@
* Settings for ambient backlight.
* @hide
*/
-public class AmbientBacklightSettings implements Parcelable {
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+public final class AmbientBacklightSettings implements Parcelable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({SOURCE_NONE, SOURCE_AUDIO, SOURCE_VIDEO, SOURCE_AUDIO_VIDEO})
@@ -62,6 +67,7 @@
/**
* The color format is RGB888.
+ * @hide
*/
public static final int COLOR_FORMAT_RGB888 = 1;
@@ -76,7 +82,7 @@
public static final int ALGORITHM_NONE = 0;
/**
- * The compress algorithm is RLE.
+ * The compress algorithm is run length encoding (RLE).
*/
public static final int ALGORITHM_RLE = 1;
@@ -115,6 +121,9 @@
*/
private final int mThreshold;
+ /**
+ * Constructs AmbientBacklightSettings.
+ */
public AmbientBacklightSettings(int source, int maxFps, int colorFormat,
int horizontalZonesNumber, int verticalZonesNumber, boolean isLetterboxOmitted,
int threshold) {
@@ -137,32 +146,57 @@
mThreshold = in.readInt();
}
+ /**
+ * Gets source of ambient backlight detection.
+ */
@Source
public int getSource() {
return mSource;
}
+ /**
+ * Gets max frames per second.
+ */
+ @IntRange(from = 1)
public int getMaxFps() {
return mMaxFps;
}
- @ColorFormat
+ /**
+ * Gets color format.
+ */
+ @PixelFormat.Format
public int getColorFormat() {
return mColorFormat;
}
+ /**
+ * Gets the number of lights in each horizontal zone.
+ */
+ @IntRange(from = 0)
public int getHorizontalZonesNumber() {
return mHorizontalZonesNumber;
}
+ /**
+ * Gets the number of lights in each vertical zone.
+ */
+ @IntRange(from = 0)
public int getVerticalZonesNumber() {
return mVerticalZonesNumber;
}
+ /**
+ * Returns {@code true} if letter box is omitted; {@code false} otherwise.
+ * @hide
+ */
public boolean isLetterboxOmitted() {
return mIsLetterboxOmitted;
}
+ /**
+ * @hide
+ */
public int getThreshold() {
return mThreshold;
}
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 26d83ac..4d4526c 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -567,7 +567,6 @@
/**
* Registers a {@link AmbientBacklightCallback}.
- * @hide
*/
public void registerAmbientBacklightCallback(
@NonNull @CallbackExecutor Executor executor,
@@ -581,7 +580,6 @@
/**
* Unregisters the existing {@link AmbientBacklightCallback}.
- * @hide
*/
public void unregisterAmbientBacklightCallback(
@NonNull final AmbientBacklightCallback callback) {
@@ -602,7 +600,6 @@
* Set the ambient backlight settings.
*
* @param settings The settings to use for the backlight detector.
- * @hide
*/
public void setAmbientBacklightSettings(
@NonNull AmbientBacklightSettings settings) {
@@ -618,7 +615,6 @@
* Enables or disables the ambient backlight detection.
*
* @param enabled {@code true} to enable, {@code false} to disable.
- * @hide
*/
public void setAmbientBacklightEnabled(boolean enabled) {
try {
@@ -843,14 +839,12 @@
/**
* Callback used to monitor status of ambient backlight.
- * @hide
*/
public abstract static class AmbientBacklightCallback {
/**
* Called when new ambient backlight event is emitted.
- * @hide
*/
- public void onAmbientBacklightEvent(AmbientBacklightEvent event) {
+ public void onAmbientBacklightEvent(@NonNull AmbientBacklightEvent event) {
}
}
}
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index a23845f..c25f77b 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -107,6 +107,7 @@
method public void onRfDiscoveryStarted(boolean);
method public void onRfFieldActivated(boolean);
method public void onRoutingChanged();
+ method public void onRoutingTableFull();
method public void onStateUpdated(int);
method public void onTagConnected(boolean);
method public void onTagDispatch(@NonNull java.util.function.Consumer<java.lang.Boolean>);
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
index b102e87..fb793b0 100644
--- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -52,5 +52,6 @@
void onNdefMessage(in Tag tag, in NdefMessage message, in ResultReceiver hasOemExecutableContent);
void onLaunchHceAppChooserActivity(in String selectedAid, in List<ApduServiceInfo> services, in ComponentName failedComponent, in String category);
void onLaunchHceTapAgainActivity(in ApduServiceInfo service, in String category);
+ void onRoutingTableFull();
void onLogEventNotified(in OemLogItems item);
}
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index abd99bc..57ee981 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -394,6 +394,13 @@
void onLaunchHceTapAgainDialog(@NonNull ApduServiceInfo service, @NonNull String category);
/**
+ * Callback to indicate that routing table is full and the OEM can optionally launch a
+ * dialog to request the user to remove some Card Emulation apps from the device to free
+ * routing table space.
+ */
+ void onRoutingTableFull();
+
+ /**
* Callback when OEM specified log event are notified.
* @param item the log items that contains log information of NFC event.
*/
@@ -853,6 +860,12 @@
handleVoidCallback(enabled, cb::onReaderOptionChanged, ex));
}
+ public void onRoutingTableFull() throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoidCallback(null,
+ (Object input) -> cb.onRoutingTableFull(), ex));
+ }
+
@Override
public void onGetOemAppSearchIntent(List<String> packages, ResultReceiver intentConsumer)
throws RemoteException {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index fbce6ca..aca2c4e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -16,6 +16,7 @@
package com.android.providers.settings;
+import static android.provider.DeviceConfig.DUMP_ARG_NAMESPACE;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT;
@@ -42,6 +43,7 @@
import android.provider.Settings;
import android.provider.Settings.Config.SyncDisabledMode;
import android.provider.UpdatableDeviceConfigServiceReadiness;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.util.FastPrintWriter;
@@ -55,11 +57,13 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.regex.Pattern;
/**
* Receives shell commands from the command line related to device config flags, and dispatches them
@@ -80,6 +84,7 @@
final SettingsProvider mProvider;
private static final String TAG = "DeviceConfigService";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public DeviceConfigService(SettingsProvider provider) {
mProvider = provider;
@@ -97,14 +102,35 @@
}
}
+ // TODO(b/364399200): add unit test
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ String filter = null;
if (android.provider.flags.Flags.dumpImprovements()) {
- pw.print("SyncDisabledForTests: ");
- MyShellCommand.getSyncDisabledForTests(pw, pw);
+ if (args.length > 0) {
+ switch (args[0]) {
+ case DUMP_ARG_NAMESPACE:
+ if (args.length < 2) {
+ throw new IllegalArgumentException("argument " + DUMP_ARG_NAMESPACE
+ + " requires an extra argument");
+ }
+ filter = args[1];
+ if (DEBUG) {
+ Slog.d(TAG, "dump(): setting filter as " + filter);
+ }
+ break;
+ default:
+ Slog.w(TAG, "dump(): ignoring invalid arguments: " + Arrays.toString(args));
+ break;
+ }
+ }
+ if (filter == null) {
+ pw.print("SyncDisabledForTests: ");
+ MyShellCommand.getSyncDisabledForTests(pw, pw);
- pw.print("UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService(): ");
- pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService());
+ pw.print("UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService(): ");
+ pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService());
+ }
pw.println("DeviceConfig provider: ");
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(fd)) {
@@ -117,8 +143,16 @@
IContentProvider iprovider = mProvider.getIContentProvider();
pw.println("DeviceConfig flags:");
+ Pattern lineFilter = filter == null ? null : Pattern.compile("^.*" + filter + ".*\\/.*$");
for (String line : MyShellCommand.listAll(iprovider)) {
- pw.println(line);
+ if (lineFilter == null || lineFilter.matcher(line).matches()) {
+ pw.println(line);
+ }
+ }
+
+ if (filter != null) {
+ // TODO(b/364399200): use filter to skip instead?
+ return;
}
ArrayList<String> missingFiles = new ArrayList<String>();
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 9392b1a..96e99b1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -66,13 +66,13 @@
interactionHandler = interactionHandler,
dialogFactory = dialogFactory,
widgetSection = widgetSection,
- modifier = Modifier.element(Communal.Elements.Grid)
+ modifier = Modifier.element(Communal.Elements.Grid),
)
}
with(lockSection) {
LockIcon(
overrideColor = MaterialTheme.colorScheme.onPrimaryContainer,
- modifier = Modifier.element(Communal.Elements.LockIcon)
+ modifier = Modifier.element(Communal.Elements.LockIcon),
)
}
with(bottomAreaSection) {
@@ -80,17 +80,13 @@
Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
)
}
- }
+ },
) { measurables, constraints ->
val communalGridMeasurable = measurables[0]
val lockIconMeasurable = measurables[1]
val bottomAreaMeasurable = measurables[2]
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
+ val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0)
val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
val lockIconBounds =
@@ -109,14 +105,8 @@
)
layout(constraints.maxWidth, constraints.maxHeight) {
- communalGridPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
+ communalGridPlaceable.place(x = 0, y = 0)
+ lockIconPlaceable.place(x = lockIconBounds.left, y = lockIconBounds.top)
bottomAreaPlaceable.place(
x = 0,
y = constraints.maxHeight - bottomAreaPlaceable.height,
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 5e1ac1f..df1185c 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
@@ -807,7 +807,6 @@
) {
ResizeableItemFrameViewModel()
}
-
if (viewModel.isEditMode && dragDropState != null) {
val isItemDragging = dragDropState.draggingItemKey == item.key
val outlineAlpha by
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index 6e30575..16002bc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -59,7 +59,14 @@
private val onAddWidget: (componentName: ComponentName, user: UserHandle, rank: Int) -> Unit,
private val onDeleteWidget: (id: Int, componentName: ComponentName, rank: Int) -> Unit,
private val onReorderWidgets: (widgetIdToRankMap: Map<Int, Int>) -> Unit,
- private val onResizeWidget: (id: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) -> Unit,
+ private val onResizeWidget:
+ (
+ id: Int,
+ spanY: Int,
+ widgetIdToRankMap: Map<Int, Int>,
+ componentName: ComponentName,
+ rank: Int,
+ ) -> Unit,
) {
var list = communalContent.toMutableStateList()
private set
@@ -105,7 +112,9 @@
} else {
emptyMap()
}
- onResizeWidget(item.appWidgetId, newSpan, widgetIdToRankMap)
+ val componentName = item.componentName
+ val rank = item.rank
+ onResizeWidget(item.appWidgetId, newSpan, widgetIdToRankMap, componentName, rank)
}
/**
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
index 82918a5..af2d7e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt
@@ -47,20 +47,14 @@
@Test
fun logAddWidget_componentNotLoggable_doNotLog() {
- underTest.logAddWidget(
- componentName = "com.green.package/my_test_widget",
- rank = 1,
- )
+ underTest.logAddWidget(componentName = "com.green.package/my_test_widget", rank = 1)
verify(statsLogProxy, never())
- .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+ .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
}
@Test
fun logAddWidget_componentLoggable_logAddEvent() {
- underTest.logAddWidget(
- componentName = "com.blue.package/my_test_widget",
- rank = 1,
- )
+ underTest.logAddWidget(componentName = "com.blue.package/my_test_widget", rank = 1)
verify(statsLogProxy)
.writeCommunalHubWidgetEventReported(
SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__ADD,
@@ -71,20 +65,14 @@
@Test
fun logRemoveWidget_componentNotLoggable_doNotLog() {
- underTest.logRemoveWidget(
- componentName = "com.yellow.package/my_test_widget",
- rank = 2,
- )
+ underTest.logRemoveWidget(componentName = "com.yellow.package/my_test_widget", rank = 2)
verify(statsLogProxy, never())
- .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+ .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
}
@Test
fun logRemoveWidget_componentLoggable_logRemoveEvent() {
- underTest.logRemoveWidget(
- componentName = "com.red.package/my_test_widget",
- rank = 2,
- )
+ underTest.logRemoveWidget(componentName = "com.red.package/my_test_widget", rank = 2)
verify(statsLogProxy)
.writeCommunalHubWidgetEventReported(
SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__REMOVE,
@@ -95,20 +83,14 @@
@Test
fun logTapWidget_componentNotLoggable_doNotLog() {
- underTest.logTapWidget(
- componentName = "com.yellow.package/my_test_widget",
- rank = 2,
- )
+ underTest.logTapWidget(componentName = "com.yellow.package/my_test_widget", rank = 2)
verify(statsLogProxy, never())
- .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt())
+ .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
}
@Test
fun logTapWidget_componentLoggable_logRemoveEvent() {
- underTest.logTapWidget(
- componentName = "com.red.package/my_test_widget",
- rank = 2,
- )
+ underTest.logTapWidget(componentName = "com.red.package/my_test_widget", rank = 2)
verify(statsLogProxy)
.writeCommunalHubWidgetEventReported(
SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__TAP,
@@ -140,4 +122,43 @@
)
assertThat(statsEvents).hasSize(1)
}
+
+ @Test
+ fun logResizeWidget_componentNotLoggable_doNotLog() {
+ underTest.logResizeWidget(
+ componentName = "com.green.package/my_test_widget",
+ rank = 1,
+ spanY = 2,
+ )
+ verify(statsLogProxy, never())
+ .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt())
+ }
+
+ @Test
+ fun logResizeWidget_componentLoggable_logResizeEvent() {
+ underTest.logResizeWidget(
+ componentName = "com.blue.package/my_test_widget",
+ rank = 1,
+ spanY = 2,
+ )
+ verify(statsLogProxy)
+ .writeCommunalHubWidgetEventReported(
+ SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE,
+ "com.blue.package/my_test_widget",
+ rank = 1,
+ spanY = 2,
+ )
+ }
+
+ @Test
+ fun logResizeWidget_defaultSpanY_usesDefaultValue() {
+ underTest.logResizeWidget(componentName = "com.blue.package/my_test_widget", rank = 1)
+ verify(statsLogProxy)
+ .writeCommunalHubWidgetEventReported(
+ SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE,
+ "com.blue.package/my_test_widget",
+ rank = 1,
+ spanY = 0,
+ )
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index cecc11e..6b851cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -353,6 +353,32 @@
assertThat(event.contentDescription).isEqualTo("Test Clock widget added to lock screen")
}
+ @Test
+ fun onResizeWidget_logsMetrics() =
+ testScope.runTest {
+ val appWidgetId = 123
+ val spanY = 2
+ val widgetIdToRankMap = mapOf(appWidgetId to 1)
+ val componentName = ComponentName("test.package", "TestWidget")
+ val rank = 1
+
+ underTest.onResizeWidget(
+ appWidgetId = appWidgetId,
+ spanY = spanY,
+ widgetIdToRankMap = widgetIdToRankMap,
+ componentName = componentName,
+ rank = rank,
+ )
+
+ verify(communalInteractor).resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
+ verify(metricsLogger)
+ .logResizeWidget(
+ componentName = componentName.flattenToString(),
+ rank = rank,
+ spanY = spanY,
+ )
+ }
+
private companion object {
val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
const val WIDGET_PICKER_PACKAGE_NAME = "widget_picker_package_name"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
index 03feceb..b72668b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -18,6 +18,7 @@
import android.app.StatusBarManager
import android.content.testableContext
+import android.graphics.Rect
import android.testing.TestableLooper.RunWithLooper
import androidx.compose.runtime.snapshots.Snapshot
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -43,6 +44,7 @@
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
import com.android.systemui.statusbar.sysuiStatusBarStateController
+import com.android.systemui.util.animation.DisappearParameters
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -375,6 +377,92 @@
}
}
+ @Test
+ fun applyQsScrollPositionForClipping() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ val left = 1f
+ val top = 3f
+ val right = 5f
+ val bottom = 7f
+
+ underTest.applyNewQsScrollerBounds(left, top, right, bottom)
+
+ assertThat(qsMediaHost.currentClipping)
+ .isEqualTo(Rect(left.toInt(), top.toInt(), right.toInt(), bottom.toInt()))
+ }
+ }
+
+ @Test
+ fun shouldUpdateMediaSquishiness_inSplitShadeFalse_mediaSquishinessSet() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ underTest.isInSplitShade = false
+ underTest.squishinessFraction = 0.3f
+
+ underTest.shouldUpdateSquishinessOnMedia = true
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+
+ assertThat(underTest.qsMediaHost.squishFraction).isWithin(0.01f).of(0.3f)
+
+ underTest.shouldUpdateSquishinessOnMedia = false
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qsMediaHost.squishFraction).isWithin(0.01f).of(1f)
+ }
+ }
+
+ @Test
+ fun inSplitShade_differentStatusBarState_mediaSquishinessSet() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ underTest.isInSplitShade = true
+ underTest.squishinessFraction = 0.3f
+
+ sysuiStatusBarStateController.setState(StatusBarState.SHADE)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(0.3f)
+
+ sysuiStatusBarStateController.setState(StatusBarState.KEYGUARD)
+ runCurrent()
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(1f)
+
+ sysuiStatusBarStateController.setState(StatusBarState.SHADE_LOCKED)
+ runCurrent()
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+ assertThat(underTest.qsMediaHost.squishFraction).isWithin(epsilon).of(1f)
+ }
+ }
+
+ @Test
+ fun disappearParams() =
+ with(kosmos) {
+ testScope.testWithinLifecycle {
+ setMediaState(ACTIVE_MEDIA)
+
+ setConfigurationForMediaInRow(false)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+
+ assertThat(underTest.qqsMediaHost.disappearParameters)
+ .isEqualTo(disappearParamsColumn)
+ assertThat(underTest.qsMediaHost.disappearParameters)
+ .isEqualTo(disappearParamsColumn)
+
+ setConfigurationForMediaInRow(true)
+ Snapshot.sendApplyNotifications()
+ runCurrent()
+
+ assertThat(underTest.qqsMediaHost.disappearParameters).isEqualTo(disappearParamsRow)
+ assertThat(underTest.qsMediaHost.disappearParameters).isEqualTo(disappearParamsRow)
+ }
+ }
+
private fun TestScope.setMediaState(state: MediaState) {
with(kosmos) {
val activeMedia = state == ACTIVE_MEDIA
@@ -404,6 +492,26 @@
}
private const val epsilon = 0.001f
+
+ private val disappearParamsColumn =
+ DisappearParameters().apply {
+ fadeStartPosition = 0.95f
+ disappearStart = 0f
+ disappearEnd = 0.95f
+ disappearSize.set(1f, 0f)
+ gonePivot.set(0f, 0f)
+ contentTranslationFraction.set(0f, 1f)
+ }
+
+ private val disappearParamsRow =
+ DisappearParameters().apply {
+ fadeStartPosition = 0.95f
+ disappearStart = 0f
+ disappearEnd = 0.6f
+ disappearSize.set(0f, 0.4f)
+ gonePivot.set(1f, 0f)
+ contentTranslationFraction.set(0.25f, 1f)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 152911a..af0274b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -44,6 +44,7 @@
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
import com.android.systemui.classifier.FalsingCollector
@@ -54,6 +55,7 @@
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.flags.EnableSceneContainer
@@ -114,6 +116,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
@@ -2356,6 +2359,58 @@
assertThat(isLockscreenEnabled).isTrue()
}
+ @Test
+ fun replacesLockscreenSceneOnBackStack_whenUnlockdViaAlternateBouncer_fromShade() =
+ testScope.runTest {
+ val transitionState =
+ prepareState(
+ isDeviceUnlocked = false,
+ initialSceneKey = Scenes.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ )
+ underTest.start()
+
+ val isUnlocked by
+ collectLastValue(
+ kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked }
+ )
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val backStack by collectLastValue(sceneBackInteractor.backStack)
+ val isAlternateBouncerVisible by
+ collectLastValue(kosmos.alternateBouncerInteractor.isVisible)
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isAlternateBouncerVisible).isFalse()
+
+ // Change to shade.
+ sceneInteractor.changeScene(Scenes.Shade, "")
+ transitionState.value = ObservableTransitionState.Idle(Scenes.Shade)
+ runCurrent()
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen)
+ assertThat(isAlternateBouncerVisible).isFalse()
+
+ // Show the alternate bouncer.
+ kosmos.alternateBouncerInteractor.forceShow()
+ kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open
+ runCurrent()
+ assertThat(isUnlocked).isFalse()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen)
+ assertThat(isAlternateBouncerVisible).isTrue()
+
+ // Trigger a fingerprint unlock.
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ runCurrent()
+ assertThat(isUnlocked).isTrue()
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
+ assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Gone)
+ assertThat(isAlternateBouncerVisible).isFalse()
+ }
+
private fun TestScope.emulateSceneTransition(
transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
toScene: SceneKey,
diff --git a/packages/SystemUI/res/layout/split_clock_view.xml b/packages/SystemUI/res/layout/split_clock_view.xml
deleted file mode 100644
index 8198f03..0000000
--- a/packages/SystemUI/res/layout/split_clock_view.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 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
- -->
-
-<!-- Extends LinearLayout -->
-<com.android.systemui.statusbar.policy.SplitClockView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- >
- <TextClock
- android:id="@+id/time_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
- android:textSize="@dimen/qs_time_collapsed_size"
- android:textColor="?android:attr/textColorPrimary"
- />
- <TextClock
- android:id="@+id/am_pm_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
- android:textSize="@dimen/qs_time_collapsed_size"
- android:textColor="?android:attr/textColorPrimary"
- android:importantForAccessibility="no"
- />
-
- <!-- Empty text view so we have the same height when expanded/collapsed-->
- <TextView
- android:id="@+id/empty_time_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
- android:textColor="?android:attr/textColorPrimary"
- />
-</com.android.systemui.statusbar.policy.SplitClockView>
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
index 7cfad60..6423f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
@@ -30,6 +30,7 @@
@Named(LOGGABLE_PREFIXES) private val loggablePrefixes: List<String>,
private val statsLogProxy: StatsLogProxy,
) {
+
/** Logs an add widget event for metrics. No-op if widget is not loggable. */
fun logAddWidget(componentName: String, rank: Int?) {
if (!componentName.isLoggable()) {
@@ -69,11 +70,21 @@
)
}
+ fun logResizeWidget(componentName: String, rank: Int, spanY: Int = 0) {
+ if (!componentName.isLoggable()) {
+ return
+ }
+
+ statsLogProxy.writeCommunalHubWidgetEventReported(
+ SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE,
+ componentName,
+ rank = rank,
+ spanY = spanY,
+ )
+ }
+
/** Logs loggable widgets and the total widget count as a [StatsEvent]. */
- fun logWidgetsSnapshot(
- statsEvents: MutableList<StatsEvent>,
- componentNames: List<String>,
- ) {
+ fun logWidgetsSnapshot(statsEvents: MutableList<StatsEvent>, componentNames: List<String>) {
val loggableComponentNames = componentNames.filter { it.isLoggable() }.toTypedArray()
statsEvents.add(
statsLogProxy.buildCommunalHubSnapshotStatsEvent(
@@ -95,6 +106,7 @@
action: Int,
componentName: String,
rank: Int,
+ spanY: Int = 0,
)
/** Builds a [SysUiStatsLog.COMMUNAL_HUB_SNAPSHOT] stats event. */
@@ -112,12 +124,14 @@
action: Int,
componentName: String,
rank: Int,
+ spanY: Int,
) {
SysUiStatsLog.write(
SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED,
action,
componentName,
rank,
+ spanY,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index e25ea4c..4e0e112 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -178,7 +178,13 @@
* alongside the resize, in case resizing also requires re-ordering. This ensures the
* re-ordering is done in the same database transaction as the resize.
*/
- open fun onResizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {}
+ open fun onResizeWidget(
+ appWidgetId: Int,
+ spanY: Int,
+ widgetIdToRankMap: Map<Int, Int>,
+ componentName: ComponentName,
+ rank: Int,
+ ) {}
/** Called as the UI requests opening the widget editor with an optional preselected widget. */
open fun onOpenWidgetEditor(shouldOpenWidgetPickerOnStart: Boolean = false) {}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 6508e4b5..ccff230 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -141,8 +141,19 @@
override fun onReorderWidgets(widgetIdToRankMap: Map<Int, Int>) =
communalInteractor.updateWidgetOrder(widgetIdToRankMap)
- override fun onResizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
+ override fun onResizeWidget(
+ appWidgetId: Int,
+ spanY: Int,
+ widgetIdToRankMap: Map<Int, Int>,
+ componentName: ComponentName,
+ rank: Int,
+ ) {
communalInteractor.resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
+ metricsLogger.logResizeWidget(
+ componentName = componentName.flattenToString(),
+ rank = rank,
+ spanY = spanY,
+ )
}
override fun onReorderWidgetStart() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index 51ff598..ec6a17b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -70,6 +70,7 @@
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.layout.positionOnScreen
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
@@ -124,6 +125,7 @@
import com.android.systemui.qs.ui.composable.QuickSettingsTheme
import com.android.systemui.res.R
import com.android.systemui.util.LifecycleFragment
+import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printSection
import com.android.systemui.util.println
@@ -447,8 +449,7 @@
}
override fun setShouldUpdateSquishinessOnMedia(shouldUpdate: Boolean) {
- super.setShouldUpdateSquishinessOnMedia(shouldUpdate)
- // TODO (b/353253280)
+ viewModel.shouldUpdateSquishinessOnMedia = shouldUpdate
}
override fun setInSplitShade(isInSplitShade: Boolean) {
@@ -660,7 +661,20 @@
Column(
modifier =
- Modifier.offset {
+ Modifier.onPlaced { coordinates ->
+ val positionOnScreen = coordinates.positionOnScreen()
+ val left = positionOnScreen.x
+ val right = left + coordinates.size.width
+ val top = positionOnScreen.y
+ val bottom = top + coordinates.size.height
+ viewModel.applyNewQsScrollerBounds(
+ left = left,
+ top = top,
+ right = right,
+ bottom = bottom,
+ )
+ }
+ .offset {
IntOffset(
x = 0,
y = viewModel.qsScrollTranslationY.fastRoundToInt(),
@@ -704,7 +718,10 @@
val Media =
@Composable {
if (viewModel.qsMediaVisible) {
- MediaObject(mediaHost = viewModel.qsMediaHost)
+ MediaObject(
+ mediaHost = viewModel.qsMediaHost,
+ update = { translationY = viewModel.qsMediaTranslationY },
+ )
}
}
Box(
@@ -987,7 +1004,11 @@
}
@Composable
-private fun MediaObject(mediaHost: MediaHost, modifier: Modifier = Modifier) {
+private fun MediaObject(
+ mediaHost: MediaHost,
+ modifier: Modifier = Modifier,
+ update: UniqueObjectHostView.() -> Unit = {},
+) {
Box {
AndroidView(
modifier = modifier,
@@ -1000,6 +1021,7 @@
)
}
},
+ update = { view -> view.update() },
onReset = {},
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index e912a0c..9029563 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -26,6 +26,7 @@
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.BouncerPanelExpansionCalculator
import com.android.systemui.Dumpable
@@ -270,6 +271,23 @@
val qsMediaInRow: Boolean
get() = qsMediaInRowViewModel.shouldMediaShowInRow
+ var shouldUpdateSquishinessOnMedia by mutableStateOf(false)
+
+ val qsMediaTranslationY by derivedStateOf {
+ if (
+ qsExpansion > 0f &&
+ !isKeyguardState &&
+ !qqsMediaVisible &&
+ !qsMediaInRow &&
+ !isInSplitShade
+ ) {
+ val interpolation = Interpolators.ACCELERATE.getInterpolation(1f - qsExpansion)
+ -qsMediaHost.hostView.height * 1.3f * interpolation
+ } else {
+ 0f
+ }
+ }
+
val animateTilesExpansion: Boolean
get() = inFirstPage && !mediaSuddenlyAppearingInLandscape
@@ -297,6 +315,18 @@
MediaHostState.EXPANDED
}
+ private val shouldApplySquishinessToMedia by derivedStateOf {
+ shouldUpdateSquishinessOnMedia || (isInSplitShade && statusBarState == StatusBarState.SHADE)
+ }
+
+ private val mediaSquishiness by derivedStateOf {
+ if (shouldApplySquishinessToMedia) {
+ squishinessFraction
+ } else {
+ 1f
+ }
+ }
+
private var qsBounds by mutableStateOf(Rect())
private val constrainedSquishinessFraction: Float
@@ -355,8 +385,6 @@
private val isOverscrolling: Boolean
get() = overScrollAmount != 0
- private var shouldUpdateMediaSquishiness by mutableStateOf(false)
-
private val forceQs by derivedStateOf {
(isQsExpanded || isStackScrollerOverscrolling) &&
(isKeyguardState && !showCollapsedOnKeyguard)
@@ -394,11 +422,26 @@
),
)
+ fun applyNewQsScrollerBounds(left: Float, top: Float, right: Float, bottom: Float) {
+ if (usingMedia) {
+ qsMediaHost.currentClipping.set(
+ left.toInt(),
+ top.toInt(),
+ right.toInt(),
+ bottom.toInt(),
+ )
+ }
+ }
+
override suspend fun onActivated(): Nothing {
initMediaHosts() // init regardless of using media (same as current QS).
coroutineScope {
launch { hydrateSquishinessInteractor() }
- launch { hydrateQqsMediaExpansion() }
+ if (usingMedia) {
+ launch { hydrateQqsMediaExpansion() }
+ launch { hydrateMediaSquishiness() }
+ launch { hydrateMediaDisappearParameters() }
+ }
launch { hydrator.activate() }
launch { containerViewModel.activate() }
launch { qqsMediaInRowViewModel.activate() }
@@ -429,6 +472,21 @@
snapshotFlow { qqsMediaExpansion }.collect { qqsMediaHost.expansion = it }
}
+ private suspend fun hydrateMediaSquishiness() {
+ snapshotFlow { mediaSquishiness }.collect { qsMediaHost.squishFraction = it }
+ }
+
+ private suspend fun hydrateMediaDisappearParameters() {
+ coroutineScope {
+ launch {
+ snapshotFlow { qqsMediaInRow }.collect { qqsMediaHost.applyDisappearParameters(it) }
+ }
+ launch {
+ snapshotFlow { qsMediaInRow }.collect { qsMediaHost.applyDisappearParameters(it) }
+ }
+ }
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.asIndenting().run {
printSection("Quick Settings state") {
@@ -474,6 +532,9 @@
println("qsMediaInRow", qsMediaInRow)
println("collapsedLandscapeMedia", collapsedLandscapeMedia)
println("qqsMediaExpansion", qqsMediaExpansion)
+ println("shouldUpdateSquishinessOnMedia", shouldUpdateSquishinessOnMedia)
+ println("mediaSquishiness", mediaSquishiness)
+ println("qsMediaTranslationY", qsMediaTranslationY)
}
}
}
@@ -510,3 +571,22 @@
// lazily.
.onStart { emit(mediaHost.visible) }
}
+
+// Taken from QSPanelControllerBase
+private fun MediaHost.applyDisappearParameters(inRow: Boolean) {
+ disappearParameters.apply {
+ fadeStartPosition = 0.95f
+ disappearStart = 0f
+ if (inRow) {
+ disappearSize.set(0f, 0.4f)
+ gonePivot.set(1f, 0f)
+ contentTranslationFraction.set(0.25f, 1f)
+ disappearEnd = 0.6f
+ } else {
+ disappearSize.set(1f, 0f)
+ gonePivot.set(0f, 0f)
+ contentTranslationFraction.set(0f, 1f)
+ disappearEnd = 0.95f
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index 7ff43c6..fe59c4d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -130,15 +130,7 @@
// TODO(b/361789146): Draw the shapes instead of clipping
val tileShape = TileDefaults.animateTileShape(uiState.state)
- val animatedColor by
- animateColorAsState(
- if (iconOnly || !uiState.handlesSecondaryClick) {
- colors.iconBackground
- } else {
- colors.background
- },
- label = "QSTileBackgroundColor",
- )
+ val animatedColor by animateColorAsState(colors.background, label = "QSTileBackgroundColor")
TileExpandable(
color = { animatedColor },
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 580a51a..daeaaa5 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -373,6 +373,7 @@
"device was unlocked with alternate bouncer showing" +
" and shade didn't need to be left open"
} else {
+ replaceLockscreenSceneOnBackStack()
null
}
}
@@ -391,16 +392,7 @@
val prevScene = previousScene.value
val targetScene = prevScene ?: Scenes.Gone
if (targetScene != Scenes.Gone) {
- sceneBackInteractor.updateBackStack { stack ->
- val list = stack.asIterable().toMutableList()
- check(list.last() == Scenes.Lockscreen) {
- "The bottommost/last SceneKey of the back stack isn't" +
- " the Lockscreen scene like expected. The back" +
- " stack is $stack."
- }
- list[list.size - 1] = Scenes.Gone
- sceneStackOf(*list.toTypedArray())
- }
+ replaceLockscreenSceneOnBackStack()
}
targetScene to
"device was unlocked with primary bouncer showing," +
@@ -435,6 +427,20 @@
}
}
+ /** If the [Scenes.Lockscreen] is on the backstack, replaces it with [Scenes.Gone]. */
+ private fun replaceLockscreenSceneOnBackStack() {
+ sceneBackInteractor.updateBackStack { stack ->
+ val list = stack.asIterable().toMutableList()
+ check(list.last() == Scenes.Lockscreen) {
+ "The bottommost/last SceneKey of the back stack isn't" +
+ " the Lockscreen scene like expected. The back" +
+ " stack is $stack."
+ }
+ list[list.size - 1] = Scenes.Gone
+ sceneStackOf(*list.toTypedArray())
+ }
+ }
+
private fun handlePowerState() {
applicationScope.launch {
powerInteractor.detailedWakefulness.collect { wakefulness ->
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 24dba59..4d77e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -17,8 +17,6 @@
package com.android.systemui.shade;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -27,16 +25,13 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
import android.view.Display;
-import android.view.Gravity;
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.View;
@@ -271,33 +266,7 @@
// Now that the notification shade encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
// hardware-accelerated.
- mLp = new LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT,
- LayoutParams.TYPE_NOTIFICATION_SHADE,
- LayoutParams.FLAG_NOT_FOCUSABLE
- | LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
- | LayoutParams.FLAG_SPLIT_TOUCH
- | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- PixelFormat.TRANSLUCENT);
- mLp.token = new Binder();
- mLp.gravity = Gravity.TOP;
- mLp.setFitInsetsTypes(0 /* types */);
- mLp.setTitle("NotificationShade");
- mLp.packageName = mContext.getPackageName();
- mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mLp.privateFlags |= PRIVATE_FLAG_OPTIMIZE_MEASURE;
-
- if (SceneContainerFlag.isEnabled()) {
- // This prevents the appearance and disappearance of the software keyboard (also known
- // as the "IME") from scrolling/panning the window to make room for the keyboard.
- //
- // The scene container logic does its own adjustment and animation when the IME appears
- // or disappears.
- mLp.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
- }
-
+ mLp = ShadeWindowLayoutParams.INSTANCE.create(mContext);
mWindowManager.addView(mWindowRootView, mLp);
// We use BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE here, however, there is special logic in
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt
new file mode 100644
index 0000000..6bb50f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.os.Binder
+import android.view.Gravity
+import android.view.ViewGroup
+import android.view.WindowManager.LayoutParams
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+
+object ShadeWindowLayoutParams {
+ /**
+ * Creates [LayoutParams] for the shade window.
+ *
+ * This is extracted to a single place as those layout params will be used by several places:
+ * - When sysui starts, and the shade is added the first time
+ * - When the shade moves to a different window (e.g. while an external display is connected)
+ */
+ fun create(context: Context): LayoutParams {
+ return LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ LayoutParams.TYPE_NOTIFICATION_SHADE,
+ LayoutParams.FLAG_NOT_FOCUSABLE or
+ LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING or
+ LayoutParams.FLAG_SPLIT_TOUCH or
+ LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
+ LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+ // Now that the notification shade encompasses the sliding panel and its
+ // translucent backdrop, the entire thing is made TRANSLUCENT and is
+ // hardware-accelerated.
+ PixelFormat.TRANSLUCENT,
+ )
+ .apply {
+ token = Binder()
+ gravity = Gravity.TOP
+ fitInsetsTypes = 0
+ title = "NotificationShade"
+ packageName = context.packageName
+ layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ privateFlags = privateFlags or LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE
+ if (SceneContainerFlag.isEnabled) {
+ // This prevents the appearance and disappearance of the software keyboard (also
+ // known as the "IME") from scrolling/panning the window to make room for the
+ // keyboard.
+ //
+ // The scene container logic does its own adjustment and animation when the IME
+ // appears or disappears.
+ softInputMode = LayoutParams.SOFT_INPUT_ADJUST_NOTHING
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index e74ed8d..c487ff5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -49,6 +49,7 @@
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
+import android.util.Log;
import android.view.ContentInfo;
import androidx.annotation.NonNull;
@@ -65,6 +66,7 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.icon.IconPack;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -194,6 +196,10 @@
*/
private boolean mIsDemoted = false;
+ // TODO(b/377565433): Move into NotificationContentModel during/after
+ // NotificationRowContentBinderRefactor.
+ private PromotedNotificationContentModel mPromotedNotificationContentModel;
+
/**
* True if both
* 1) app provided full screen intent but does not have the permission to send it
@@ -1061,6 +1067,32 @@
this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarModel.getPublicText());
}
+ /**
+ * Gets the content needed to render this notification as a promoted notification on various
+ * surfaces (like status bar chips and AOD).
+ */
+ public PromotedNotificationContentModel getPromotedNotificationContentModel() {
+ if (PromotedNotificationContentModel.featureFlagEnabled()) {
+ return mPromotedNotificationContentModel;
+ } else {
+ Log.wtf(TAG, "getting promoted content without feature flag enabled");
+ return null;
+ }
+ }
+
+ /**
+ * Sets the content needed to render this notification as a promoted notification on various
+ * surfaces (like status bar chips and AOD).
+ */
+ public void setPromotedNotificationContentModel(
+ @Nullable PromotedNotificationContentModel promotedNotificationContentModel) {
+ if (PromotedNotificationContentModel.featureFlagEnabled()) {
+ this.mPromotedNotificationContentModel = promotedNotificationContentModel;
+ } else {
+ Log.wtf(TAG, "setting promoted content without feature flag enabled");
+ }
+ }
+
/** Information about a suggestion that is being edited. */
public static class EditedSuggestionInfo {
@@ -1101,4 +1133,6 @@
private static final long INITIALIZATION_DELAY = 400;
private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
private static final int COLOR_INVALID = 1;
+
+ private static final String TAG = "NotificationEntry";
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index 23da90d..8edbc5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -34,6 +34,7 @@
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProvider
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
@@ -173,6 +174,7 @@
isGroupSummary = sbn.notification.isGroupSummary,
bucket = bucket,
callType = sbn.toCallType(),
+ promotedContent = promotedNotificationContentModel,
)
}
}
@@ -199,6 +201,7 @@
isGroupSummary: Boolean,
bucket: Int,
callType: CallType,
+ promotedContent: PromotedNotificationContentModel?,
): ActiveNotificationModel {
return individuals[key]?.takeIf {
it.isCurrent(
@@ -223,6 +226,7 @@
contentIntent = contentIntent,
bucket = bucket,
callType = callType,
+ promotedContent = promotedContent,
)
}
?: ActiveNotificationModel(
@@ -247,6 +251,7 @@
contentIntent = contentIntent,
bucket = bucket,
callType = callType,
+ promotedContent = promotedContent,
)
}
@@ -272,6 +277,7 @@
isGroupSummary: Boolean,
bucket: Int,
callType: CallType,
+ promotedContent: PromotedNotificationContentModel?,
): Boolean {
return when {
key != this.key -> false
@@ -295,6 +301,9 @@
contentIntent != this.contentIntent -> false
bucket != this.bucket -> false
callType != this.callType -> false
+ // QQQ: Do we need to do the same `isCurrent` thing within the content model to avoid
+ // recreating the active notification model constantly?
+ promotedContent != this.promotedContent -> false
else -> true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
new file mode 100644
index 0000000..41ee3b9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.promoted.shared.model
+
+import android.annotation.DrawableRes
+import android.graphics.drawable.Icon
+import com.android.internal.widget.NotificationProgressModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
+
+/**
+ * The content needed to render a promoted notification to surfaces besides the notification stack,
+ * like the skeleton view on AOD or the status bar chip.
+ */
+data class PromotedNotificationContentModel(
+ val key: String,
+
+ // for all styles:
+ val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+ val appName: CharSequence?,
+ val subText: CharSequence?,
+ val time: When?,
+ val lastAudiblyAlertedMs: Long,
+ @DrawableRes val profileBadgeResId: Int?,
+ val title: CharSequence?,
+ val text: CharSequence?,
+ val skeletonLargeIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+ val style: Style,
+
+ // for CallStyle:
+ val personIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+ val personName: CharSequence?,
+ val verificationIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
+ val verificationText: CharSequence?,
+
+ // for ProgressStyle:
+ val progress: NotificationProgressModel?,
+) {
+ class Builder(val key: String) {
+ var skeletonSmallIcon: Icon? = null
+ var appName: CharSequence? = null
+ var subText: CharSequence? = null
+ var time: When? = null
+ var lastAudiblyAlertedMs: Long = 0L
+ @DrawableRes var profileBadgeResId: Int? = null
+ var title: CharSequence? = null
+ var text: CharSequence? = null
+ var skeletonLargeIcon: Icon? = null
+ var style: Style = Style.Ineligible
+
+ // for CallStyle:
+ var personIcon: Icon? = null
+ var personName: CharSequence? = null
+ var verificationIcon: Icon? = null
+ var verificationText: CharSequence? = null
+
+ // for ProgressStyle:
+ var progress: NotificationProgressModel? = null
+
+ fun build() =
+ PromotedNotificationContentModel(
+ key = key,
+ skeletonSmallIcon = skeletonSmallIcon,
+ appName = appName,
+ subText = subText,
+ time = time,
+ lastAudiblyAlertedMs = lastAudiblyAlertedMs,
+ profileBadgeResId = profileBadgeResId,
+ title = title,
+ text = text,
+ skeletonLargeIcon = skeletonLargeIcon,
+ style = style,
+ personIcon = personIcon,
+ personName = personName,
+ verificationIcon = verificationIcon,
+ verificationText = verificationText,
+ progress = progress,
+ )
+ }
+
+ /** The timestamp associated with a notification, along with the mode used to display it. */
+ data class When(val time: Long, val mode: Mode) {
+ /** The mode used to display a notification's `when` value. */
+ enum class Mode {
+ Absolute,
+ CountDown,
+ CountUp,
+ }
+ }
+
+ /** The promotion-eligible style of a notification, or [Style.Ineligible] if not. */
+ enum class Style {
+ BigPicture,
+ BigText,
+ Call,
+ Progress,
+ Ineligible,
+ }
+
+ companion object {
+ @JvmStatic
+ fun featureFlagEnabled(): Boolean =
+ PromotedNotificationUi.isEnabled || StatusBarNotifChips.isEnabled
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
index 19a92a2..a2b7155 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
@@ -17,7 +17,9 @@
import android.app.PendingIntent
import android.graphics.drawable.Icon
+import android.util.Log
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.stack.PriorityBucket
/**
@@ -36,6 +38,7 @@
val groupKey: String?,
/** When this notification was posted. */
val whenTime: Long,
+ // TODO(b/377566661): Make isPromoted just check if promotedContent != null.
/** True if this notification should be promoted and false otherwise. */
val isPromoted: Boolean,
/** Is this entry in the ambient / minimized section (lowest priority)? */
@@ -78,7 +81,24 @@
@PriorityBucket val bucket: Int,
/** The call type set on the notification. */
val callType: CallType,
-) : ActiveNotificationEntryModel()
+ /**
+ * The content needed to render this as a promoted notification on various surfaces, or null if
+ * this notification cannot be rendered as a promoted notification.
+ */
+ val promotedContent: PromotedNotificationContentModel?,
+) : ActiveNotificationEntryModel() {
+ init {
+ if (!PromotedNotificationContentModel.featureFlagEnabled()) {
+ if (promotedContent != null) {
+ Log.wtf(TAG, "passing non-null promoted content without feature flag enabled")
+ }
+ }
+ }
+
+ companion object {
+ private const val TAG = "ActiveNotificationEntryModel"
+ }
+}
/** Model for a group of notifications. */
data class ActiveNotificationGroupModel(
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 0c511aea..aef26de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -520,6 +520,7 @@
mListenForCanShowAlternateBouncer.cancel(null);
}
mListenForCanShowAlternateBouncer = null;
+
// Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot.
mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow(
mAlternateBouncerInteractor.getCanShowAlternateBouncer(),
@@ -568,6 +569,12 @@
}
private void consumeCanShowAlternateBouncer(boolean canShow) {
+ if (SceneContainerFlag.isEnabled()) {
+ // When the scene framework is enabled, the alternative bouncer is hidden from the scene
+ // framework logic so there's no need for this logic here.
+ return;
+ }
+
// Hack: this is required to fix issues where
// KeyguardBouncerRepository#alternateBouncerVisible state is incorrectly set and then never
// reset. This is caused by usages of show()/forceShow() that only read this flow to set the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java
deleted file mode 100644
index 0d36b48..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2014 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.statusbar.policy;
-
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.UserHandle;
-import android.text.format.DateFormat;
-import android.util.AttributeSet;
-import android.widget.LinearLayout;
-import android.widget.TextClock;
-
-import com.android.systemui.res.R;
-
-/**
- * Container for a clock which has two separate views for the clock itself and AM/PM indicator. This
- * is used to scale the clock independently of AM/PM.
- */
-public class SplitClockView extends LinearLayout {
-
- private TextClock mTimeView;
- private TextClock mAmPmView;
-
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_TIME_CHANGED.equals(action)
- || Intent.ACTION_TIMEZONE_CHANGED.equals(action)
- || Intent.ACTION_LOCALE_CHANGED.equals(action)
- || Intent.ACTION_CONFIGURATION_CHANGED.equals(action)
- || Intent.ACTION_USER_SWITCHED.equals(action)) {
- updatePatterns();
- }
- }
- };
-
- public SplitClockView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mTimeView = findViewById(R.id.time_view);
- mAmPmView = findViewById(R.id.am_pm_view);
- mTimeView.setShowCurrentUserTime(true);
- mAmPmView.setShowCurrentUserTime(true);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
-
- updatePatterns();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- getContext().unregisterReceiver(mIntentReceiver);
- }
-
- private void updatePatterns() {
- String formatString = DateFormat.getTimeFormatString(getContext(),
- ActivityManager.getCurrentUser());
- int index = getAmPmPartEndIndex(formatString);
- String timeString;
- String amPmString;
- if (index == -1) {
- timeString = formatString;
- amPmString = "";
- } else {
- timeString = formatString.substring(0, index);
- amPmString = formatString.substring(index);
- }
- mTimeView.setFormat12Hour(timeString);
- mTimeView.setFormat24Hour(timeString);
- mTimeView.setContentDescriptionFormat12Hour(formatString);
- mTimeView.setContentDescriptionFormat24Hour(formatString);
- mAmPmView.setFormat12Hour(amPmString);
- mAmPmView.setFormat24Hour(amPmString);
- }
-
- /**
- * @return the index where the AM/PM part starts at the end in {@code formatString} including
- * leading white spaces or {@code -1} if no AM/PM part is found or {@code formatString}
- * doesn't end with AM/PM part
- */
- private static int getAmPmPartEndIndex(String formatString) {
- boolean hasAmPm = false;
- int length = formatString.length();
- for (int i = length - 1; i >= 0; i--) {
- char c = formatString.charAt(i);
- boolean isAmPm = c == 'a';
- boolean isWhitespace = Character.isWhitespace(c);
- if (isAmPm) {
- hasAmPm = true;
- }
- if (isAmPm || isWhitespace) {
- continue;
- }
- if (i == length - 1) {
-
- // First character was not AM/PM and not whitespace, so it's not ending with AM/PM.
- return -1;
- } else {
-
- // If we have AM/PM at all, return last index, or -1 to indicate that it's not
- // ending with AM/PM.
- return hasAmPm ? i + 1 : -1;
- }
- }
-
- // Only AM/PM and whitespaces? The whole string is AM/PM. Else: Only whitespaces in the
- // string.
- return hasAmPm ? 0 : -1;
- }
-
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
index 32c582f..2ec8016 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
@@ -19,6 +19,7 @@
import android.app.PendingIntent
import android.graphics.drawable.Icon
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.notification.stack.BUCKET_UNKNOWN
@@ -46,6 +47,7 @@
contentIntent: PendingIntent? = null,
bucket: Int = BUCKET_UNKNOWN,
callType: CallType = CallType.None,
+ promotedContent: PromotedNotificationContentModel? = null,
) =
ActiveNotificationModel(
key = key,
@@ -69,4 +71,5 @@
isGroupSummary = isGroupSummary,
bucket = bucket,
callType = callType,
+ promotedContent = promotedContent,
)
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 8567ccb..b221d74 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -187,11 +187,6 @@
// Simple flag to enable/disable debug logging.
private static final boolean DEBUG = Build.IS_DEBUGGABLE;
- // String constants for XML schema migration related to changes in keyguard package.
- private static final String OLD_KEYGUARD_HOST_PACKAGE = "android";
- private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
- private static final int KEYGUARD_HOST_ID = 0x4b455947;
-
// Filename for app widgets state persisted on disk.
private static final String STATE_FILENAME = "appwidgets.xml";
@@ -211,6 +206,7 @@
private static final int UNKNOWN_USER_ID = -10;
// Version of XML schema for app widgets. Bump if the stored widgets need to be upgraded.
+ // Version 1 introduced in 2014 - Android 5.0
private static final int CURRENT_VERSION = 1;
// Every widget update request is associated which an increasing sequence number. This is
@@ -4428,19 +4424,11 @@
int version = fromVersion;
- // Update 1: keyguard moved from package "android" to "com.android.keyguard"
+ // Update 1: From version 0 to 1, was used from Android 4 to Android 5. It updated the
+ // location of the keyguard widget database. No modern device will have db version 0.
if (version == 0) {
- HostId oldHostId = new HostId(Process.myUid(),
- KEYGUARD_HOST_ID, OLD_KEYGUARD_HOST_PACKAGE);
-
- Host host = lookupHostLocked(oldHostId);
- if (host != null) {
- final int uid = getUidForPackage(NEW_KEYGUARD_HOST_PACKAGE,
- UserHandle.USER_SYSTEM);
- if (uid >= 0) {
- host.id = new HostId(uid, KEYGUARD_HOST_ID, NEW_KEYGUARD_HOST_PACKAGE);
- }
- }
+ Slog.e(TAG, "Found widget database with version 0, this should not be possible,"
+ + " forcing upgrade to version 1");
version = 1;
}
@@ -4450,24 +4438,8 @@
}
}
- private static File getStateFile(int userId) {
- return new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME);
- }
-
private static AtomicFile getSavedStateFile(int userId) {
- File dir = Environment.getUserSystemDirectory(userId);
- File settingsFile = getStateFile(userId);
- if (!settingsFile.exists() && userId == UserHandle.USER_SYSTEM) {
- if (!dir.exists()) {
- dir.mkdirs();
- }
- // Migrate old data
- File oldFile = new File("/data/system/" + STATE_FILENAME);
- // Method doesn't throw an exception on failure. Ignore any errors
- // in moving the file (like non-existence)
- oldFile.renameTo(settingsFile);
- }
- return new AtomicFile(settingsFile);
+ return new AtomicFile(new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME));
}
void onUserStopped(int userId) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cb89f28..dfddc08 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -668,6 +668,8 @@
*/
private static final boolean ENABLE_PROC_LOCK = true;
+ private static final int DEFAULT_INTENT_CREATOR_UID = -1;
+
/**
* The lock for process management.
*
@@ -19308,22 +19310,36 @@
if (!preventIntentRedirect()) return;
if (intent == null) return;
+
+ String targetPackage = intent.getComponent() != null
+ ? intent.getComponent().getPackageName()
+ : intent.getPackage();
+ final boolean isCreatorSameAsTarget = creatorPackage != null && creatorPackage.equals(
+ targetPackage);
+ final boolean noExtraIntentKeys =
+ intent.getExtraIntentKeys() == null || intent.getExtraIntentKeys().isEmpty();
+ final int creatorUid = noExtraIntentKeys ? DEFAULT_INTENT_CREATOR_UID : Binder.getCallingUid();
+
intent.forEachNestedCreatorToken(extraIntent -> {
- IntentCreatorToken creatorToken = createIntentCreatorToken(extraIntent, creatorPackage);
+ if (isCreatorSameAsTarget) {
+ FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED, creatorUid, true);
+ return;
+ }
+ IntentCreatorToken creatorToken = createIntentCreatorToken(extraIntent, creatorUid,
+ creatorPackage);
if (creatorToken != null) {
extraIntent.setCreatorToken(creatorToken);
// TODO remove Slog.wtf once proven FrameworkStatsLog works. b/375396329
Slog.wtf(TAG, "A creator token is added to an intent. creatorPackage: "
+ creatorPackage + "; intent: " + extraIntent);
- FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED,
- creatorToken.getCreatorUid());
+ FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED, creatorUid, false);
}
});
}
- private IntentCreatorToken createIntentCreatorToken(Intent intent, String creatorPackage) {
+ private IntentCreatorToken createIntentCreatorToken(Intent intent, int creatorUid,
+ String creatorPackage) {
if (IntentCreatorToken.isValid(intent)) return null;
- int creatorUid = getCallingUid();
IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, creatorPackage, intent);
IntentCreatorToken token;
synchronized (sIntentCreatorTokenCache) {
diff --git a/services/core/java/com/android/server/input/KeyboardBacklightController.java b/services/core/java/com/android/server/input/KeyboardBacklightController.java
index c3205af..0defd27 100644
--- a/services/core/java/com/android/server/input/KeyboardBacklightController.java
+++ b/services/core/java/com/android/server/input/KeyboardBacklightController.java
@@ -20,6 +20,7 @@
import android.annotation.BinderThread;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Color;
import android.hardware.input.IKeyboardBacklightListener;
import android.hardware.input.IKeyboardBacklightState;
@@ -81,9 +82,6 @@
private static final String UEVENT_KEYBOARD_BACKLIGHT_TAG = "kbd_backlight";
@VisibleForTesting
- static final long USER_INACTIVITY_THRESHOLD_MILLIS = Duration.ofSeconds(30).toMillis();
-
- @VisibleForTesting
static final int[] DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL =
new int[DEFAULT_NUM_BRIGHTNESS_CHANGE_STEPS + 1];
@@ -112,6 +110,7 @@
private AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener mAmbientListener;
private int mAmbientBacklightValue = 0;
+ private final int mUserInactivityThresholdMs;
static {
// Fixed brightness levels to avoid issues when converting back and forth from the
@@ -139,6 +138,9 @@
mAnimatorFactory = animatorFactory;
mAmbientController = new AmbientKeyboardBacklightController(context, looper);
mUEventManager = uEventManager;
+ Resources res = mContext.getResources();
+ mUserInactivityThresholdMs = res.getInteger(
+ com.android.internal.R.integer.config_keyboardBacklightTimeoutMs);
}
@Override
@@ -300,7 +302,7 @@
}
mHandler.removeMessages(MSG_NOTIFY_USER_INACTIVITY);
mHandler.sendEmptyMessageAtTime(MSG_NOTIFY_USER_INACTIVITY,
- SystemClock.uptimeMillis() + USER_INACTIVITY_THRESHOLD_MILLIS);
+ SystemClock.uptimeMillis() + mUserInactivityThresholdMs);
}
private void handleUserInactivity() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index d5d4070..52ddb80 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -22,8 +22,11 @@
import android.app.backup.BackupRestoreEventLogger;
import android.service.notification.DeviceEffectsApplier;
+import com.android.internal.annotations.Keep;
+
import java.util.Set;
+@Keep
public interface NotificationManagerInternal {
NotificationChannel getNotificationChannel(String pkg, int uid, String channelId);
NotificationChannelGroup getNotificationChannelGroup(String pkg, int uid, String channelId);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4d0c7ec..207764b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -211,6 +211,7 @@
import android.app.RemoteServiceException.BadUserInitiatedJobNotificationException;
import android.app.StatsManager;
import android.app.UriGrantsManager;
+import android.app.ZenBypassingApp;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.backup.BackupManager;
import android.app.backup.BackupRestoreEventLogger;
@@ -238,7 +239,6 @@
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
import android.content.pm.ModuleInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
@@ -3089,7 +3089,7 @@
migrateDefaultNAS();
maybeShowInitialReviewPermissionsNotification();
- if (android.app.Flags.modesApi()) {
+ if (android.app.Flags.modesApi() && !mZenModeHelper.hasDeviceEffectsApplier()) {
// Cannot be done earlier, as some services aren't ready until this point.
mZenModeHelper.setDeviceEffectsApplier(
new DefaultDeviceEffectsApplier(getContext()));
@@ -4043,7 +4043,7 @@
"canNotifyAsPackage for uid " + uid);
}
- return areNotificationsEnabledForPackageInt(pkg, uid);
+ return areNotificationsEnabledForPackageInt(uid);
}
/**
@@ -4864,30 +4864,20 @@
}
@Override
- public List<String> getPackagesBypassingDnd(int userId,
- boolean includeConversationChannels) {
+ public ParceledListSlice<ZenBypassingApp> getPackagesBypassingDnd(int userId)
+ throws RemoteException {
checkCallerIsSystem();
- final ArraySet<String> packageNames = new ArraySet<>();
-
- List<PackageInfo> pkgs = mPackageManagerClient.getInstalledPackagesAsUser(0, userId);
- for (PackageInfo pi : pkgs) {
- String pkg = pi.packageName;
- // If any NotificationChannel for this package is bypassing, the
- // package is considered bypassing.
- for (NotificationChannel channel : getNotificationChannelsBypassingDnd(pkg,
- pi.applicationInfo.uid).getList()) {
- // Skips non-demoted conversation channels.
- if (!includeConversationChannels
- && !TextUtils.isEmpty(channel.getConversationId())
- && !channel.isDemoted()) {
- continue;
- }
- packageNames.add(pkg);
+ UserHandle user = UserHandle.of(userId);
+ ArrayList<ZenBypassingApp> bypassing =
+ mPreferencesHelper.getPackagesBypassingDnd(userId);
+ for (int i = bypassing.size() - 1; i >= 0; i--) {
+ String pkg = bypassing.get(i).getPkg();
+ if (!areNotificationsEnabledForPackage(pkg, getUidForPackageAndUser(pkg, user))) {
+ bypassing.remove(i);
}
}
-
- return new ArrayList<String>(packageNames);
+ return new ParceledListSlice<>(bypassing);
}
@Override
@@ -7763,7 +7753,7 @@
@Override
public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
- return areNotificationsEnabledForPackageInt(pkg, uid);
+ return areNotificationsEnabledForPackageInt(uid);
}
@Override
@@ -8742,7 +8732,7 @@
}
// blocked apps
- boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid);
+ boolean isBlocked = !areNotificationsEnabledForPackageInt(uid);
synchronized (mNotificationLock) {
isBlocked |= isRecordBlockedLocked(r);
}
@@ -8792,7 +8782,7 @@
}
}
- private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) {
+ private boolean areNotificationsEnabledForPackageInt(int uid) {
return mPermissionHelper.hasPermission(uid);
}
@@ -9328,7 +9318,7 @@
* notifying all listeners to a background thread; false otherwise.
*/
private boolean postNotification() {
- boolean appBanned = !areNotificationsEnabledForPackageInt(pkg, uid);
+ boolean appBanned = !areNotificationsEnabledForPackageInt(uid);
boolean isCallNotification = isCallNotification(pkg, uid);
boolean posted = false;
synchronized (NotificationManagerService.this.mNotificationLock) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 9f0b4b0..e6f784c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -57,6 +57,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.app.ZenBypassingApp;
import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -1950,6 +1951,35 @@
}
/**
+ * Gets all apps that can bypass DND, and a boolean indicating whether all (true) or some
+ * (false) of its notification channels can currently bypass.
+ */
+ public @NonNull ArrayList<ZenBypassingApp> getPackagesBypassingDnd(@UserIdInt int userId) {
+ ArrayList<ZenBypassingApp> bypassing = new ArrayList<>();
+ synchronized (mLock) {
+ for (PackagePreferences p : mPackagePreferences.values()) {
+ if (p.userId != userId) {
+ continue;
+ }
+ int totalChannelCount = p.channels.size();
+ int bypassingCount = 0;
+ if (totalChannelCount == 0) {
+ continue;
+ }
+ for (NotificationChannel channel : p.channels.values()) {
+ if (channelIsLiveLocked(p, channel) && channel.canBypassDnd()) {
+ bypassingCount++;
+ }
+ }
+ if (bypassingCount > 0) {
+ bypassing.add(new ZenBypassingApp(p.pkg, totalChannelCount == bypassingCount));
+ }
+ }
+ }
+ return bypassing;
+ }
+
+ /**
* True for pre-O apps that only have the default channel, or pre O apps that have no
* channels yet. This method will create the default channel for pre-O apps that don't have it.
* Should never be true for O+ targeting apps, but that's enforced on boot/when an app
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index cfeacdf..ca4f83f 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -24,6 +24,8 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.ERROR_XML_PARSING;
import static android.service.notification.Condition.SOURCE_UNKNOWN;
import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_FALSE;
@@ -44,8 +46,6 @@
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.Preconditions.checkArgument;
-import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
-import static android.app.backup.NotificationLoggingConstants.ERROR_XML_PARSING;
import static java.util.Objects.requireNonNull;
@@ -303,6 +303,15 @@
}
/**
+ * @return whether a {@link DeviceEffectsApplier} has already been set or not
+ */
+ boolean hasDeviceEffectsApplier() {
+ synchronized (mConfigLock) {
+ return mDeviceEffectsApplier != null;
+ }
+ }
+
+ /**
* Set the {@link DeviceEffectsApplier} used to apply the consolidated effects.
*
* <p>Previously calculated effects (as loaded from the user's {@link ZenModeConfig}) will be
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5518bfa..1052c94 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1022,7 +1022,7 @@
// Close.
file.finishWrite(outs);
} catch (IOException e) {
- Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
+ Slog.w(TAG, "Failed to write to file " + file.getBaseFile(), e);
file.failWrite(outs);
}
}
@@ -1055,7 +1055,7 @@
final String tag = parser.getName();
if (depth == 1) {
if (!TAG_ROOT.equals(tag)) {
- Slog.e(TAG, "Invalid root tag: " + tag);
+ Slog.v(TAG, "Invalid root tag: " + tag);
return;
}
continue;
@@ -1066,7 +1066,7 @@
mRawLastResetTime.set(parseLongAttribute(parser, ATTR_VALUE));
break;
default:
- Slog.e(TAG, "Invalid tag: " + tag);
+ Slog.v(TAG, "Invalid tag: " + tag);
break;
}
}
@@ -1113,7 +1113,7 @@
// Remove all dangling bitmap files.
cleanupDanglingBitmapDirectoriesLocked(userId);
} catch (XmlPullParserException | IOException e) {
- Slog.e(TAG, "Failed to write to file " + file, e);
+ Slog.w(TAG, "Failed to write to file " + file, e);
file.failWrite(os);
}
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 7469c92..09feb18 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -36,6 +36,7 @@
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
+import android.health.connect.HealthPermissions;
import android.media.RingtoneManager;
import android.media.midi.MidiManager;
import android.net.Uri;
@@ -48,6 +49,7 @@
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.permission.PermissionManager;
+import android.permission.flags.Flags;
import android.print.PrintManager;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
@@ -214,8 +216,13 @@
private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>();
static {
- SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
- SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+ if (Flags.replaceBodySensorPermissionEnabled()) {
+ SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEART_RATE);
+ SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND);
+ } else {
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
+ SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND);
+ }
}
private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index eae587b..704c1b8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -210,6 +210,7 @@
import android.app.RemoteInput;
import android.app.RemoteInputHistoryItem;
import android.app.StatsManager;
+import android.app.ZenBypassingApp;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.backup.BackupRestoreEventLogger;
import android.app.job.JobScheduler;
@@ -360,6 +361,9 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -374,9 +378,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@RunWithLooper
@@ -489,7 +490,8 @@
private final NotificationChannel mParentChannel =
new NotificationChannel(PARENT_CHANNEL_ID, "parentName", IMPORTANCE_DEFAULT);
private final NotificationChannel mConversationChannel =
- new NotificationChannel(CONVERSATION_CHANNEL_ID, "conversationName", IMPORTANCE_DEFAULT);
+ new NotificationChannel(
+ CONVERSATION_CHANNEL_ID, "conversationName", IMPORTANCE_DEFAULT);
private static final String PARENT_CHANNEL_ID = "parentChannelId";
private static final String CONVERSATION_CHANNEL_ID = "conversationChannelId";
@@ -4296,8 +4298,13 @@
new NotificationChannel("foo", "foo", IMPORTANCE_HIGH));
Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
- mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "testTvExtenderChannelOverride_onTv", 0,
- generateNotificationRecord(null, tv).getNotification(), mUserId);
+ mBinderService.enqueueNotificationWithTag(
+ mPkg,
+ mPkg,
+ "testTvExtenderChannelOverride_onTv",
+ 0,
+ generateNotificationRecord(null, tv).getNotification(),
+ mUserId);
verify(mPreferencesHelper, times(1)).getConversationNotificationChannel(
anyString(), anyInt(), eq("foo"), eq(null), anyBoolean(), anyBoolean());
}
@@ -4311,8 +4318,13 @@
mTestNotificationChannel);
Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
- mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "testTvExtenderChannelOverride_notOnTv",
- 0, generateNotificationRecord(null, tv).getNotification(), mUserId);
+ mBinderService.enqueueNotificationWithTag(
+ mPkg,
+ mPkg,
+ "testTvExtenderChannelOverride_notOnTv",
+ 0,
+ generateNotificationRecord(null, tv).getNotification(),
+ mUserId);
verify(mPreferencesHelper, times(1)).getConversationNotificationChannel(
anyString(), anyInt(), eq(mTestNotificationChannel.getId()), eq(null),
anyBoolean(), anyBoolean());
@@ -7745,9 +7757,21 @@
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setStyle(new Notification.MessagingStyle("").addMessage(message2));
- NotificationRecord recordB = new NotificationRecord(mContext, new StatusBarNotification(mPkg,
- mPkg, 0, "tag", mUid, 0, nbB.build(), UserHandle.getUserHandleForUid(mUid), null, 0),
- c);
+ NotificationRecord recordB =
+ new NotificationRecord(
+ mContext,
+ new StatusBarNotification(
+ mPkg,
+ mPkg,
+ 0,
+ "tag",
+ mUid,
+ 0,
+ nbB.build(),
+ UserHandle.getUserHandleForUid(mUid),
+ null,
+ 0),
+ c);
// Update means we drop access to first
reset(mUgmInternal);
@@ -13174,6 +13198,37 @@
}
@Test
+ public void getPackagesBypassingDnd_blocked()
+ throws RemoteException, PackageManager.NameNotFoundException {
+
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel2 = new NotificationChannel("id3", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel3 = new NotificationChannel("id4", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel2.setBypassDnd(true);
+ channel3.setBypassDnd(false);
+ // has DND access, so can set bypassDnd attribute
+ mService.mPreferencesHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true,
+ /*has DND access*/ true, UID_N_MR1, false);
+ mService.mPreferencesHelper.createNotificationChannel(PKG_P, UID_P, channel2, true, true,
+ UID_P, false);
+ mService.mPreferencesHelper.createNotificationChannel(PKG_P, UID_P, channel3, true, true,
+ UID_P, false);
+
+ when(mPackageManager.getPackageUid(eq(PKG_P), anyLong(), anyInt())).thenReturn(UID_P);
+ when(mPackageManager.getPackageUid(eq(PKG_N_MR1), anyLong(), anyInt()))
+ .thenReturn(UID_N_MR1);
+ when(mPermissionHelper.hasPermission(UID_N_MR1)).thenReturn(false);
+ when(mPermissionHelper.hasPermission(UID_P)).thenReturn(true);
+
+ assertThat(mBinderService.getPackagesBypassingDnd(UserHandle.getUserId(UID_P)).getList())
+ .containsExactly(new ZenBypassingApp(PKG_P, false));
+ }
+
+ @Test
public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException {
mService.setPreferencesHelper(mPreferencesHelper);
@@ -13187,125 +13242,11 @@
@Test
public void testGetPackagesBypassingDnd_empty() throws RemoteException {
mService.setPreferencesHelper(mPreferencesHelper);
- List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true);
+ List<String> result = mBinderService.getPackagesBypassingDnd(mUserId).getList();
assertThat(result).isEmpty();
}
@Test
- public void testGetPackagesBypassingDnd_excludeConversationChannels() throws RemoteException {
- mService.setPreferencesHelper(mPreferencesHelper);
-
- // Set packages
- PackageInfo pkg0 = new PackageInfo();
- pkg0.packageName = "pkg0";
- pkg0.applicationInfo = new ApplicationInfo();
- pkg0.applicationInfo.uid = mUid;
- PackageInfo pkg1 = new PackageInfo();
- pkg1.packageName = "pkg1";
- pkg1.applicationInfo = new ApplicationInfo();
- pkg1.applicationInfo.uid = mUid;
- PackageInfo pkg2 = new PackageInfo();
- pkg2.packageName = "pkg2";
- pkg2.applicationInfo = new ApplicationInfo();
- pkg2.applicationInfo.uid = mUid;
-
- when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId))
- .thenReturn(List.of(pkg0, pkg1, pkg2));
-
- // Conversation channels
- NotificationChannel nc0 = new NotificationChannel("id0", "id0",
- NotificationManager.IMPORTANCE_HIGH);
- nc0.setConversationId("parentChannel", "conversationId");
-
- // Demoted conversation channel
- NotificationChannel nc1 = new NotificationChannel("id1", "id1",
- NotificationManager.IMPORTANCE_HIGH);
- nc1.setConversationId("parentChannel", "conversationId");
- nc1.setDemoted(true);
-
- // Non-conversation channels
- NotificationChannel nc2 = new NotificationChannel("id2", "id2",
- NotificationManager.IMPORTANCE_HIGH);
- NotificationChannel nc3 = new NotificationChannel("id3", "id3",
- NotificationManager.IMPORTANCE_HIGH);
-
- ParceledListSlice<NotificationChannel> pls0 =
- new ParceledListSlice(ImmutableList.of(nc0));
- ParceledListSlice<NotificationChannel> pls1 =
- new ParceledListSlice(ImmutableList.of(nc1));
- ParceledListSlice<NotificationChannel> pls2 =
- new ParceledListSlice(ImmutableList.of(nc2, nc3));
-
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid))
- .thenReturn(pls0);
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid))
- .thenReturn(pls1);
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid))
- .thenReturn(pls2);
-
- List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, false);
-
- assertThat(result).containsExactly("pkg1", "pkg2");
- }
-
- @Test
- public void testGetPackagesBypassingDnd_includeConversationChannels() throws RemoteException {
- mService.setPreferencesHelper(mPreferencesHelper);
-
- // Set packages
- PackageInfo pkg0 = new PackageInfo();
- pkg0.packageName = "pkg0";
- pkg0.applicationInfo = new ApplicationInfo();
- pkg0.applicationInfo.uid = mUid;
- PackageInfo pkg1 = new PackageInfo();
- pkg1.packageName = "pkg1";
- pkg1.applicationInfo = new ApplicationInfo();
- pkg1.applicationInfo.uid = mUid;
- PackageInfo pkg2 = new PackageInfo();
- pkg2.packageName = "pkg2";
- pkg2.applicationInfo = new ApplicationInfo();
- pkg2.applicationInfo.uid = mUid;
-
- when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId))
- .thenReturn(List.of(pkg0, pkg1, pkg2));
-
- // Conversation channels
- NotificationChannel nc0 = new NotificationChannel("id0", "id0",
- NotificationManager.IMPORTANCE_HIGH);
- nc0.setConversationId("parentChannel", "conversationId");
-
- // Demoted conversation channel
- NotificationChannel nc1 = new NotificationChannel("id1", "id1",
- NotificationManager.IMPORTANCE_HIGH);
- nc1.setConversationId("parentChannel", "conversationId");
- nc1.setDemoted(true);
-
- // Non-conversation channels
- NotificationChannel nc2 = new NotificationChannel("id2", "id2",
- NotificationManager.IMPORTANCE_HIGH);
- NotificationChannel nc3 = new NotificationChannel("id3", "id3",
- NotificationManager.IMPORTANCE_HIGH);
-
- ParceledListSlice<NotificationChannel> pls0 =
- new ParceledListSlice(ImmutableList.of(nc0));
- ParceledListSlice<NotificationChannel> pls1 =
- new ParceledListSlice(ImmutableList.of(nc1));
- ParceledListSlice<NotificationChannel> pls2 =
- new ParceledListSlice(ImmutableList.of(nc2, nc3));
-
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid))
- .thenReturn(pls0);
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid))
- .thenReturn(pls1);
- when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid))
- .thenReturn(pls2);
-
- List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true);
-
- assertThat(result).containsExactly("pkg0", "pkg1", "pkg2");
- }
-
- @Test
public void testMatchesCallFilter_noPermissionShouldThrow() throws Exception {
// set the testable NMS to not system uid/appid
mService.isSystemUid = false;
@@ -15482,8 +15423,13 @@
for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
StatusBarNotification sbn = generateNotificationRecord(mTestNotificationChannel,
i, null, false).getSbn();
- mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "testCannotPostNonUijWhenOverLimit",
- sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(
+ mPkg,
+ mPkg,
+ "testCannotPostNonUijWhenOverLimit",
+ sbn.getId(),
+ sbn.getNotification(),
+ sbn.getUserId());
waitForIdle();
}
@@ -16213,6 +16159,8 @@
initNMS(SystemService.PHASE_SYSTEM_SERVICES_READY);
mInternalService.setDeviceEffectsApplier(mock(DeviceEffectsApplier.class));
+
+ mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper);
// No exception!
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index e1b478c..dda060d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -108,6 +108,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.app.ZenBypassingApp;
import android.content.AttributionSource;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -2620,6 +2621,72 @@
}
@Test
+ public void getPackagesBypassingDnd_noChannelsBypassing() throws Exception {
+ assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(UID_N_MR1))).isEmpty();
+ }
+
+ @Test
+ public void getPackagesBypassingDnd_oneChannelBypassing_deleted() {
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel1.setDeleted(true);
+ // has DND access, so can set bypassDnd attribute
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true,
+ /*has DND access*/ true, UID_N_MR1, false);
+
+ assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(UID_N_MR1))).isEmpty();
+ }
+
+ @Test
+ public void getPackagesBypassingDnd_oneChannelBypassing_groupBlocked() {
+ int uid = UID_N_MR1;
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(true);
+ channel1.setGroup(ncg.getId());
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, uid, ncg, /* fromTargetApp */ true,
+ uid, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel1, true, /*has DND access*/ true,
+ uid, false);
+ ncg.setBlocked(true);
+
+ assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(uid))).isEmpty();
+ }
+
+ @Test
+ public void getPackagesBypassingDnd_multipleApps() {
+ List<ZenBypassingApp> expected = ImmutableList.of(
+ new ZenBypassingApp(PKG_O, true), new ZenBypassingApp(PKG_P, false));
+
+ NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel2 = new NotificationChannel("id2", "name2",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel3 = new NotificationChannel("id3", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ NotificationChannel channel4 = new NotificationChannel("id4", "name3",
+ NotificationManager.IMPORTANCE_MAX);
+ channel1.setBypassDnd(false);
+ channel2.setBypassDnd(true);
+ channel3.setBypassDnd(true);
+ channel4.setBypassDnd(false);
+ // has DND access, so can set bypassDnd attribute
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true,
+ /*has DND access*/ true, UID_N_MR1, false);
+ mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, true,
+ UID_O, false);
+ mHelper.createNotificationChannel(PKG_P, UID_P, channel3, true, true,
+ UID_P, false);
+ mHelper.createNotificationChannel(PKG_P, UID_P, channel4, true, true,
+ UID_P, false);
+
+ assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(UID_O)))
+ .containsExactlyElementsIn(expected);
+ }
+
+ @Test
public void testCreateAndDeleteCanChannelsBypassDnd_localSettings() {
int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
when(mPermissionHelper.hasPermission(uid)).thenReturn(true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 4b94e10..020670d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -49,6 +49,8 @@
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
import static android.app.NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.SYSTEM_UID;
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
@@ -84,8 +86,6 @@
import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
-import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
-import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
import static com.android.server.notification.ZenModeEventLogger.ACTIVE_RULE_TYPE_MANUAL;
import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
@@ -102,6 +102,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -200,6 +201,9 @@
import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParserException;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -219,9 +223,6 @@
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@RunWith(ParameterizedAndroidJunit4.class)
@@ -5348,6 +5349,22 @@
mTestableLooper.processAllMessages();
verify(mDeviceEffectsApplier).apply(eq(effects), eq(ORIGIN_APP));
+ assertTrue(mZenModeHelper.hasDeviceEffectsApplier());
+ }
+
+ @Test
+ public void testHasDeviceEffectsApplier_returnsFalseIfNotSet() {
+ assertFalse(mZenModeHelper.hasDeviceEffectsApplier());
+ }
+
+ @Test
+ @EnableFlags(FLAG_MODES_API)
+ public void testSettingDeviceEffects_throwsExceptionIfAlreadySet() {
+ mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier));
}
@Test
diff --git a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
index 58fb4e1..938e2f8 100644
--- a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
@@ -19,6 +19,7 @@
import android.animation.ValueAnimator
import android.content.Context
import android.content.ContextWrapper
+import android.content.res.Resources
import android.graphics.Color
import android.hardware.input.IKeyboardBacklightListener
import android.hardware.input.IKeyboardBacklightState
@@ -28,11 +29,12 @@
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.view.InputDevice
+import android.util.TypedValue
import androidx.test.annotation.UiThreadTest
import androidx.test.core.app.ApplicationProvider
+import com.android.internal.R
import com.android.server.input.KeyboardBacklightController.DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL
import com.android.server.input.KeyboardBacklightController.MAX_BRIGHTNESS_CHANGE_STEPS
-import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS
import com.android.test.input.MockInputManagerRule
import java.io.FileNotFoundException
import java.io.FileOutputStream
@@ -49,6 +51,7 @@
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.eq
import org.mockito.Mockito.spy
@@ -94,6 +97,7 @@
const val LIGHT_ID = 2
const val SECOND_LIGHT_ID = 3
const val MAX_BRIGHTNESS = 255
+ const val USER_INACTIVITY_THRESHOLD_MILLIS = 30000
}
@get:Rule
@@ -105,6 +109,8 @@
private lateinit var native: NativeInputManagerService
@Mock
private lateinit var uEventManager: UEventManager
+ @Mock
+ private lateinit var resources: Resources
private lateinit var keyboardBacklightController: KeyboardBacklightController
private lateinit var context: Context
private lateinit var dataStore: PersistentDataStore
@@ -117,6 +123,7 @@
@Before
fun setup() {
context = spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ `when`(context.resources).thenReturn(resources)
dataStore = PersistentDataStore(object : PersistentDataStore.Injector() {
override fun openRead(): InputStream? {
throw FileNotFoundException()
@@ -129,6 +136,7 @@
override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
})
testLooper = TestLooper()
+ setupConfig()
keyboardBacklightController = KeyboardBacklightController(context, native, dataStore,
testLooper.looper, FakeAnimatorFactory(), uEventManager)
val inputManager = InputManager(context)
@@ -147,7 +155,31 @@
sysfsNodeChanges++
}
}
-
+ private fun setupConfig() {
+ val brightnessValues = intArrayOf(100, 200, 0)
+ val decreaseThresholds = intArrayOf(-1, 900, 1900)
+ val increaseThresholds = intArrayOf(1000, 2000, -1)
+ `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightBrightnessValues))
+ .thenReturn(brightnessValues)
+ `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightDecreaseLuxThreshold))
+ .thenReturn(decreaseThresholds)
+ `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightIncreaseLuxThreshold))
+ .thenReturn(increaseThresholds)
+ `when`(resources.getInteger(R.integer.config_keyboardBacklightTimeoutMs))
+ .thenReturn(USER_INACTIVITY_THRESHOLD_MILLIS)
+ `when`(
+ resources.getValue(
+ eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant),
+ any(TypedValue::class.java),
+ anyBoolean()
+ )
+ ).then {
+ val args = it.arguments
+ val outValue = args[1] as TypedValue
+ outValue.data = java.lang.Float.floatToRawIntBits(1.0f)
+ Unit
+ }
+ }
@Test
fun testKeyboardBacklightIncrementDecrement() {
KeyboardBacklightFlags(
@@ -365,7 +397,7 @@
lightColorMap[LIGHT_ID]
)
- testLooper.moveTimeForward(USER_INACTIVITY_THRESHOLD_MILLIS + 1000)
+ testLooper.moveTimeForward((USER_INACTIVITY_THRESHOLD_MILLIS + 1000).toLong())
testLooper.dispatchNext()
assertEquals(
"Keyboard backlight level should be turned off after inactivity",