Merge "multiple-desktops: Add aconfig flag" into main
diff --git a/Android.bp b/Android.bp
index 48f0928..54cb268 100644
--- a/Android.bp
+++ b/Android.bp
@@ -107,7 +107,6 @@
":android.hardware.radio.data-V3-java-source",
":android.hardware.radio.network-V3-java-source",
":android.hardware.radio.voice-V3-java-source",
- ":android.hardware.security.keymint-V3-java-source",
":android.hardware.security.secureclock-V1-java-source",
":android.hardware.thermal-V3-java-source",
":android.hardware.tv.tuner-V3-java-source",
@@ -116,7 +115,6 @@
":android.security.legacykeystore-java-source",
":android.security.maintenance-java-source",
":android.security.metrics-java-source",
- ":android.system.keystore2-V4-java-source",
":android.hardware.cas-V1-java-source",
":credstore_aidl",
":dumpstate_aidl",
@@ -149,7 +147,16 @@
":statslog-framework-java-gen", // FrameworkStatsLog.java
":statslog-hwui-java-gen", // HwuiStatsLog.java
":audio_policy_configuration_V7_0",
- ],
+ ] + select(release_flag("RELEASE_ATTEST_MODULES"), {
+ true: [
+ ":android.hardware.security.keymint-V4-java-source",
+ ":android.system.keystore2-V5-java-source",
+ ],
+ default: [
+ ":android.hardware.security.keymint-V3-java-source",
+ ":android.system.keystore2-V4-java-source",
+ ],
+ }),
}
java_library {
@@ -398,6 +405,7 @@
"bouncycastle-repackaged-unbundled",
"com.android.sysprop.foldlockbehavior",
"com.android.sysprop.view",
+ "configinfra_framework_flags_java_lib",
"framework-internal-utils",
"dynamic_instrumentation_manager_aidl-java",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
diff --git a/FF_LEADS_OWNERS b/FF_LEADS_OWNERS
new file mode 100644
index 0000000..a650c6b
--- /dev/null
+++ b/FF_LEADS_OWNERS
@@ -0,0 +1,10 @@
+bills@google.com
+carmenjackson@google.com
+nalini@google.com
+nosh@google.com
+olilan@google.com
+philipcuadra@google.com
+rajekumar@google.com
+shayba@google.com
+timmurray@google.com
+zezeozue@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index 5b9f1ef..f660158 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -13569,6 +13569,7 @@
field public static final String PROPERTY_MEDIA_CAPABILITIES = "android.media.PROPERTY_MEDIA_CAPABILITIES";
field public static final String PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES = "android.net.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES";
field public static final String PROPERTY_SPECIAL_USE_FGS_SUBTYPE = "android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE";
+ field @FlaggedApi("com.android.server.backup.enable_restricted_mode_changes") public static final String PROPERTY_USE_RESTRICTED_BACKUP_MODE = "android.app.backup.PROPERTY_USE_RESTRICTED_BACKUP_MODE";
field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
field public static final int SIGNATURE_MATCH = 0; // 0x0
field public static final int SIGNATURE_NEITHER_SIGNED = 1; // 0x1
@@ -13805,6 +13806,7 @@
public final class SharedLibraryInfo implements android.os.Parcelable {
method public int describeContents();
+ method @FlaggedApi("android.content.pm.sdk_dependency_installer") @NonNull public java.util.List<java.lang.String> getCertDigests();
method @NonNull public android.content.pm.VersionedPackage getDeclaringPackage();
method @NonNull public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
method @IntRange(from=0xffffffff) public long getLongVersion();
@@ -17472,6 +17474,7 @@
method public void setFloatUniform(@NonNull String, @NonNull float[]);
method public void setInputColorFilter(@NonNull String, @NonNull android.graphics.ColorFilter);
method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
+ method public void setInputXfermode(@NonNull String, @NonNull android.graphics.RuntimeXfermode);
method public void setIntUniform(@NonNull String, int);
method public void setIntUniform(@NonNull String, int, int);
method public void setIntUniform(@NonNull String, int, int, int);
@@ -17490,7 +17493,9 @@
method public void setFloatUniform(@NonNull String, float, float, float, float);
method public void setFloatUniform(@NonNull String, @NonNull float[]);
method public void setInputBuffer(@NonNull String, @NonNull android.graphics.BitmapShader);
+ method @FlaggedApi("com.android.graphics.hwui.flags.runtime_color_filters_blenders") public void setInputColorFilter(@NonNull String, @NonNull android.graphics.ColorFilter);
method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
+ method @FlaggedApi("com.android.graphics.hwui.flags.runtime_color_filters_blenders") public void setInputXfermode(@NonNull String, @NonNull android.graphics.RuntimeXfermode);
method public void setIntUniform(@NonNull String, int);
method public void setIntUniform(@NonNull String, int, int);
method public void setIntUniform(@NonNull String, int, int, int);
@@ -17510,6 +17515,7 @@
method public void setFloatUniform(@NonNull String, @NonNull float[]);
method public void setInputColorFilter(@NonNull String, @NonNull android.graphics.ColorFilter);
method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
+ method public void setInputXfermode(@NonNull String, @NonNull android.graphics.RuntimeXfermode);
method public void setIntUniform(@NonNull String, int);
method public void setIntUniform(@NonNull String, int, int);
method public void setIntUniform(@NonNull String, int, int, int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0ea00f5..9197a01 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -57,6 +57,7 @@
field @Deprecated public static final String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
field public static final String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE";
+ field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final String BIND_DEPENDENCY_INSTALLER = "android.permission.BIND_DEPENDENCY_INSTALLER";
field public static final String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
field public static final String BIND_DISPLAY_HASHING_SERVICE = "android.permission.BIND_DISPLAY_HASHING_SERVICE";
field @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public static final String BIND_DOMAIN_SELECTION_SERVICE = "android.permission.BIND_DOMAIN_SELECTION_SERVICE";
@@ -165,6 +166,7 @@
field public static final String HDMI_CEC = "android.permission.HDMI_CEC";
field @Deprecated public static final String HIDE_NON_SYSTEM_OVERLAY_WINDOWS = "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS";
field public static final String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
+ field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final String INSTALL_DEPENDENCY_SHARED_LIBRARIES = "android.permission.INSTALL_DEPENDENCY_SHARED_LIBRARIES";
field public static final String INSTALL_DPC_PACKAGES = "android.permission.INSTALL_DPC_PACKAGES";
field public static final String INSTALL_DYNAMIC_SYSTEM = "android.permission.INSTALL_DYNAMIC_SYSTEM";
field public static final String INSTALL_EXISTING_PACKAGES = "com.android.permission.INSTALL_EXISTING_PACKAGES";
@@ -531,6 +533,7 @@
field public static final int config_systemCallStreaming = 17039431; // 0x1040047
field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039
field public static final int config_systemContacts = 17039403; // 0x104002b
+ field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final int config_systemDependencyInstaller;
field public static final int config_systemFinancedDeviceController = 17039430; // 0x1040046
field public static final int config_systemGallery = 17039399; // 0x1040027
field public static final int config_systemNotificationIntelligence = 17039413; // 0x1040035
@@ -1937,6 +1940,7 @@
method public android.os.IBinder getBinder();
method public long getCurrentRestoreSet();
method public int getNextFullRestoreDataChunk(android.os.ParcelFileDescriptor);
+ method @FlaggedApi("com.android.server.backup.enable_restricted_mode_changes") @NonNull public java.util.List<java.lang.String> getPackagesThatShouldNotUseRestrictedMode(@NonNull java.util.List<java.lang.String>, int);
method public int getRestoreData(android.os.ParcelFileDescriptor);
method public int getTransportFlags();
method public int initializeDevice();
@@ -4590,6 +4594,24 @@
}
+package android.content.pm.dependencyinstaller {
+
+ @FlaggedApi("android.content.pm.sdk_dependency_installer") public final class DependencyInstallerCallback implements android.os.Parcelable {
+ method public int describeContents();
+ method public void onAllDependenciesResolved(@NonNull int[]);
+ method public void onFailureToResolveAllDependencies();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.dependencyinstaller.DependencyInstallerCallback> CREATOR;
+ }
+
+ @FlaggedApi("android.content.pm.sdk_dependency_installer") public abstract class DependencyInstallerService extends android.app.Service {
+ ctor public DependencyInstallerService();
+ method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
+ method public abstract void onDependenciesRequired(@NonNull java.util.List<android.content.pm.SharedLibraryInfo>, @NonNull android.content.pm.dependencyinstaller.DependencyInstallerCallback);
+ }
+
+}
+
package android.content.pm.dex {
public class ArtManager {
@@ -5401,6 +5423,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
method public android.util.Pair<float[],float[]> getMinimumBrightnessCurve();
method public android.graphics.Point getStableDisplaySize();
+ method @FlaggedApi("com.android.server.display.feature.flags.is_always_on_available_api") public boolean isAlwaysOnDisplayCurrentlyAvailable();
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 136c636..8fd2cd5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2318,7 +2318,7 @@
public class SharedConnectivityManager {
method @Nullable public static android.net.wifi.sharedconnectivity.app.SharedConnectivityManager create(@NonNull android.content.Context, @NonNull String, @NonNull String);
- method @FlaggedApi("com.android.wifi.flags.shared_connectivity_broadcast_receiver_test_api") @NonNull public android.content.BroadcastReceiver getBroadcastReceiver();
+ method @NonNull public android.content.BroadcastReceiver getBroadcastReceiver();
method @Nullable public android.content.ServiceConnection getServiceConnection();
method public void setService(@Nullable android.os.IInterface);
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 34a3ad1..a8412fa 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -358,7 +358,7 @@
@UnsupportedAppUsage
void resumeAppSwitches();
boolean bindBackupAgent(in String packageName, int backupRestoreMode, int targetUserId,
- int backupDestination);
+ int backupDestination, boolean useRestrictedMode);
void backupAgentCreated(in String packageName, in IBinder agent, int userId);
void unbindBackupAgent(in ApplicationInfo appInfo);
int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index a063917..53a7dad 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -17,6 +17,7 @@
package android.app;
import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
+import static android.provider.flags.Flags.stageFlagsForBuild;
import static android.server.Flags.removeGameManagerServiceFromWear;
import android.accounts.AccountManager;
@@ -216,6 +217,7 @@
import android.os.UserManager;
import android.os.Vibrator;
import android.os.VibratorManager;
+import android.os.flagging.ConfigInfrastructureFrameworkInitializer;
import android.os.health.SystemHealthManager;
import android.os.image.DynamicSystemManager;
import android.os.image.IDynamicSystemService;
@@ -1837,6 +1839,10 @@
VirtualizationFrameworkInitializer.registerServiceWrappers();
ConnectivityFrameworkInitializerBaklava.registerServiceWrappers();
+ if (stageFlagsForBuild()) {
+ ConfigInfrastructureFrameworkInitializer.registerServiceWrappers();
+ }
+
if (com.android.server.telecom.flags.Flags.telecomMainlineBlockedNumbersManager()) {
ProviderFrameworkInitializer.registerServiceWrappers();
}
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index dcac59c..5004c02 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -16,6 +16,8 @@
package android.app.backup;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Intent;
@@ -27,6 +29,7 @@
import com.android.internal.backup.IBackupTransport;
import com.android.internal.backup.ITransportStatusCallback;
import com.android.internal.infra.AndroidFuture;
+import com.android.server.backup.Flags;
import java.util.Arrays;
import java.util.List;
@@ -671,6 +674,22 @@
}
/**
+ * Ask the transport whether packages that are about to be backed up or restored should not be
+ * put into a restricted mode by the framework and started normally instead.
+ *
+ * @param operationType 0 for backup, 1 for restore.
+ * @return a subset of the {@code packageNames} passed in, indicating
+ * which packages should NOT be put into restricted mode for the given operation type.
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public List<String> getPackagesThatShouldNotUseRestrictedMode(
+ @NonNull List<String> packageNames,
+ @BackupAnnotations.OperationType int operationType) {
+ return List.of();
+ }
+
+ /**
* Bridge between the actual IBackupTransport implementation and the stable API. If the
* binder interface needs to change, we use this layer to translate so that we can
* (if appropriate) decouple those framework-side changes from the BackupTransport
@@ -977,5 +996,19 @@
resultFuture.cancel(/* mayInterruptIfRunning */ true);
}
}
+
+ @Override
+ @FlaggedApi(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void getPackagesThatShouldNotUseRestrictedMode(List<String> packageNames,
+ int operationType, AndroidFuture<List<String>> resultFuture) {
+ try {
+ List<String> result =
+ BackupTransport.this.getPackagesThatShouldNotUseRestrictedMode(packageNames,
+ operationType);
+ resultFuture.complete(result);
+ } catch (RuntimeException e) {
+ resultFuture.cancel(/* mayInterruptIfRunning */ true);
+ }
+ }
}
}
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index c47fe23..de01280 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -148,6 +148,14 @@
flag {
namespace: "virtual_devices"
+ name: "notifications_for_device_streaming"
+ description: "Add notifications permissions to device streaming role"
+ bug: "375240276"
+ is_exported: true
+}
+
+flag {
+ namespace: "virtual_devices"
name: "default_device_camera_access_policy"
description: "API for default device camera access policy"
bug: "371173368"
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 13b13b9..37295ac 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -193,6 +193,42 @@
"android.net.PROPERTY_SELF_CERTIFIED_NETWORK_CAPABILITIES";
/**
+ * <application> level {@link android.content.pm.PackageManager.Property} tag
+ * specifying whether the app should be put into the "restricted" backup mode when it's started
+ * for backup and restore operations.
+ *
+ * <p> See <a
+ * href="https://developer.android.com/identity/data/autobackup#ImplementingBackupAgent"> for
+ * information about restricted mode</a>.
+ *
+ * <p> Starting with Android 16 apps may not be started in restricted mode based on this
+ * property.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * <application>
+ * <property
+ * android:name="android.app.backup.PROPERTY_USE_RESTRICTED_BACKUP_MODE"
+ * android:value="true|false"/>
+ * </application>
+ * </pre>
+ *
+ * <p>If this property is set, the operating system will respect it for now (see Note below).
+ * If it's not set, the behavior depends on the SDK level that the app is targeting. For apps
+ * targeting SDK level {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} or lower, the
+ * property defaults to {@code true}. For apps targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#BAKLAVA} or higher, the operating system will make a
+ * decision dynamically.
+ *
+ * <p>Note: It's not recommended to set this property to {@code true} unless absolutely
+ * necessary. In a future Android version, this property may be deprecated in favor of removing
+ * restricted mode completely.
+ */
+ @FlaggedApi(com.android.server.backup.Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public static final String PROPERTY_USE_RESTRICTED_BACKUP_MODE =
+ "android.app.backup.PROPERTY_USE_RESTRICTED_BACKUP_MODE";
+
+ /**
* Application level property that an app can specify to opt-out from having private data
* directories both on the internal and external storages.
*
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index f7191e6..5dfec98 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -105,6 +105,8 @@
private final List<VersionedPackage> mOptionalDependentPackages;
private List<SharedLibraryInfo> mDependencies;
+ private final List<String> mCertDigests;
+
/**
* Creates a new instance.
*
@@ -134,6 +136,7 @@
mDependencies = dependencies;
mIsNative = isNative;
mOptionalDependentPackages = null;
+ mCertDigests = null;
}
/**
@@ -165,6 +168,7 @@
mDeclaringPackage = declaringPackage;
mDependencies = dependencies;
mIsNative = isNative;
+ mCertDigests = null;
var allDependents = allDependentPackages.first;
var usesLibOptional = allDependentPackages.second;
@@ -206,6 +210,7 @@
mIsNative = parcel.readBoolean();
mOptionalDependentPackages = parcel.readParcelableList(new ArrayList<>(),
VersionedPackage.class.getClassLoader(), VersionedPackage.class);
+ mCertDigests = parcel.createStringArrayList();
}
/**
@@ -214,6 +219,7 @@
* @param versionMajor
*/
public SharedLibraryInfo(String name, long versionMajor, int type) {
+ //TODO: change to this(name, versionMajor, type, /* certDigest= */null); after flag removal
mPath = null;
mPackageName = null;
mName = name;
@@ -224,6 +230,29 @@
mDependencies = null;
mIsNative = false;
mOptionalDependentPackages = null;
+ mCertDigests = null;
+ }
+
+ /**
+ * @hide
+ * @param name The lib name.
+ * @param versionMajor The lib major version.
+ * @param type The type of shared library.
+ * @param certDigests The list of certificate digests for this shared library.
+ */
+ @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ public SharedLibraryInfo(String name, long versionMajor, int type, List<String> certDigests) {
+ mPath = null;
+ mPackageName = null;
+ mName = name;
+ mVersion = versionMajor;
+ mType = type;
+ mDeclaringPackage = null;
+ mDependentPackages = null;
+ mDependencies = null;
+ mIsNative = false;
+ mOptionalDependentPackages = null;
+ mCertDigests = certDigests;
}
/**
@@ -433,6 +462,19 @@
return mOptionalDependentPackages;
}
+ /**
+ * Gets the list of certificate digests for the shared library.
+ *
+ * @return The list of certificate digests
+ */
+ @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ public @NonNull List<String> getCertDigests() {
+ if (mCertDigests == null) {
+ return Collections.emptyList();
+ }
+ return mCertDigests;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -463,6 +505,7 @@
parcel.writeTypedList(mDependencies);
parcel.writeBoolean(mIsNative);
parcel.writeParcelableList(mOptionalDependentPackages, flags);
+ parcel.writeStringList(mCertDigests);
}
private static String typeToString(int type) {
diff --git a/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.aidl b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.aidl
new file mode 100644
index 0000000..06fcabc
--- /dev/null
+++ b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.content.pm.dependencyinstaller;
+
+parcelable DependencyInstallerCallback;
\ No newline at end of file
diff --git a/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java
new file mode 100644
index 0000000..ba089f7
--- /dev/null
+++ b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java
@@ -0,0 +1,100 @@
+/**
+ * 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.content.pm.dependencyinstaller;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.pm.Flags;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+/**
+ * Callbacks for {@link DependencyInstallerService}. The implementation of
+ * DependencyInstallerService uses this interface to indicate completion of the session creation
+ * request given by the system server.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+public final class DependencyInstallerCallback implements Parcelable {
+ private final IBinder mBinder;
+ private final IDependencyInstallerCallback mCallback;
+
+ /** @hide */
+ public DependencyInstallerCallback(IBinder binder) {
+ mBinder = binder;
+ mCallback = IDependencyInstallerCallback.Stub.asInterface(binder);
+ }
+
+ private DependencyInstallerCallback(Parcel in) {
+ mBinder = in.readStrongBinder();
+ mCallback = IDependencyInstallerCallback.Stub.asInterface(mBinder);
+ }
+
+ /**
+ * Callback to indicate that all the requested dependencies have been resolved and their
+ * sessions created. See {@link DependencyInstallerService#onDependenciesRequired}.
+ *
+ * @param sessionIds the install session IDs for all requested dependencies
+ */
+ public void onAllDependenciesResolved(@NonNull int[] sessionIds) {
+ try {
+ mCallback.onAllDependenciesResolved(sessionIds);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Callback to indicate that at least one of the required dependencies could not be resolved
+ * and any associated sessions have been abandoned.
+ */
+ public void onFailureToResolveAllDependencies() {
+ try {
+ mCallback.onFailureToResolveAllDependencies();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeStrongBinder(mBinder);
+ }
+
+ public static final @NonNull Creator<DependencyInstallerCallback> CREATOR =
+ new Creator<>() {
+ @Override
+ public DependencyInstallerCallback createFromParcel(Parcel in) {
+ return new DependencyInstallerCallback(in);
+ }
+
+ @Override
+ public DependencyInstallerCallback[] newArray(int size) {
+ return new DependencyInstallerCallback[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/dependencyinstaller/DependencyInstallerService.java b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerService.java
new file mode 100644
index 0000000..1186415
--- /dev/null
+++ b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerService.java
@@ -0,0 +1,83 @@
+/**
+ * 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.content.pm.dependencyinstaller;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.Flags;
+import android.content.pm.SharedLibraryInfo;
+import android.os.IBinder;
+
+import java.util.List;
+
+/**
+ * Service that needs to be implemented by the holder of the DependencyInstaller role. This service
+ * will be invoked by the system during application installations if it depends on
+ * {@link android.content.pm.SharedLibraryInfo#TYPE_STATIC} or
+ * {@link android.content.pm.SharedLibraryInfo#TYPE_SDK_PACKAGE} and those dependencies aren't
+ * already installed.
+ * <p>
+ * Below is an example manifest registration for a {@code DependencyInstallerService}.
+ * <pre>
+ * {@code
+ * <service android:name=".ExampleDependencyInstallerService"
+ * android:permission="android.permission.BIND_DEPENDENCY_INSTALLER" >
+ * ...
+ * <intent-filter>
+ * <action android:name="android.content.pm.action.INSTALL_DEPENDENCY" />
+ * </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+public abstract class DependencyInstallerService extends Service {
+
+ private IDependencyInstallerService mBinder;
+
+ @Override
+ public final @NonNull IBinder onBind(@Nullable Intent intent) {
+ if (mBinder == null) {
+ mBinder = new IDependencyInstallerService.Stub() {
+ @Override
+ public void onDependenciesRequired(List<SharedLibraryInfo> neededLibraries,
+ DependencyInstallerCallback callback) {
+ DependencyInstallerService.this.onDependenciesRequired(neededLibraries,
+ callback);
+ }
+ };
+ }
+ return mBinder.asBinder();
+ }
+
+ /**
+ * Notify the holder of the DependencyInstaller role of the missing dependencies required for
+ * the completion of an active install session.
+ *
+ * @param neededLibraries the list of shared library dependencies needed to be obtained and
+ * installed.
+ */
+ public abstract void onDependenciesRequired(@NonNull List<SharedLibraryInfo> neededLibraries,
+ @NonNull DependencyInstallerCallback callback);
+}
diff --git a/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl
new file mode 100644
index 0000000..92d1d9e
--- /dev/null
+++ b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl
@@ -0,0 +1,41 @@
+/**
+ * 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.content.pm.dependencyinstaller;
+
+import java.util.List;
+
+/**
+* Callbacks for Dependency Installer. The app side invokes on this interface to indicate
+* completion of the async dependency install request given by the system server.
+*
+* {@hide}
+*/
+oneway interface IDependencyInstallerCallback {
+ /**
+ * Callback to indicate that all the requested dependencies have been resolved and have been
+ * committed for installation. See {@link DependencyInstallerService#onDependenciesRequired}.
+ *
+ * @param sessionIds the install session IDs for all requested dependencies
+ */
+ void onAllDependenciesResolved(in int[] sessionIds);
+
+ /**
+ * Callback to indicate that at least one of the required dependencies could not be resolved
+ * and any associated sessions have been abandoned.
+ */
+ void onFailureToResolveAllDependencies();
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerService.aidl b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerService.aidl
new file mode 100644
index 0000000..94f5bf4
--- /dev/null
+++ b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerService.aidl
@@ -0,0 +1,37 @@
+/**
+ * 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.content.pm.dependencyinstaller;
+
+import android.content.pm.dependencyinstaller.DependencyInstallerCallback;
+import android.content.pm.SharedLibraryInfo;
+import java.util.List;
+
+/**
+* Interface used to communicate with the application code that holds the Dependency Installer role.
+* {@hide}
+*/
+oneway interface IDependencyInstallerService {
+ /**
+ * Notify dependency installer of the required dependencies to complete the current install
+ * session.
+ *
+ * @param neededLibraries the list of shared library dependencies needed to be obtained and
+ * installed.
+ */
+ void onDependenciesRequired(in List<SharedLibraryInfo> neededLibraries,
+ in DependencyInstallerCallback callback);
+ }
\ No newline at end of file
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index e6a1640..25327a9 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -61,6 +61,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.display.feature.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -102,6 +103,7 @@
private final WeakDisplayCache mDisplayCache = new WeakDisplayCache();
private int mDisplayIdToMirror = INVALID_DISPLAY;
+ private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
/**
* Broadcast receiver that indicates when the Wifi display status changes.
@@ -1613,6 +1615,17 @@
}
/**
+ * Returns whether this device supports Always On Display.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_IS_ALWAYS_ON_AVAILABLE_API)
+ public boolean isAlwaysOnDisplayCurrentlyAvailable() {
+ return getAmbientDisplayConfiguration().alwaysOnAvailableForUser(mContext.getUserId());
+ }
+
+ /**
* Returns whether device supports seamless refresh rate switching.
*
* Match content frame rate setting has three options: seamless, non-seamless and never.
@@ -1674,6 +1687,15 @@
}
}
+ private AmbientDisplayConfiguration getAmbientDisplayConfiguration() {
+ synchronized (this) {
+ if (mAmbientDisplayConfiguration == null) {
+ mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
+ }
+ }
+ return mAmbientDisplayConfiguration;
+ }
+
/**
* Creates a VirtualDisplay that will mirror the content of displayIdToMirror
* @param name The name for the virtual display
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 9ad2e7f..036ccd8 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -118,18 +118,33 @@
mUseConcurrent = UserHandle.isCore(Process.myUid());
// Even then, we don't use it if instrumentation is loaded as it breaks some
// platform tests.
- final ActivityThread activityThread = ActivityThread.currentActivityThread();
- if (activityThread != null) {
- final Instrumentation instrumentation = activityThread.getInstrumentation();
- mUseConcurrent &= instrumentation == null || !instrumentation.isInstrumenting();
- }
+ final Instrumentation instrumentation = getInstrumentation();
+ mUseConcurrent &= instrumentation == null || !instrumentation.isInstrumenting();
// We can lift this restriction in the future after we've made it possible for test authors
// to test Looper and MessageQueue without resorting to reflection.
+ // Holdback study.
+ if (mUseConcurrent && Flags.messageQueueForceLegacy()) {
+ mUseConcurrent = false;
+ }
+
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
+ @android.ravenwood.annotation.RavenwoodReplace(blockedBy = ActivityThread.class)
+ private static Instrumentation getInstrumentation() {
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+ if (activityThread != null) {
+ return activityThread.getInstrumentation();
+ }
+ return null;
+ }
+
+ private static Instrumentation getInstrumentation$ravenwood() {
+ return null; // Instrumentation not supported on Ravenwood yet.
+ }
+
@Override
protected void finalize() throws Throwable {
try {
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 24e1d66..e63b664 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -128,3 +128,6 @@
# Dropbox
per-file DropBoxManager* = mwachens@google.com
+
+# Flags
+per-file flags.aconfig = file:/FF_LEADS_OWNERS
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 118167d..0144506 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -4,6 +4,15 @@
# keep-sorted start block=yes newline_separated=yes
flag {
+ # Holdback study for concurrent MessageQueue.
+ # Do not promote beyond trunkfood.
+ namespace: "system_performance"
+ name: "message_queue_force_legacy"
+ description: "Whether to holdback concurrent MessageQueue (force legacy)."
+ bug: "336880969"
+}
+
+flag {
name: "adpf_gpu_report_actual_work_duration"
is_exported: true
namespace: "game"
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 5a71282..8cb96ae 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -930,8 +930,8 @@
// of the next vsync event.
int totalFrameDelays = mBufferStuffingData.numberFrameDelays
+ mBufferStuffingData.numberWaitsForNextVsync + 1;
- long vsyncsSinceLastCallback =
- (frameTimeNanos - mLastNoOffsetFrameTimeNanos) / mLastFrameIntervalNanos;
+ long vsyncsSinceLastCallback = mLastFrameIntervalNanos > 0
+ ? (frameTimeNanos - mLastNoOffsetFrameTimeNanos) / mLastFrameIntervalNanos : 0;
// Detected idle state due to a longer inactive period since the last vsync callback
// than the total expected number of vsync frame delays. End buffer stuffing recovery.
diff --git a/core/java/android/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig
index b180e58..ebda4d4 100644
--- a/core/java/android/view/flags/scroll_feedback_flags.aconfig
+++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig
@@ -28,5 +28,5 @@
namespace: "wear_frameworks"
name: "dynamic_view_rotary_haptics_configuration"
description: "Whether ScrollFeedbackProvider dynamically disables View-based rotary haptics."
- bug: "377998870 "
+ bug: "377998870"
}
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 21c7baa..469ab48 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -410,4 +410,15 @@
* however backups initiated by the framework will call this method to retrieve one.
*/
void getBackupManagerMonitor(in AndroidFuture<IBackupManagerMonitor> resultFuture);
+
+ /**
+ * Ask the transport whether packages that are about to be backed up or restored should not be
+ * put into a restricted mode by the framework and started normally instead. The
+ * {@code resultFuture} should be completed with a subset of the packages passed in, indicating
+ * which packages should NOT be put into restricted mode for the given operation type.
+ *
+ * @param operationType 0 for backup, 1 for restore.
+ */
+ void getPackagesThatShouldNotUseRestrictedMode(in List<String> packageNames, int operationType,
+ in AndroidFuture<List<String>> resultFuture);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 95d07df..0042459 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7921,7 +7921,31 @@
<!-- @SystemApi Allows an application to access shared libraries.
@hide -->
<permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer"
+ android:featureFlag="!android.content.pm.sdk_dependency_installer" />
+
+ <!-- @SystemApi Allows an application to access shared libraries.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
+ android:protectionLevel="signature|installer|role"
+ android:featureFlag="android.content.pm.sdk_dependency_installer" />
+
+ <!-- @SystemApi Permission held by the system to allow binding to the dependency installer role
+ holder.
+ @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ @hide -->
+ <permission android:name="android.permission.BIND_DEPENDENCY_INSTALLER"
+ android:protectionLevel="signature"
+ android:featureFlag="android.content.pm.sdk_dependency_installer" />
+
+ <!-- @SystemApi Allows an application to install shared libraries of types
+ {@link android.content.pm.SharedLibraryInfo#TYPE_STATIC} or
+ {@link android.content.pm.SharedLibraryInfo#TYPE_SDK_PACKAGE}.
+ @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ @hide -->
+ <permission android:name="android.permission.INSTALL_DEPENDENCY_SHARED_LIBRARIES"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.content.pm.sdk_dependency_installer" />
<!-- Allows an app to log compat change usage.
@hide <p>Not for use by third-party applications.</p> -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7799ff9..5088b5a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4205,9 +4205,17 @@
must match the value of config_cameraLaunchGestureSensorType in OEM's HAL -->
<string translatable="false" name="config_cameraLaunchGestureSensorStringType"></string>
+ <!-- Allow the gesture to double tap the power button to trigger a target action. -->
+ <bool name="config_doubleTapPowerGestureEnabled">true</bool>
<!-- Allow the gesture to double tap the power button twice to start the camera while the device
is non-interactive. -->
<bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool>
+ <!-- Allow the gesture to double tap the power button twice to launch the wallet. -->
+ <bool name="config_walletDoubleTapPowerGestureEnabled">true</bool>
+ <!-- Default target action for double tap of the power button gesture.
+ 0: Launch camera
+ 1: Launch wallet -->
+ <integer name="config_defaultDoubleTapPowerGestureAction">0</integer>
<!-- Allow the gesture to quick tap the power button multiple times to start the emergency sos
experience while the device is non-interactive. -->
@@ -7217,4 +7225,7 @@
<!-- Whether to enable fp unlock when screen turns off on udfps devices -->
<bool name="config_screen_off_udfps_enabled">false</bool>
+
+ <!-- The name of the system package that will hold the dependency installer role. -->
+ <string name="config_systemDependencyInstaller" translatable="false" />
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index a0bf89d..ce46c64 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -142,6 +142,9 @@
</staging-public-group>
<staging-public-group type="string" first-id="0x01b40000">
+ <!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ @hide @SystemApi -->
+ <public name="config_systemDependencyInstaller" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01b30000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7fe0912..0e12075 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3140,9 +3140,12 @@
<!-- Gesture -->
<java-symbol type="integer" name="config_cameraLaunchGestureSensorType" />
<java-symbol type="string" name="config_cameraLaunchGestureSensorStringType" />
- <java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
<java-symbol type="integer" name="config_cameraLiftTriggerSensorType" />
<java-symbol type="string" name="config_cameraLiftTriggerSensorStringType" />
+ <java-symbol type="bool" name="config_doubleTapPowerGestureEnabled" />
+ <java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
+ <java-symbol type="bool" name="config_walletDoubleTapPowerGestureEnabled" />
+ <java-symbol type="integer" name="config_defaultDoubleTapPowerGestureAction" />
<java-symbol type="bool" name="config_emergencyGestureEnabled" />
<java-symbol type="bool" name="config_defaultEmergencyGestureEnabled" />
<java-symbol type="bool" name="config_defaultEmergencyGestureSoundEnabled" />
diff --git a/core/tests/coretests/src/android/content/pm/SharedLibraryInfoTest.java b/core/tests/coretests/src/android/content/pm/SharedLibraryInfoTest.java
new file mode 100644
index 0000000..df5cd4e
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/SharedLibraryInfoTest.java
@@ -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 android.content.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@Presubmit
+@RunWith(JUnit4.class)
+public final class SharedLibraryInfoTest {
+
+ @Rule
+ public final CheckFlagsRule checkFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private static final String LIBRARY_NAME = "name";
+ private static final long VERSION_MAJOR = 1L;
+ private static final List<String> CERT_DIGESTS = ImmutableList.of("digest1", "digest2");
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
+ public void sharedLibraryInfo_serializedAndDeserialized_retainsCertDigestInfo() {
+ SharedLibraryInfo toParcel = new SharedLibraryInfo(LIBRARY_NAME, VERSION_MAJOR,
+ SharedLibraryInfo.TYPE_SDK_PACKAGE, CERT_DIGESTS);
+
+ SharedLibraryInfo fromParcel = parcelAndUnparcel(toParcel);
+
+ assertThat(fromParcel.getCertDigests().size()).isEqualTo(toParcel.getCertDigests().size());
+ assertThat(fromParcel.getCertDigests().get(0)).isEqualTo(toParcel.getCertDigests().get(0));
+ assertThat(fromParcel.getCertDigests().get(1)).isEqualTo(toParcel.getCertDigests().get(1));
+ }
+
+ private SharedLibraryInfo parcelAndUnparcel(SharedLibraryInfo sharedLibraryInfo) {
+ Parcel parcel = Parcel.obtain();
+ parcel.setDataPosition(0);
+ sharedLibraryInfo.writeToParcel(parcel, /* flags= */0);
+
+ parcel.setDataPosition(0);
+ return SharedLibraryInfo.CREATOR.createFromParcel(parcel);
+ }
+}
diff --git a/graphics/java/android/graphics/RuntimeColorFilter.java b/graphics/java/android/graphics/RuntimeColorFilter.java
index 52724ce..d112f71 100644
--- a/graphics/java/android/graphics/RuntimeColorFilter.java
+++ b/graphics/java/android/graphics/RuntimeColorFilter.java
@@ -283,6 +283,23 @@
nativeUpdateChild(getNativeInstance(), filterName, colorFilter.getNativeInstance());
}
+ /**
+ * Assigns the uniform xfermode to the provided xfermode parameter. If the shader program does
+ * not have a uniform xfermode with that name then an IllegalArgumentException is thrown.
+ *
+ * @param xfermodeName name matching the uniform declared in the AGSL program
+ * @param xfermode filter passed into the AGSL program for sampling
+ */
+ public void setInputXfermode(@NonNull String xfermodeName, @NonNull RuntimeXfermode xfermode) {
+ if (xfermodeName == null) {
+ throw new NullPointerException("The xfermodeName parameter must not be null");
+ }
+ if (xfermode == null) {
+ throw new NullPointerException("The xfermode parameter must not be null");
+ }
+ nativeUpdateChild(getNativeInstance(), xfermodeName, xfermode.createNativeInstance());
+ }
+
/** @hide */
@Override
protected long createNativeInstance() {
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 78d257f..6316c1f 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -18,10 +18,13 @@
import android.annotation.ColorInt;
import android.annotation.ColorLong;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.util.ArrayMap;
import android.view.Window;
+import com.android.graphics.hwui.flags.Flags;
+
import libcore.util.NativeAllocationRegistry;
/**
@@ -525,6 +528,45 @@
discardNativeInstance();
}
+ /**
+ * Assigns the uniform color filter to the provided color filter parameter. If the shader
+ * program does not have a uniform color filter with that name then an IllegalArgumentException
+ * is thrown.
+ *
+ * @param filterName name matching the uniform declared in the AGSL program
+ * @param colorFilter filter passed into the AGSL program for sampling
+ */
+ @FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
+ public void setInputColorFilter(@NonNull String filterName, @NonNull ColorFilter colorFilter) {
+ if (filterName == null) {
+ throw new NullPointerException("The filterName parameter must not be null");
+ }
+ if (colorFilter == null) {
+ throw new NullPointerException("The colorFilter parameter must not be null");
+ }
+ nativeUpdateChild(mNativeInstanceRuntimeShaderBuilder, filterName,
+ colorFilter.getNativeInstance());
+ }
+
+ /**
+ * Assigns the uniform xfermode to the provided xfermode parameter. If the shader program does
+ * not have a uniform xfermode with that name then an IllegalArgumentException is thrown.
+ *
+ * @param xfermodeName name matching the uniform declared in the AGSL program
+ * @param xfermode filter passed into the AGSL program for sampling
+ */
+ @FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
+ public void setInputXfermode(@NonNull String xfermodeName, @NonNull RuntimeXfermode xfermode) {
+ if (xfermodeName == null) {
+ throw new NullPointerException("The xfermodeName parameter must not be null");
+ }
+ if (xfermode == null) {
+ throw new NullPointerException("The xfermode parameter must not be null");
+ }
+ nativeUpdateChild(mNativeInstanceRuntimeShaderBuilder, xfermodeName,
+ xfermode.createNativeInstance());
+ }
+
/** @hide */
@Override
@@ -552,5 +594,7 @@
int value4, int count);
private static native void nativeUpdateShader(
long shaderBuilder, String shaderName, long shader);
+ private static native void nativeUpdateChild(
+ long shaderBuilder, String childName, long child);
}
diff --git a/graphics/java/android/graphics/RuntimeXfermode.java b/graphics/java/android/graphics/RuntimeXfermode.java
index f5a6568..51d97a4 100644
--- a/graphics/java/android/graphics/RuntimeXfermode.java
+++ b/graphics/java/android/graphics/RuntimeXfermode.java
@@ -288,6 +288,23 @@
nativeUpdateChild(mBuilderNativeInstance, filterName, colorFilter.getNativeInstance());
}
+ /**
+ * Assigns the uniform xfermode to the provided xfermode parameter. If the shader program does
+ * not have a uniform xfermode with that name then an IllegalArgumentException is thrown.
+ *
+ * @param xfermodeName name matching the uniform declared in the AGSL program
+ * @param xfermode xfermode function passed into the AGSL program for sampling
+ */
+ public void setInputXfermode(@NonNull String xfermodeName, @NonNull RuntimeXfermode xfermode) {
+ if (xfermodeName == null) {
+ throw new NullPointerException("The xfermodeName parameter must not be null");
+ }
+ if (xfermode == null) {
+ throw new NullPointerException("The xfermode parameter must not be null");
+ }
+ nativeUpdateChild(mBuilderNativeInstance, xfermodeName, xfermode.createNativeInstance());
+ }
+
/** @hide */
public long createNativeInstance() {
return nativeCreateNativeInstance(mBuilderNativeInstance);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
index 220fc6f..819cf34 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
@@ -94,7 +94,6 @@
*/
void scheduleBackup() {
if (!mSaveEmbeddingState) {
- // TODO(b/289875940): enabled internally for broader testing.
return;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
index cb280c5..0f1246c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
@@ -44,7 +44,6 @@
@NonNull
private final IBinder mSecondaryContainerToken;
- // TODO(b/289875940): making this as non-null once the tag can be auto-generated from the rule.
@Nullable
final String mSplitRuleTag;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java
index a79a89a..bf342d7 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java
@@ -25,6 +25,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* This class holds the Parcelable data of a {@link TaskFragmentContainer}.
*/
@@ -61,6 +64,12 @@
@NonNull
final Rect mLastRequestedBounds;
+ /**
+ * Individual associated activity tokens in different containers that should be finished on
+ * exit.
+ */
+ final List<IBinder> mActivitiesToFinishOnExit = new ArrayList<>();
+
ParcelableTaskFragmentContainerData(@NonNull IBinder token, @Nullable String overlayTag,
@Nullable IBinder associatedActivityToken) {
mToken = token;
@@ -74,6 +83,7 @@
mOverlayTag = in.readString();
mAssociatedActivityToken = in.readStrongBinder();
mLastRequestedBounds = in.readTypedObject(Rect.CREATOR);
+ in.readBinderList(mActivitiesToFinishOnExit);
}
public static final Creator<ParcelableTaskFragmentContainerData> CREATOR = new Creator<>() {
@@ -99,7 +109,7 @@
dest.writeString(mOverlayTag);
dest.writeStrongBinder(mAssociatedActivityToken);
dest.writeTypedObject(mLastRequestedBounds, flags);
+ dest.writeBinderList(mActivitiesToFinishOnExit);
}
-
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index faf73c2..5ba30dd 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -98,10 +98,20 @@
mCurrentSplitAttributes = mDefaultSplitAttributes;
if (shouldFinishPrimaryWithSecondary(splitRule)) {
- mSecondaryContainer.addContainerToFinishOnExit(mPrimaryContainer);
+ addContainerToFinishOnExitWhenRestore(mSecondaryContainer, mPrimaryContainer);
}
if (shouldFinishSecondaryWithPrimary(splitRule)) {
- mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer);
+ addContainerToFinishOnExitWhenRestore(mPrimaryContainer, mSecondaryContainer);
+ }
+ }
+
+ private void addContainerToFinishOnExitWhenRestore(
+ @NonNull TaskFragmentContainer containerToAdd,
+ @NonNull TaskFragmentContainer containerToFinish) {
+ // If an activity was already added to be finished after the restoration, then that's it.
+ // Otherwise, add the container to finish on exit.
+ if (!containerToAdd.hasActivityToFinishOnExit(containerToFinish)) {
+ containerToAdd.addContainerToFinishOnExit(containerToFinish);
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index dc1d983..b3e003e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -96,12 +96,6 @@
new ArrayList<>();
/**
- * Individual associated activity tokens in different containers that should be finished on
- * exit.
- */
- private final List<IBinder> mActivitiesToFinishOnExit = new ArrayList<>();
-
- /**
* The launch options that was used to create this container. Must not {@link Bundle#isEmpty()}
* for {@link #isOverlay()} container.
*/
@@ -114,7 +108,6 @@
/**
* Windowing mode that was requested last via {@link android.window.WindowContainerTransaction}.
*/
- // TODO(b/289875940): review this and other field that might need to be moved in the base class.
@WindowingMode
private int mLastRequestedWindowingMode = WINDOWING_MODE_UNDEFINED;
@@ -443,7 +436,7 @@
// Remove the activity now because there can be a delay before the server callback.
mInfo.getActivities().remove(activityToken);
}
- mActivitiesToFinishOnExit.remove(activityToken);
+ mParcelableData.mActivitiesToFinishOnExit.remove(activityToken);
finishSelfWithActivityIfNeeded(wct, activityToken);
}
@@ -624,7 +617,20 @@
if (mIsFinished) {
return;
}
- mActivitiesToFinishOnExit.add(activityToFinish.getActivityToken());
+ mParcelableData.mActivitiesToFinishOnExit.add(activityToFinish.getActivityToken());
+ }
+
+ /**
+ * Returns {@code true} if an Activity from the given {@code container} was added to be
+ * finished on exit. Otherwise, return {@code false}.
+ */
+ boolean hasActivityToFinishOnExit(@NonNull TaskFragmentContainer container) {
+ for (IBinder activity : mParcelableData.mActivitiesToFinishOnExit) {
+ if (container.hasActivity(activity)) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -634,7 +640,7 @@
if (mIsFinished) {
return;
}
- mActivitiesToFinishOnExit.remove(activityToRemove.getActivityToken());
+ mParcelableData.mActivitiesToFinishOnExit.remove(activityToRemove.getActivityToken());
}
/** Removes all dependencies that should be finished when this container is finished. */
@@ -643,7 +649,7 @@
return;
}
mContainersToFinishOnExit.clear();
- mActivitiesToFinishOnExit.clear();
+ mParcelableData.mActivitiesToFinishOnExit.clear();
}
/**
@@ -721,7 +727,7 @@
mContainersToFinishOnExit.clear();
// Finish associated activities
- for (IBinder activityToken : mActivitiesToFinishOnExit) {
+ for (IBinder activityToken : mParcelableData.mActivitiesToFinishOnExit) {
final Activity activity = mController.getActivity(activityToken);
if (activity == null || activity.isFinishing()
|| controller.shouldRetainAssociatedActivity(this, activity)) {
@@ -729,7 +735,7 @@
}
wct.finishActivity(activity.getActivityToken());
}
- mActivitiesToFinishOnExit.clear();
+ mParcelableData.mActivitiesToFinishOnExit.clear();
}
@GuardedBy("mController.mLock")
@@ -1082,7 +1088,7 @@
+ " pendingAppearedActivities=" + mPendingAppearedActivities
+ (includeContainersToFinishOnExit ? " containersToFinishOnExit="
+ containersToFinishOnExitToString() : "")
- + " activitiesToFinishOnExit=" + mActivitiesToFinishOnExit
+ + " activitiesToFinishOnExit=" + mParcelableData.mActivitiesToFinishOnExit
+ " info=" + mInfo
+ "}";
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
index eb33ff4..35c90ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
@@ -52,6 +52,9 @@
private final SurfaceControl.Transaction mStartTransaction;
private final SurfaceControl.Transaction mFinishTransaction;
+ private final int mCornerRadius;
+ private final int mShadowRadius;
+
// Bounds updated by the evaluator as animator is running.
private final Rect mAnimatedRect = new Rect();
@@ -128,6 +131,8 @@
final int enterAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
+ mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
+ mShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
setDuration(enterAnimationDuration);
setFloatValues(0f, 1f);
setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
@@ -177,6 +182,8 @@
mTransformTensor.postRotate(degrees);
tx.setMatrix(mLeash, mTransformTensor, mMatrixTmp);
+ tx.setCornerRadius(mLeash, mCornerRadius).setShadowRadius(mLeash, mShadowRadius);
+
if (mContentOverlay != null) {
mContentOverlay.onAnimationUpdate(tx, 1f / scaleX, fraction, mEndBounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
index 4558a9f..06e8349 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
@@ -29,6 +29,7 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.shared.animation.Interpolators;
@@ -50,6 +51,9 @@
private Runnable mAnimationEndCallback;
private RectEvaluator mRectEvaluator;
+ private final int mCornerRadius;
+ private final int mShadowRadius;
+
// Bounds relative to which scaling/cropping must be done.
private final Rect mBaseBounds = new Rect();
@@ -74,7 +78,8 @@
mAnimationStartCallback.run();
}
if (mStartTx != null) {
- setBoundsAndRotation(mStartTx, mLeash, mBaseBounds, mStartBounds, mDelta);
+ setBoundsAndRotation(mStartTx, mLeash, mBaseBounds, mStartBounds, mDelta,
+ mCornerRadius, mShadowRadius);
mStartTx.apply();
}
}
@@ -83,7 +88,8 @@
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (mFinishTx != null) {
- setBoundsAndRotation(mFinishTx, mLeash, mBaseBounds, mEndBounds, 0f);
+ setBoundsAndRotation(mFinishTx, mLeash, mBaseBounds, mEndBounds, 0f,
+ mCornerRadius, mShadowRadius);
}
if (mAnimationEndCallback != null) {
mAnimationEndCallback.run();
@@ -99,7 +105,8 @@
mSurfaceControlTransactionFactory.getTransaction();
final float fraction = getAnimatedFraction();
final float degrees = (1.0f - fraction) * mDelta;
- setBoundsAndRotation(tx, mLeash, mBaseBounds, mAnimatedRect, degrees);
+ setBoundsAndRotation(tx, mLeash, mBaseBounds, mAnimatedRect, degrees,
+ mCornerRadius, mShadowRadius);
tx.apply();
}
};
@@ -128,6 +135,9 @@
mRectEvaluator = new RectEvaluator(mAnimatedRect);
+ mCornerRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
+ mShadowRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
+
setObjectValues(startBounds, endBounds);
setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
addListener(mAnimatorListener);
@@ -152,7 +162,7 @@
* @param degrees degrees of rotation - counter-clockwise is positive by convention.
*/
private static void setBoundsAndRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
- Rect baseBounds, Rect targetBounds, float degrees) {
+ Rect baseBounds, Rect targetBounds, float degrees, int cornerRadius, int shadowRadius) {
Matrix transformTensor = new Matrix();
final float[] mMatrixTmp = new float[9];
final float scaleX = (float) targetBounds.width() / baseBounds.width();
@@ -162,7 +172,9 @@
transformTensor.postTranslate(targetBounds.left, targetBounds.top);
transformTensor.postRotate(degrees, targetBounds.centerX(), targetBounds.centerY());
- tx.setMatrix(leash, transformTensor, mMatrixTmp);
+ tx.setMatrix(leash, transformTensor, mMatrixTmp)
+ .setCornerRadius(leash, cornerRadius)
+ .setShadowRadius(leash, shadowRadius);
}
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index 3738353..fd387d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -785,8 +785,16 @@
private void handleFlingTransition(SurfaceControl.Transaction startTx,
SurfaceControl.Transaction finishTx, Rect destinationBounds) {
- startTx.setPosition(mPipTransitionState.getPinnedTaskLeash(),
- destinationBounds.left, destinationBounds.top);
+ SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
+ int cornerRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
+ int shadowRadius = mContext.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
+
+ // merge transactions so everything is done on startTx
+ startTx.merge(finishTx);
+
+ startTx.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
+ .setCornerRadius(pipLeash, cornerRadius)
+ .setShadowRadius(pipLeash, shadowRadius);
startTx.apply();
// All motion operations have actually finished, so make bounds cache updates.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 99f3799..852eee5f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -600,13 +600,25 @@
private Rect calculateBoundingRectLocal(@NonNull OccludingCaptionElement element,
int elementWidthPx, @NonNull Rect captionRect) {
+ final boolean isRtl =
+ mDecorWindowContext.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL;
switch (element.mAlignment) {
case START -> {
- return new Rect(0, 0, elementWidthPx, captionRect.height());
+ if (isRtl) {
+ return new Rect(captionRect.width() - elementWidthPx, 0,
+ captionRect.width(), captionRect.height());
+ } else {
+ return new Rect(0, 0, elementWidthPx, captionRect.height());
+ }
}
case END -> {
- return new Rect(captionRect.width() - elementWidthPx, 0,
- captionRect.width(), captionRect.height());
+ if (isRtl) {
+ return new Rect(0, 0, elementWidthPx, captionRect.height());
+ } else {
+ return new Rect(captionRect.width() - elementWidthPx, 0,
+ captionRect.width(), captionRect.height());
+ }
}
}
throw new IllegalArgumentException("Unexpected alignment " + element.mAlignment);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 503ad92..5f25f42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -114,7 +114,7 @@
// If handle is not in status bar region(i.e., bottom stage in vertical split),
// do not create an input layer
if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
- if (!isCaptionVisible && statusBarInputLayerExists) {
+ if (!isCaptionVisible) {
disposeStatusBarInputLayer()
return
}
@@ -227,6 +227,7 @@
* is not visible.
*/
fun disposeStatusBarInputLayer() {
+ if (!statusBarInputLayerExists) return
statusBarInputLayerExists = false
handler.post {
statusBarInputLayer?.releaseView()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
index a4008c1..72c4666 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipEnterAnimatorTest.java
@@ -22,6 +22,7 @@
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -39,6 +40,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.phone.PipAppIconOverlay;
@@ -49,33 +51,25 @@
import org.mockito.MockitoAnnotations;
/**
- * Unit test again {@link PipEnterAnimator}.
+ * Unit test against {@link PipEnterAnimator}.
*/
@SmallTest
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner.class)
public class PipEnterAnimatorTest {
+ private static final float TEST_CORNER_RADIUS = 1f;
+ private static final float TEST_SHADOW_RADIUS = 2f;
@Mock private Context mMockContext;
-
@Mock private Resources mMockResources;
-
@Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
-
@Mock private SurfaceControl.Transaction mMockAnimateTransaction;
-
@Mock private SurfaceControl.Transaction mMockStartTransaction;
-
@Mock private SurfaceControl.Transaction mMockFinishTransaction;
-
@Mock private Runnable mMockStartCallback;
-
@Mock private Runnable mMockEndCallback;
-
@Mock private PipAppIconOverlay mMockPipAppIconOverlay;
-
@Mock private SurfaceControl mMockAppIconOverlayLeash;
-
@Mock private ActivityInfo mMockActivityInfo;
@Surface.Rotation private int mRotation;
@@ -89,13 +83,15 @@
when(mMockContext.getResources()).thenReturn(mMockResources);
when(mMockResources.getInteger(anyInt())).thenReturn(0);
when(mMockFactory.getTransaction()).thenReturn(mMockAnimateTransaction);
- when(mMockAnimateTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockAnimateTransaction);
- when(mMockStartTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockStartTransaction);
- when(mMockFinishTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockFinishTransaction);
when(mMockPipAppIconOverlay.getLeash()).thenReturn(mMockAppIconOverlayLeash);
+ when(mMockResources.getDimensionPixelSize(R.dimen.pip_corner_radius))
+ .thenReturn((int) TEST_CORNER_RADIUS);
+ when(mMockResources.getDimensionPixelSize(R.dimen.pip_shadow_radius))
+ .thenReturn((int) TEST_SHADOW_RADIUS);
+
+ prepareTransaction(mMockAnimateTransaction);
+ prepareTransaction(mMockStartTransaction);
+ prepareTransaction(mMockFinishTransaction);
mTestLeash = new SurfaceControl.Builder()
.setContainerLayer()
@@ -122,6 +118,12 @@
verify(mMockStartCallback).run();
verifyZeroInteractions(mMockEndCallback);
+
+ // Check corner and shadow radii were set
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
}
@Test
@@ -142,6 +144,12 @@
verify(mMockStartCallback).run();
verify(mMockEndCallback).run();
+
+ // Check corner and shadow radii were set
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
}
@Test
@@ -197,5 +205,21 @@
verify(mMockPipAppIconOverlay).onAnimationUpdate(
eq(mMockAnimateTransaction), anyFloat(), eq(fraction), eq(mEndBounds));
+
+ // Check corner and shadow radii were set
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockAnimateTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
+ }
+
+ // set up transaction chaining
+ private void prepareTransaction(SurfaceControl.Transaction tx) {
+ when(tx.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(tx);
+ when(tx.setCornerRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(tx);
+ when(tx.setShadowRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(tx);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
index 0adb50b..23fbad0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
@@ -16,15 +16,18 @@
package com.android.wm.shell.pip2.animation;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.kotlin.MatchersKt.eq;
-import static org.junit.Assert.assertEquals;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -34,12 +37,14 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -52,40 +57,40 @@
public class PipResizeAnimatorTest {
private static final float FLOAT_COMPARISON_DELTA = 0.001f;
+ private static final float TEST_CORNER_RADIUS = 1f;
+ private static final float TEST_SHADOW_RADIUS = 2f;
@Mock private Context mMockContext;
-
+ @Mock private Resources mMockResources;
@Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
-
@Mock private SurfaceControl.Transaction mMockTransaction;
-
@Mock private SurfaceControl.Transaction mMockStartTransaction;
-
@Mock private SurfaceControl.Transaction mMockFinishTransaction;
-
@Mock private Runnable mMockStartCallback;
-
@Mock private Runnable mMockEndCallback;
+ @Captor private ArgumentCaptor<Matrix> mArgumentCaptor;
+
private PipResizeAnimator mPipResizeAnimator;
private Rect mBaseBounds;
private Rect mStartBounds;
private Rect mEndBounds;
private SurfaceControl mTestLeash;
- private ArgumentCaptor<Matrix> mArgumentCaptor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mMockFactory.getTransaction()).thenReturn(mMockTransaction);
- when(mMockTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockTransaction);
- when(mMockStartTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockStartTransaction);
- when(mMockFinishTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
- .thenReturn(mMockFinishTransaction);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockResources.getDimensionPixelSize(R.dimen.pip_corner_radius))
+ .thenReturn((int) TEST_CORNER_RADIUS);
+ when(mMockResources.getDimensionPixelSize(R.dimen.pip_shadow_radius))
+ .thenReturn((int) TEST_SHADOW_RADIUS);
- mArgumentCaptor = ArgumentCaptor.forClass(Matrix.class);
+ prepareTransaction(mMockTransaction);
+ prepareTransaction(mMockStartTransaction);
+ prepareTransaction(mMockFinishTransaction);
+
mTestLeash = new SurfaceControl.Builder()
.setContainerLayer()
.setName("PipResizeAnimatorTest")
@@ -187,6 +192,12 @@
assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+
+ // Check corner and shadow radii were set
+ verify(mMockTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
}
@Test
@@ -237,6 +248,12 @@
assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+
+ // Check corner and shadow radii were set
+ verify(mMockTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
}
@Test
@@ -272,5 +289,21 @@
mArgumentCaptor.getValue().getValues(matrix);
assertEquals(matrix[Matrix.MSKEW_X], 0f, FLOAT_COMPARISON_DELTA);
assertEquals(matrix[Matrix.MSKEW_Y], 0f, FLOAT_COMPARISON_DELTA);
+
+ // Check corner and shadow radii were set
+ verify(mMockTransaction, atLeastOnce())
+ .setCornerRadius(eq(mTestLeash), eq(TEST_CORNER_RADIUS));
+ verify(mMockTransaction, atLeastOnce())
+ .setShadowRadius(eq(mTestLeash), eq(TEST_SHADOW_RADIUS));
+ }
+
+ // set up transaction chaining
+ private void prepareTransaction(SurfaceControl.Transaction tx) {
+ when(tx.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+ .thenReturn(tx);
+ when(tx.setCornerRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(tx);
+ when(tx.setShadowRadius(any(SurfaceControl.class), anyFloat()))
+ .thenReturn(tx);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index f7b190c..f653622 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -220,7 +220,7 @@
@Captor
private ArgumentCaptor<Runnable> mCloseMaxMenuRunnable;
- private final InsetsState mInsetsState = new InsetsState();
+ private final InsetsState mInsetsState = createInsetsState(statusBars(), /* visible= */true);
private SurfaceControl.Transaction mMockTransaction;
private StaticMockitoSession mMockitoSession;
private TestableContext mTestableContext;
@@ -1433,8 +1433,6 @@
public void notifyCaptionStateChanged_flagDisabled_doNoNotify() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -1448,8 +1446,6 @@
public void notifyCaptionStateChanged_inFullscreenMode_notifiesAppHandleVisible() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
@@ -1469,8 +1465,6 @@
public void notifyCaptionStateChanged_inWindowingMode_notifiesAppHeaderVisible() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
when(mMockAppHeaderViewHolder.getAppChipLocationInWindow()).thenReturn(
new Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -1498,8 +1492,6 @@
public void notifyCaptionStateChanged_taskNotVisible_notifiesNoCaptionVisible() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ false);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
@@ -1518,8 +1510,6 @@
public void notifyCaptionStateChanged_captionHandleExpanded_notifiesHandleMenuExpanded() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
@@ -1543,8 +1533,6 @@
public void notifyCaptionStateChanged_captionHandleClosed_notifiesHandleMenuClosed() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- when(mMockDisplayController.getInsetsState(taskInfo.displayId))
- .thenReturn(createInsetsState(statusBars(), /* visible= */true));
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass(
diff --git a/libs/hwui/jni/RuntimeEffectUtils.cpp b/libs/hwui/jni/RuntimeEffectUtils.cpp
index 46db863..ad0e540 100644
--- a/libs/hwui/jni/RuntimeEffectUtils.cpp
+++ b/libs/hwui/jni/RuntimeEffectUtils.cpp
@@ -90,7 +90,7 @@
SkFlattenable* childEffect) {
SkRuntimeShaderBuilder::BuilderChild builderChild = builder->child(childName);
if (builderChild.fChild == nullptr) {
- ThrowIAEFmt(env, "unable to find shader named %s", childName);
+ ThrowIAEFmt(env, "unable to find child named %s", childName);
return;
}
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 2a057e7..018c2b13 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -2,6 +2,7 @@
#include "Gainmap.h"
#include "GraphicsJNI.h"
+#include "RuntimeEffectUtils.h"
#include "SkBitmap.h"
#include "SkBlendMode.h"
#include "SkColor.h"
@@ -280,50 +281,6 @@
return ret;
}
-static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
- switch (type) {
- case SkRuntimeEffect::Uniform::Type::kFloat:
- case SkRuntimeEffect::Uniform::Type::kFloat2:
- case SkRuntimeEffect::Uniform::Type::kFloat3:
- case SkRuntimeEffect::Uniform::Type::kFloat4:
- case SkRuntimeEffect::Uniform::Type::kFloat2x2:
- case SkRuntimeEffect::Uniform::Type::kFloat3x3:
- case SkRuntimeEffect::Uniform::Type::kFloat4x4:
- return false;
- case SkRuntimeEffect::Uniform::Type::kInt:
- case SkRuntimeEffect::Uniform::Type::kInt2:
- case SkRuntimeEffect::Uniform::Type::kInt3:
- case SkRuntimeEffect::Uniform::Type::kInt4:
- return true;
- }
-}
-
-static void UpdateFloatUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder,
- const char* uniformName, const float values[], int count,
- bool isColor) {
- SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName);
- if (uniform.fVar == nullptr) {
- ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
- } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
- if (isColor) {
- jniThrowExceptionFmt(
- env, "java/lang/IllegalArgumentException",
- "attempting to set a color uniform using the non-color specific APIs: %s %x",
- uniformName, uniform.fVar->flags);
- } else {
- ThrowIAEFmt(env,
- "attempting to set a non-color uniform using the setColorUniform APIs: %s",
- uniformName);
- }
- } else if (isIntUniformType(uniform.fVar->type)) {
- ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
- uniformName);
- } else if (!uniform.set<float>(values, count)) {
- ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
- uniform.fVar->sizeInBytes(), sizeof(float) * count);
- }
-}
-
static void RuntimeShader_updateFloatUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
jstring jUniformName, jfloat value1, jfloat value2,
jfloat value3, jfloat value4, jint count) {
@@ -342,20 +299,6 @@
UpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(), isColor);
}
-static void UpdateIntUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder, const char* uniformName,
- const int values[], int count) {
- SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName);
- if (uniform.fVar == nullptr) {
- ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
- } else if (!isIntUniformType(uniform.fVar->type)) {
- ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
- uniformName);
- } else if (!uniform.set<int>(values, count)) {
- ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
- uniform.fVar->sizeInBytes(), sizeof(float) * count);
- }
-}
-
static void RuntimeShader_updateIntUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
jstring jUniformName, jint value1, jint value2,
jint value3, jint value4, jint count) {
@@ -388,6 +331,15 @@
builder->child(name.c_str()) = sk_ref_sp(shader);
}
+static void RuntimeShader_updateChild(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jlong childHandle) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ auto* childEffect = reinterpret_cast<SkFlattenable*>(childHandle);
+
+ UpdateChild(env, builder, name.c_str(), childEffect);
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gShaderMethods[] = {
@@ -428,6 +380,7 @@
{"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
(void*)RuntimeShader_updateIntUniforms},
{"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader},
+ {"nativeUpdateChild", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateChild},
};
int register_android_graphics_Shader(JNIEnv* env)
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 5b1ea8b..d8a8c8b 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -1,13 +1,7 @@
package: "com.android.media.flags"
container: "system"
-flag {
- name: "enable_rlp_callbacks_in_media_router2"
- is_exported: true
- namespace: "media_solutions"
- description: "Make RouteListingPreference getter and callbacks public in MediaRouter2."
- bug: "281067101"
-}
+# Flags are ordered alphabetically by name.
flag {
name: "adjust_volume_for_foreground_app_playing_audio_without_media_session"
@@ -17,6 +11,13 @@
}
flag {
+ name: "enable_audio_input_device_routing_and_volume_control"
+ namespace: "media_better_together"
+ description: "Allows audio input devices routing and volume control via system settings."
+ bug: "355684672"
+}
+
+flag {
name: "enable_audio_policies_device_and_bluetooth_controller"
is_exported: true
namespace: "media_solutions"
@@ -25,17 +26,54 @@
}
flag {
- name: "fallback_to_default_handling_when_media_session_has_fixed_volume_handling"
- namespace: "media_solutions"
- description: "Fallbacks to the default handling for volume adjustment when media session has fixed volume handling and its app is in the foreground and setting a media controller."
- bug: "293743975"
+ name: "enable_built_in_speaker_route_suitability_statuses"
+ is_exported: true
+ namespace: "media_solutions"
+ description: "Make MediaRoute2Info provide information about routes suitability for transfer."
+ bug: "279555229"
}
flag {
- name: "enable_waiting_state_for_system_session_creation_request"
+ name: "enable_cross_user_routing_in_media_router2"
+ is_exported: true
namespace: "media_solutions"
- description: "Introduces a waiting state for the session creation request and prevents it from early failing when the selectedRoute from the bluetooth stack doesn't match the pending request route id."
- bug: "307723189"
+ description: "Allows clients of privileged MediaRouter2 that hold INTERACT_ACROSS_USERS_FULL to control routing across users."
+ bug: "288580225"
+}
+
+flag {
+ name: "enable_full_scan_with_media_content_control"
+ namespace: "media_better_together"
+ description: "Allows holders of the MEDIA_CONTENT_CONTROL permission to scan for routes while not in the foreground."
+ bug: "352401364"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_get_transferable_routes"
+ is_exported: true
+ namespace: "media_solutions"
+ description: "Exposes RoutingController#getTransferableRoutes() (previously hidden) to the public API."
+ bug: "323154573"
+}
+
+flag {
+ name: "enable_mirroring_in_media_router_2"
+ namespace: "media_better_together"
+ description: "Enables support for mirroring routes in the MediaRouter2 framework, allowing Output Switcher to offer mirroring routes."
+ bug: "362507305"
+}
+
+flag {
+ name: "enable_mr2_service_non_main_bg_thread"
+ namespace: "media_solutions"
+ description: "Enables the use of a background thread in the media routing framework, instead of using the main thread."
+ bug: "310145678"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -55,37 +93,6 @@
}
flag {
- name: "enable_privileged_routing_for_media_routing_control"
- is_exported: true
- namespace: "media_solutions"
- description: "Allow access to privileged routing capabilities to MEDIA_ROUTING_CONTROL holders."
- bug: "305919655"
-}
-
-flag {
- name: "enable_cross_user_routing_in_media_router2"
- is_exported: true
- namespace: "media_solutions"
- description: "Allows clients of privileged MediaRouter2 that hold INTERACT_ACROSS_USERS_FULL to control routing across users."
- bug: "288580225"
-}
-
-flag {
- name: "enable_use_of_bluetooth_device_get_alias_for_mr2info_get_name"
- namespace: "media_solutions"
- description: "Use BluetoothDevice.getAlias to populate the name of Bluetooth MediaRoute2Infos."
- bug: "314324170"
-}
-
-flag {
- name: "enable_built_in_speaker_route_suitability_statuses"
- is_exported: true
- namespace: "media_solutions"
- description: "Make MediaRoute2Info provide information about routes suitability for transfer."
- bug: "279555229"
-}
-
-flag {
name: "enable_notifying_activity_manager_with_media_session_status_change"
is_exported: true
namespace: "media_solutions"
@@ -94,11 +101,10 @@
}
flag {
- name: "enable_get_transferable_routes"
- is_exported: true
+ name: "enable_null_session_in_media_browser_service"
namespace: "media_solutions"
- description: "Exposes RoutingController#getTransferableRoutes() (previously hidden) to the public API."
- bug: "323154573"
+ description: "Enables apps owning a MediaBrowserService to disconnect all connected browsers."
+ bug: "185136506"
}
flag {
@@ -109,31 +115,6 @@
}
flag {
- name: "enable_mr2_service_non_main_bg_thread"
- namespace: "media_solutions"
- description: "Enables the use of a background thread in the media routing framework, instead of using the main thread."
- bug: "310145678"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "enable_screen_off_scanning"
- is_exported: true
- namespace: "media_solutions"
- description: "Enable new MediaRouter2 API to enable watch companion apps to scan while the phone screen is off."
- bug: "281072508"
-}
-
-flag {
- name: "enable_null_session_in_media_browser_service"
- namespace: "media_solutions"
- description: "Enables apps owning a MediaBrowserService to disconnect all connected browsers."
- bug: "185136506"
-}
-
-flag {
name: "enable_prevention_of_manager_scans_when_no_apps_scan"
namespace: "media_solutions"
description: "Prevents waking up route providers when no apps are scanning, even if SysUI or Settings are scanning."
@@ -144,25 +125,46 @@
}
flag {
- name: "enable_full_scan_with_media_content_control"
- namespace: "media_better_together"
- description: "Allows holders of the MEDIA_CONTENT_CONTROL permission to scan for routes while not in the foreground."
- bug: "352401364"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
+ name: "enable_privileged_routing_for_media_routing_control"
+ is_exported: true
+ namespace: "media_solutions"
+ description: "Allow access to privileged routing capabilities to MEDIA_ROUTING_CONTROL holders."
+ bug: "305919655"
}
flag {
- name: "enable_audio_input_device_routing_and_volume_control"
- namespace: "media_better_together"
- description: "Allows audio input devices routing and volume control via system settings."
- bug: "355684672"
+ name: "enable_rlp_callbacks_in_media_router2"
+ is_exported: true
+ namespace: "media_solutions"
+ description: "Make RouteListingPreference getter and callbacks public in MediaRouter2."
+ bug: "281067101"
}
flag {
- name: "enable_mirroring_in_media_router_2"
- namespace: "media_better_together"
- description: "Enables support for mirroring routes in the MediaRouter2 framework, allowing Output Switcher to offer mirroring routes."
- bug: "362507305"
+ name: "enable_screen_off_scanning"
+ is_exported: true
+ namespace: "media_solutions"
+ description: "Enable new MediaRouter2 API to enable watch companion apps to scan while the phone screen is off."
+ bug: "281072508"
+}
+
+flag {
+ name: "enable_use_of_bluetooth_device_get_alias_for_mr2info_get_name"
+ namespace: "media_solutions"
+ description: "Use BluetoothDevice.getAlias to populate the name of Bluetooth MediaRoute2Infos."
+ bug: "314324170"
+}
+
+flag {
+ name: "enable_waiting_state_for_system_session_creation_request"
+ namespace: "media_solutions"
+ description: "Introduces a waiting state for the session creation request and prevents it from early failing when the selectedRoute from the bluetooth stack doesn't match the pending request route id."
+ bug: "307723189"
+}
+
+flag {
+ name: "fallback_to_default_handling_when_media_session_has_fixed_volume_handling"
+ namespace: "media_solutions"
+ description: "Fallbacks to the default handling for volume adjustment when media session has fixed volume handling and its app is in the foreground and setting a media controller."
+ bug: "293743975"
}
diff --git a/media/java/android/media/quality/AmbientBacklightEvent.java b/media/java/android/media/quality/AmbientBacklightEvent.java
index 5c11def..273f21e 100644
--- a/media/java/android/media/quality/AmbientBacklightEvent.java
+++ b/media/java/android/media/quality/AmbientBacklightEvent.java
@@ -40,7 +40,7 @@
@IntDef({AMBIENT_BACKLIGHT_EVENT_ENABLED, AMBIENT_BACKLIGHT_EVENT_DISABLED,
AMBIENT_BACKLIGHT_EVENT_METADATA,
AMBIENT_BACKLIGHT_EVENT_INTERRUPTED})
- public @interface AmbientBacklightEventTypes {}
+ public @interface Type {}
/**
* Event type for ambient backlight events. The ambient backlight is enabled.
@@ -69,9 +69,9 @@
private final AmbientBacklightMetadata mMetadata;
/**
- * Constructor of AmbientBacklightEvent.
+ * Constructs AmbientBacklightEvent.
*/
- public AmbientBacklightEvent(int eventType,
+ public AmbientBacklightEvent(@Type int eventType,
@Nullable AmbientBacklightMetadata metadata) {
mEventType = eventType;
mMetadata = metadata;
@@ -85,6 +85,7 @@
/**
* Gets event type.
*/
+ @Type
public int getEventType() {
return mEventType;
}
diff --git a/media/java/android/media/quality/AmbientBacklightMetadata.java b/media/java/android/media/quality/AmbientBacklightMetadata.java
index 9c11f9a..5cea10d 100644
--- a/media/java/android/media/quality/AmbientBacklightMetadata.java
+++ b/media/java/android/media/quality/AmbientBacklightMetadata.java
@@ -29,6 +29,9 @@
/**
* Metadata of ambient backlight.
+ *
+ * <p>A metadata instance is sent from ambient backlight hardware in a {@link AmbientBacklightEvent}
+ * with {@link AmbientBacklightEvent#AMBIENT_BACKLIGHT_EVENT_METADATA}.
* @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
@@ -44,10 +47,15 @@
private final int[] mZonesColors;
/**
- * Constructor of AmbientBacklightMetadata.
+ * Constructs AmbientBacklightMetadata.
*/
- public AmbientBacklightMetadata(@NonNull String packageName, int compressAlgorithm,
- int source, int colorFormat, int horizontalZonesNumber, int verticalZonesNumber,
+ public AmbientBacklightMetadata(
+ @NonNull String packageName,
+ @AmbientBacklightSettings.CompressAlgorithm int compressAlgorithm,
+ @AmbientBacklightSettings.Source int source,
+ @PixelFormat.Format int colorFormat,
+ int horizontalZonesNumber,
+ int verticalZonesNumber,
@NonNull int[] zonesColors) {
mPackageName = packageName;
mCompressAlgorithm = compressAlgorithm;
@@ -69,7 +77,7 @@
}
/**
- * Gets package name.
+ * Gets package name of the metadata.
* @hide
*/
@NonNull
@@ -102,7 +110,9 @@
}
/**
- * Gets the number of lights in each horizontal zone.
+ * Gets the number of horizontal color zones.
+ *
+ * <p>A color zone is a group of lights that always display the same color.
*/
@IntRange(from = 0)
public int getHorizontalZonesNumber() {
@@ -110,7 +120,9 @@
}
/**
- * Gets the number of lights in each vertical zone.
+ * Gets the number of vertical color zones.
+ *
+ * <p>A color zone is a group of lights that always display the same color.
*/
@IntRange(from = 0)
public int getVerticalZonesNumber() {
@@ -118,10 +130,11 @@
}
/**
+ * Gets color data of vertical color zones.
* @hide
*/
@NonNull
- public int[] getZonesColors() {
+ public int[] getVerticalZonesColors() {
return mZonesColors;
}
diff --git a/media/java/android/media/quality/AmbientBacklightSettings.java b/media/java/android/media/quality/AmbientBacklightSettings.java
index 4ed7bc7..d904cf7 100644
--- a/media/java/android/media/quality/AmbientBacklightSettings.java
+++ b/media/java/android/media/quality/AmbientBacklightSettings.java
@@ -30,7 +30,7 @@
import java.lang.annotation.RetentionPolicy;
/**
- * Settings for ambient backlight.
+ * Settings to configure ambient backlight hardware.
* @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
@@ -124,8 +124,13 @@
/**
* Constructs AmbientBacklightSettings.
*/
- public AmbientBacklightSettings(int source, int maxFps, int colorFormat,
- int horizontalZonesNumber, int verticalZonesNumber, boolean isLetterboxOmitted,
+ public AmbientBacklightSettings(
+ @Source int source,
+ int maxFps,
+ @PixelFormat.Format int colorFormat,
+ int horizontalZonesNumber,
+ int verticalZonesNumber,
+ boolean isLetterboxOmitted,
int threshold) {
mSource = source;
mMaxFps = maxFps;
@@ -171,7 +176,9 @@
}
/**
- * Gets the number of lights in each horizontal zone.
+ * Gets the number of horizontal color zones.
+ *
+ * <p>A color zone is a group of lights that always display the same color.
*/
@IntRange(from = 0)
public int getHorizontalZonesNumber() {
@@ -179,7 +186,9 @@
}
/**
- * Gets the number of lights in each vertical zone.
+ * Gets the number of vertical color zones.
+ *
+ * <p>A color zone is a group of lights that always display the same color.
*/
@IntRange(from = 0)
public int getVerticalZonesNumber() {
@@ -187,7 +196,11 @@
}
/**
- * Returns {@code true} if letter box is omitted; {@code false} otherwise.
+ * Returns {@code true} if the black portion of the screen in letter box mode is omitted;
+ * {@code false} otherwise.
+ *
+ * <p>Letter-box is a technique to keep the original aspect ratio when displayed on a screen
+ * with different aspect ratio. Black bars are added to the top and bottom.
* @hide
*/
public boolean isLetterboxOmitted() {
@@ -195,6 +208,10 @@
}
/**
+ * Gets the detection threshold of the ambient light.
+ *
+ * <p>If the color of a color zone is changed by the difference is smaller than the threshold,
+ * the change is ignored.
* @hide
*/
public int getThreshold() {
diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
index 250d59b..aaedf21 100644
--- a/media/java/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -42,10 +42,12 @@
SoundProfile createSoundProfile(in SoundProfile pp);
void updateSoundProfile(in String id, in SoundProfile pp);
void removeSoundProfile(in String id);
- SoundProfile getSoundProfileById(in String id);
+ SoundProfile getSoundProfile(in int type, in String name);
List<SoundProfile> getSoundProfilesByPackage(in String packageName);
List<SoundProfile> getAvailableSoundProfiles();
List<String> getSoundProfilePackageNames();
+ List<String> getSoundProfileAllowList();
+ void setSoundProfileAllowList(in List<String> packages);
void registerPictureProfileCallback(in IPictureProfileCallback cb);
void registerSoundProfileCallback(in ISoundProfileCallback cb);
diff --git a/media/java/android/media/quality/ISoundProfileCallback.aidl b/media/java/android/media/quality/ISoundProfileCallback.aidl
index 72d1524..9043757 100644
--- a/media/java/android/media/quality/ISoundProfileCallback.aidl
+++ b/media/java/android/media/quality/ISoundProfileCallback.aidl
@@ -17,6 +17,7 @@
package android.media.quality;
+import android.media.quality.ParamCapability;
import android.media.quality.SoundProfile;
/**
@@ -24,7 +25,9 @@
* @hide
*/
oneway interface ISoundProfileCallback {
- void onSoundProfileAdded(in long id, in SoundProfile p);
- void onSoundProfileUpdated(in long id, in SoundProfile p);
- void onSoundProfileRemoved(in long id, in SoundProfile p);
+ void onSoundProfileAdded(in String id, in SoundProfile p);
+ void onSoundProfileUpdated(in String id, in SoundProfile p);
+ void onSoundProfileRemoved(in String id, in SoundProfile p);
+ void onParamCapabilitiesChanged(in String id, in List<ParamCapability> caps);
+ void onError(in int err);
}
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 4d4526c..dcf4971 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -111,7 +111,7 @@
};
ISoundProfileCallback spCallback = new ISoundProfileCallback.Stub() {
@Override
- public void onSoundProfileAdded(long profileId, SoundProfile profile) {
+ public void onSoundProfileAdded(String profileId, SoundProfile profile) {
synchronized (mLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
@@ -120,7 +120,7 @@
}
}
@Override
- public void onSoundProfileUpdated(long profileId, SoundProfile profile) {
+ public void onSoundProfileUpdated(String profileId, SoundProfile profile) {
synchronized (mLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
@@ -129,7 +129,7 @@
}
}
@Override
- public void onSoundProfileRemoved(long profileId, SoundProfile profile) {
+ public void onSoundProfileRemoved(String profileId, SoundProfile profile) {
synchronized (mLock) {
for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
// TODO: filter callback record
@@ -137,6 +137,24 @@
}
}
}
+ @Override
+ public void onParamCapabilitiesChanged(String profileId, List<ParamCapability> caps) {
+ synchronized (mLock) {
+ for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
+ // TODO: filter callback record
+ record.postParamCapabilitiesChanged(profileId, caps);
+ }
+ }
+ }
+ @Override
+ public void onError(int err) {
+ synchronized (mLock) {
+ for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
+ // TODO: filter callback record
+ record.postError(err);
+ }
+ }
+ }
};
IAmbientBacklightCallback abCallback = new IAmbientBacklightCallback.Stub() {
@Override
@@ -331,14 +349,17 @@
/**
- * Gets sound profile by given profile ID.
- * @return the corresponding sound profile if available; {@code null} if the ID doesn't
- * exist or the profile is not accessible to the caller.
+ * Gets sound profile by given profile type and name.
+ *
+ * @return the corresponding sound profile if available; {@code null} if the name doesn't
+ * exist.
* @hide
*/
- public SoundProfile getSoundProfileById(String profileId) {
+ @Nullable
+ public SoundProfile getSoundProfile(
+ @SoundProfile.ProfileType int type, @NonNull String name) {
try {
- return mService.getSoundProfileById(profileId);
+ return mService.getSoundProfile(type, name);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -349,8 +370,9 @@
* @SystemApi gets profiles that available to the given package
* @hide
*/
+ @NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
- public List<SoundProfile> getSoundProfilesByPackage(String packageName) {
+ public List<SoundProfile> getSoundProfilesByPackage(@NonNull String packageName) {
try {
return mService.getSoundProfilesByPackage(packageName);
} catch (RemoteException e) {
@@ -362,6 +384,7 @@
* Gets profiles that available to the caller package
* @hide
*/
+ @NonNull
public List<SoundProfile> getAvailableSoundProfiles() {
try {
return mService.getAvailableSoundProfiles();
@@ -371,9 +394,12 @@
}
/**
- * @SystemApi all stored sound profiles of all packages
+ * @SystemApi Gets all package names whose sound profiles are available.
+ *
+ * @see #getSoundProfilesByPackage(String)
* @hide
*/
+ @NonNull
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public List<String> getSoundProfilePackageNames() {
try {
@@ -387,12 +413,13 @@
/**
* Creates a sound profile and store it in the system.
*
- * @return the stored profile with an assigned profile ID.
+ * <p>If the profile is created successfully,
+ * {@link SoundProfileCallback#onSoundProfileAdded(long, SoundProfile)} is invoked.
* @hide
*/
- public SoundProfile createSoundProfile(SoundProfile sp) {
+ public void createSoundProfile(@NonNull SoundProfile sp) {
try {
- return mService.createSoundProfile(sp);
+ mService.createSoundProfile(sp);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -403,7 +430,7 @@
* Updates an existing sound profile and store it in the system.
* @hide
*/
- public void updateSoundProfile(String profileId, SoundProfile sp) {
+ public void updateSoundProfile(@NonNull String profileId, @NonNull SoundProfile sp) {
try {
mService.updateSoundProfile(profileId, sp);
} catch (RemoteException e) {
@@ -416,7 +443,7 @@
* Removes a sound profile from the system.
* @hide
*/
- public void removeSoundProfile(String profileId) {
+ public void removeSoundProfile(@NonNull String profileId) {
try {
mService.removeSoundProfile(profileId);
} catch (RemoteException e) {
@@ -468,6 +495,36 @@
}
/**
+ * Gets the allowlist of packages that can create and removed sound profiles
+ *
+ * @see #createSoundProfile(SoundProfile)
+ * @see #removeSoundProfile(String)
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
+ @NonNull
+ public List<String> getSoundProfileAllowList() {
+ try {
+ return mService.getSoundProfileAllowList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the allowlist of packages that can create and removed sound profiles
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
+ public void setSoundProfileAllowList(@NonNull List<String> packageNames) {
+ try {
+ mService.setSoundProfileAllowList(packageNames);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns {@code true} if media quality HAL is implemented; {@code false} otherwise.
* @hide
*/
@@ -567,6 +624,7 @@
/**
* Registers a {@link AmbientBacklightCallback}.
+ * @hide
*/
public void registerAmbientBacklightCallback(
@NonNull @CallbackExecutor Executor executor,
@@ -580,6 +638,7 @@
/**
* Unregisters the existing {@link AmbientBacklightCallback}.
+ * @hide
*/
public void unregisterAmbientBacklightCallback(
@NonNull final AmbientBacklightCallback callback) {
@@ -600,6 +659,7 @@
* Set the ambient backlight settings.
*
* @param settings The settings to use for the backlight detector.
+ * @hide
*/
public void setAmbientBacklightSettings(
@NonNull AmbientBacklightSettings settings) {
@@ -615,6 +675,7 @@
* Enables or disables the ambient backlight detection.
*
* @param enabled {@code true} to enable, {@code false} to disable.
+ * @hide
*/
public void setAmbientBacklightEnabled(boolean enabled) {
try {
@@ -698,7 +759,7 @@
return mCallback;
}
- public void postSoundProfileAdded(final long id, SoundProfile profile) {
+ public void postSoundProfileAdded(final String id, SoundProfile profile) {
mExecutor.execute(new Runnable() {
@Override
@@ -708,7 +769,7 @@
});
}
- public void postSoundProfileUpdated(final long id, SoundProfile profile) {
+ public void postSoundProfileUpdated(final String id, SoundProfile profile) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
@@ -717,7 +778,7 @@
});
}
- public void postSoundProfileRemoved(final long id, SoundProfile profile) {
+ public void postSoundProfileRemoved(final String id, SoundProfile profile) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
@@ -725,6 +786,24 @@
}
});
}
+
+ public void postParamCapabilitiesChanged(final String id, List<ParamCapability> caps) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onParamCapabilitiesChanged(id, caps);
+ }
+ });
+ }
+
+ public void postError(int error) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onError(error);
+ }
+ });
+ }
}
private static final class AmbientBacklightCallbackRecord {
@@ -801,12 +880,13 @@
* This is invoked when parameter capabilities has been changed due to status changes of the
* content.
*
- * @param profileId the ID of the profile used by the media content.
+ * @param profileId the ID of the profile used by the media content. {@code null} if there
+ * is no associated profile
* @param updatedCaps the updated capabilities.
* @hide
*/
public void onParamCapabilitiesChanged(
- @NonNull String profileId, @NonNull List<ParamCapability> updatedCaps) {
+ @Nullable String profileId, @NonNull List<ParamCapability> updatedCaps) {
}
}
@@ -816,29 +896,64 @@
*/
public abstract static class SoundProfileCallback {
/**
+ * This is invoked when a sound profile has been added.
+ *
+ * @param profileId the ID of the profile.
+ * @param profile the newly added profile.
* @hide
*/
- public void onSoundProfileAdded(long id, SoundProfile profile) {
+ public void onSoundProfileAdded(
+ @NonNull String profileId, @NonNull SoundProfile profile) {
}
+
/**
+ * This is invoked when a sound profile has been updated.
+ *
+ * @param profileId the ID of the profile.
+ * @param profile the profile with updated info.
* @hide
*/
- public void onSoundProfileUpdated(long id, SoundProfile profile) {
+ public void onSoundProfileUpdated(
+ @NonNull String profileId, @NonNull SoundProfile profile) {
}
+
/**
+ * This is invoked when a sound profile has been removed.
+ *
+ * @param profileId the ID of the profile.
+ * @param profile the removed profile.
* @hide
*/
- public void onSoundProfileRemoved(long id, SoundProfile profile) {
+ public void onSoundProfileRemoved(
+ @NonNull String profileId, @NonNull SoundProfile profile) {
}
+
/**
+ * This is invoked when an issue has occurred.
+ *
+ * @param errorCode the error code
* @hide
*/
- public void onError(int errorCode) {
+ public void onError(@SoundProfile.ErrorCode int errorCode) {
+ }
+
+ /**
+ * This is invoked when parameter capabilities has been changed due to status changes of the
+ * content.
+ *
+ * @param profileId the ID of the profile used by the media content. {@code null} if there
+ * is no associated profile
+ * @param updatedCaps the updated capabilities.
+ * @hide
+ */
+ public void onParamCapabilitiesChanged(
+ @Nullable String profileId, @NonNull List<ParamCapability> updatedCaps) {
}
}
/**
* Callback used to monitor status of ambient backlight.
+ * @hide
*/
public abstract static class AmbientBacklightCallback {
/**
diff --git a/media/java/android/media/quality/SoundProfile.java b/media/java/android/media/quality/SoundProfile.java
index 20d117b..de93afe 100644
--- a/media/java/android/media/quality/SoundProfile.java
+++ b/media/java/android/media/quality/SoundProfile.java
@@ -17,54 +17,119 @@
package android.media.quality;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.media.tv.TvInputInfo;
import android.media.tv.flags.Flags;
-import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresPermission;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
+ * Profile for sound quality.
* @hide
*/
@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
public class SoundProfile implements Parcelable {
@Nullable
- private Long mId;
+ private String mId;
+ private final int mType;
@NonNull
private final String mName;
@Nullable
private final String mInputId;
- @Nullable
+ @NonNull
private final String mPackageName;
@NonNull
- private final Bundle mParams;
+ private final PersistableBundle mParams;
- protected SoundProfile(Parcel in) {
- if (in.readByte() == 0) {
- mId = null;
- } else {
- mId = in.readLong();
- }
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "TYPE_", value = {
+ TYPE_SYSTEM,
+ TYPE_APPLICATION})
+ public @interface ProfileType {}
+
+ /**
+ * System profile type.
+ *
+ * <p>A profile of system type is managed by the system, and readable to the package returned by
+ * {@link #getPackageName()}.
+ */
+ public static final int TYPE_SYSTEM = 1;
+ /**
+ * Application profile type.
+ *
+ * <p>A profile of application type is managed by the package returned by
+ * {@link #getPackageName()}.
+ */
+ public static final int TYPE_APPLICATION = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "ERROR_", value = {
+ ERROR_UNKNOWN,
+ ERROR_NO_PERMISSION,
+ ERROR_DUPLICATE,
+ ERROR_INVALID_ARGUMENT,
+ ERROR_NOT_ALLOWLISTED
+ })
+ public @interface ErrorCode {}
+
+ /**
+ * Error code for unknown errors.
+ */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Error code for missing necessary permission to handle the profiles.
+ */
+ public static final int ERROR_NO_PERMISSION = 1;
+
+ /**
+ * Error code for creating a profile with existing profile type and name.
+ *
+ * @see #getProfileType()
+ * @see #getName()
+ */
+ public static final int ERROR_DUPLICATE = 2;
+
+ /**
+ * Error code for invalid argument.
+ */
+ public static final int ERROR_INVALID_ARGUMENT = 3;
+
+ /**
+ * Error code for the case when an operation requires an allowlist but the caller is not in the
+ * list.
+ *
+ * @see MediaQualityManager#getSoundProfileAllowList()
+ */
+ public static final int ERROR_NOT_ALLOWLISTED = 4;
+
+ protected SoundProfile(@NonNull Parcel in) {
+ mId = in.readString();
+ mType = in.readInt();
mName = in.readString();
mInputId = in.readString();
mPackageName = in.readString();
- mParams = in.readBundle();
+ mParams = in.readPersistableBundle();
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
- if (mId == null) {
- dest.writeByte((byte) 0);
- } else {
- dest.writeByte((byte) 1);
- dest.writeLong(mId);
- }
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeInt(mType);
dest.writeString(mName);
dest.writeString(mInputId);
dest.writeString(mPackageName);
- dest.writeBundle(mParams);
+ dest.writePersistableBundle(mParams);
}
@Override
@@ -72,6 +137,7 @@
return 0;
}
+ @NonNull
public static final Creator<SoundProfile> CREATOR = new Creator<SoundProfile>() {
@Override
public SoundProfile createFromParcel(Parcel in) {
@@ -91,93 +157,164 @@
* @hide
*/
public SoundProfile(
- @Nullable Long id,
+ @Nullable String id,
+ int type,
@NonNull String name,
@Nullable String inputId,
- @Nullable String packageName,
- @NonNull Bundle params) {
+ @NonNull String packageName,
+ @NonNull PersistableBundle params) {
this.mId = id;
+ this.mType = type;
this.mName = name;
- com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
this.mInputId = inputId;
this.mPackageName = packageName;
this.mParams = params;
}
+ /**
+ * Gets profile ID.
+ *
+ * <p>A profile ID is a globally unique ID generated and assigned by the system. For profile
+ * objects retrieved from system (e.g {@link MediaQualityManager#getAvailableSoundProfiles()})
+ * this profile ID is non-null; For profiles built locally with {@link Builder}, it's
+ * {@code null}.
+ *
+ * @return the unique profile ID; {@code null} if the profile is built locally with
+ * {@link Builder}.
+ */
@Nullable
- public Long getProfileId() {
+ public String getProfileId() {
return mId;
}
+ /**
+ * Only used by system to assign the ID.
+ * @hide
+ */
+ public void setProfileId(String id) {
+ mId = id;
+ }
+
+ /**
+ * Gets profile type.
+ */
+ @ProfileType
+ public int getProfileType() {
+ return mType;
+ }
+
+ /**
+ * Gets the profile name.
+ */
@NonNull
public String getName() {
return mName;
}
+ /**
+ * Gets the input ID if the profile is for a TV input.
+ *
+ * @return the corresponding TV input ID; {@code null} if the profile is not associated with a
+ * TV input.
+ *
+ * @see TvInputInfo#getId()
+ */
@Nullable
public String getInputId() {
return mInputId;
}
+ /**
+ * Gets the package name of this profile.
+ *
+ * <p>The package name defines the user of a profile. Only this specific package and system app
+ * can access to this profile.
+ *
+ * @return the package name; {@code null} if the profile is built locally using
+ * {@link Builder} and the package is not set.
+ */
@Nullable
public String getPackageName() {
return mPackageName;
}
+
+ /**
+ * Gets the parameters of this profile.
+ *
+ * <p>The keys of commonly used parameters can be found in
+ * {@link MediaQualityContract.SoundQuality}.
+ */
@NonNull
- public Bundle getParameters() {
- return new Bundle(mParams);
+ public PersistableBundle getParameters() {
+ return new PersistableBundle(mParams);
}
/**
* A builder for {@link SoundProfile}
+ * @hide
*/
public static class Builder {
@Nullable
- private Long mId;
+ private String mId;
+ private int mType = TYPE_APPLICATION;
@NonNull
private String mName;
@Nullable
private String mInputId;
- @Nullable
+ @NonNull
private String mPackageName;
@NonNull
- private Bundle mParams;
+ private PersistableBundle mParams;
/**
* Creates a new Builder.
- *
- * @hide
*/
public Builder(@NonNull String name) {
mName = name;
- com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
}
/**
- * Copy constructor.
- *
- * @hide
+ * Copy constructor of builder.
*/
public Builder(@NonNull SoundProfile p) {
mId = null; // ID needs to be reset
+ mType = p.getProfileType();
mName = p.getName();
mPackageName = p.getPackageName();
mInputId = p.getInputId();
+ mParams = p.getParameters();
}
/**
- * Sets profile ID.
- * @hide using by MediaQualityService
+ * Only used by system to assign the ID.
+ * @hide
*/
@NonNull
- public Builder setProfileId(@Nullable Long id) {
+ public Builder setProfileId(@Nullable String id) {
mId = id;
return this;
}
/**
- * Sets input ID.
+ * Sets profile type.
+ *
+ * @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
+ @NonNull
+ public Builder setProfileType(@ProfileType int value) {
+ mType = value;
+ return this;
+ }
+
+ /**
+ * Sets input ID.
+ *
+ * @see SoundProfile#getInputId()
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
@NonNull
public Builder setInputId(@NonNull String value) {
mInputId = value;
@@ -186,7 +323,12 @@
/**
* Sets package name of the profile.
+ *
+ * @see SoundProfile#getPackageName()
+ *
+ * @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
@NonNull
public Builder setPackageName(@NonNull String value) {
mPackageName = value;
@@ -195,12 +337,15 @@
/**
* Sets profile parameters.
+ *
+ * @see SoundProfile#getParameters()
*/
@NonNull
- public Builder setParameters(@NonNull Bundle params) {
- mParams = new Bundle(params);
+ public Builder setParameters(@NonNull PersistableBundle params) {
+ mParams = new PersistableBundle(params);
return this;
}
+
/**
* Builds the instance.
*/
@@ -209,6 +354,7 @@
SoundProfile o = new SoundProfile(
mId,
+ mType,
mName,
mInputId,
mPackageName,
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 23dd9b7..4180710 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -45,8 +45,10 @@
header_libs: [
"jni_headers",
+ "native_headers",
"libhwui_internal_headers",
],
+ export_header_lib_headers: ["native_headers"],
static_libs: [
"libarect",
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 79a0607..f587660 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -57,6 +57,7 @@
@FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension {
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference();
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int forceRoutingTableCommit();
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.List<java.lang.String> getActiveNfceeList();
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.nfc.RoutingStatus getRoutingStatus();
method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.List<android.nfc.NfcRoutingTableEntry> getRoutingTable();
@@ -73,6 +74,9 @@
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void triggerInitialization();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback);
+ field public static final int COMMIT_ROUTING_STATUS_FAILED = 3; // 0x3
+ field public static final int COMMIT_ROUTING_STATUS_FAILED_UPDATE_IN_PROGRESS = 6; // 0x6
+ field public static final int COMMIT_ROUTING_STATUS_OK = 0; // 0x0
field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int DISABLE = 0; // 0x0
field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int ENABLE_DEFAULT = 1; // 0x1
field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int ENABLE_EE = 3; // 0x3
@@ -96,6 +100,7 @@
method public void onEnableFinished(int);
method public void onEnableRequested(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onEnableStarted();
+ method public void onExtractOemPackages(@NonNull android.nfc.NdefMessage, @NonNull java.util.function.Consumer<java.util.List<java.lang.String>>);
method public void onGetOemAppSearchIntent(@NonNull java.util.List<java.lang.String>, @NonNull java.util.function.Consumer<android.content.Intent>);
method public void onHceEventReceived(int);
method public void onLaunchHceAppChooserActivity(@NonNull String, @NonNull java.util.List<android.nfc.cardemulation.ApduServiceInfo>, @NonNull android.content.ComponentName, @NonNull String);
@@ -106,7 +111,7 @@
method public void onReaderOptionChanged(boolean);
method public void onRfDiscoveryStarted(boolean);
method public void onRfFieldActivated(boolean);
- method public void onRoutingChanged();
+ method public void onRoutingChanged(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void onRoutingTableFull();
method public void onStateUpdated(int);
method public void onTagConnected(boolean);
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index 40fd068..31514a0 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -120,4 +120,5 @@
boolean isTagPresent();
List<Entry> getRoutingTableEntryList();
void indicateDataMigration(boolean inProgress, String pkg);
+ int commitRouting();
}
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
index fb793b0..1a21c0b 100644
--- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -41,7 +41,7 @@
void onEnableFinished(int status);
void onDisableFinished(int status);
void onTagDispatch(in ResultReceiver isSkipped);
- void onRoutingChanged();
+ void onRoutingChanged(in ResultReceiver isSkipped);
void onHceEventReceived(int action);
void onReaderOptionChanged(boolean enabled);
void onCardEmulationActivated(boolean isActivated);
@@ -54,4 +54,5 @@
void onLaunchHceTapAgainActivity(in ApduServiceInfo service, in String category);
void onRoutingTableFull();
void onLogEventNotified(in OemLogItems item);
+ void onExtractOemPackages(in NdefMessage message, in ResultReceiver packageReceiver);
}
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index fd131b8..326ca64 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -194,6 +194,30 @@
public @interface StatusCode {}
/**
+ * Routing commit succeeded.
+ */
+ public static final int COMMIT_ROUTING_STATUS_OK = 0;
+ /**
+ * Routing commit failed.
+ */
+ public static final int COMMIT_ROUTING_STATUS_FAILED = 3;
+ /**
+ * Routing commit failed due to the update is in progress.
+ */
+ public static final int COMMIT_ROUTING_STATUS_FAILED_UPDATE_IN_PROGRESS = 6;
+
+ /**
+ * Status codes returned when calling {@link #forceRoutingTableCommit()}
+ * @hide
+ */
+ @IntDef(prefix = "COMMIT_ROUTING_STATUS_", value = {
+ COMMIT_ROUTING_STATUS_OK,
+ COMMIT_ROUTING_STATUS_FAILED,
+ COMMIT_ROUTING_STATUS_FAILED_UPDATE_IN_PROGRESS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CommitRoutingStatusCode {}
+ /**
* Interface for Oem extensions for NFC.
*/
public interface Callback {
@@ -286,8 +310,12 @@
/**
* Notifies routing configuration is changed.
+ * @param isCommitRoutingSkipped The {@link Consumer} to be
+ * completed. If routing commit should be skipped,
+ * the {@link Consumer#accept(Object)} should be called with
+ * {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}.
*/
- void onRoutingChanged();
+ void onRoutingChanged(@NonNull Consumer<Boolean> isCommitRoutingSkipped);
/**
* API to activate start stop cpu boost on hce event.
@@ -404,6 +432,19 @@
* @param item the log items that contains log information of NFC event.
*/
void onLogEventNotified(@NonNull OemLogItems item);
+
+ /**
+ * Callback to to extract OEM defined packages from given NDEF message when
+ * a NFC tag is detected. These are used to handle NFC tags encoded with a
+ * proprietary format for storing app name (Android native app format).
+ *
+ * @param message NDEF message containing OEM package names
+ * @param packageConsumer The {@link Consumer} to be completed.
+ * The {@link Consumer#accept(Object)} should be called with
+ * the list of package names.
+ */
+ void onExtractOemPackages(@NonNull NdefMessage message,
+ @NonNull Consumer<List<String>> packageConsumer);
}
@@ -740,6 +781,18 @@
return result;
}
+ /**
+ * API to force a routing table commit.
+ * @return a {@link StatusCode} to indicate if commit routing succeeded or not
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ @CommitRoutingStatusCode
+ public int forceRoutingTableCommit() {
+ return NfcAdapter.callServiceReturn(
+ () -> NfcAdapter.sService.commitRouting(), COMMIT_ROUTING_STATUS_FAILED);
+ }
+
private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
@Override
@@ -843,9 +896,10 @@
new ReceiverWrapper<>(isSkipped), cb::onTagDispatch, ex));
}
@Override
- public void onRoutingChanged() throws RemoteException {
+ public void onRoutingChanged(ResultReceiver isSkipped) throws RemoteException {
mCallbackMap.forEach((cb, ex) ->
- handleVoidCallback(null, (Object input) -> cb.onRoutingChanged(), ex));
+ handleVoidCallback(
+ new ReceiverWrapper<>(isSkipped), cb::onRoutingChanged, ex));
}
@Override
public void onHceEventReceived(int action) throws RemoteException {
@@ -924,6 +978,15 @@
handleVoidCallback(item, cb::onLogEventNotified, ex));
}
+ @Override
+ public void onExtractOemPackages(NdefMessage message, ResultReceiver packageConsumer)
+ throws RemoteException {
+ mCallbackMap.forEach((cb, ex) ->
+ handleVoid2ArgCallback(message,
+ new ReceiverWrapper<>(packageConsumer),
+ cb::onExtractOemPackages, ex));
+ }
+
private <T> void handleVoidCallback(
T input, Consumer<T> callbackMethod, Executor executor) {
synchronized (mLock) {
@@ -1034,8 +1097,14 @@
Bundle bundle = new Bundle();
bundle.putParcelable("intent", (Intent) result);
mResultReceiver.send(0, bundle);
+ } else if (result instanceof List<?> list) {
+ if (list.stream().allMatch(String.class::isInstance)) {
+ Bundle bundle = new Bundle();
+ bundle.putStringArray("packageNames",
+ list.stream().map(pkg -> (String) pkg).toArray(String[]::new));
+ mResultReceiver.send(0, bundle);
+ }
}
-
}
@Override
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a4b8821..123f823 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1748,6 +1748,13 @@
}
flag {
+ name: "notification_shade_blur"
+ namespace: "systemui"
+ description: "Enables the new blur effect on the Notification Shade."
+ bug: "370555223"
+}
+
+flag {
name: "ensure_enr_views_visibility"
namespace: "systemui"
description: "Ensures public and private visibilities"
@@ -1790,4 +1797,4 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 13ac321..f3d5139 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -113,34 +113,38 @@
}
private val showIcon =
- canShowIcon
- .flatMapLatest { canShow ->
- if (!canShow) {
- flowOf(false)
- } else {
- combine(
- shouldShowIconForOosAfterHysteresis,
- interactor.connectionState,
- interactor.isWifiActive,
- airplaneModeRepository.isAirplaneMode,
- ) { showForOos, connectionState, isWifiActive, isAirplaneMode ->
- if (isWifiActive || isAirplaneMode) {
- false
- } else {
- showForOos ||
- connectionState == SatelliteConnectionState.On ||
- connectionState == SatelliteConnectionState.Connected
+ if (interactor.isOpportunisticSatelliteIconEnabled) {
+ canShowIcon
+ .flatMapLatest { canShow ->
+ if (!canShow) {
+ flowOf(false)
+ } else {
+ combine(
+ shouldShowIconForOosAfterHysteresis,
+ interactor.connectionState,
+ interactor.isWifiActive,
+ airplaneModeRepository.isAirplaneMode,
+ ) { showForOos, connectionState, isWifiActive, isAirplaneMode ->
+ if (isWifiActive || isAirplaneMode) {
+ false
+ } else {
+ showForOos ||
+ connectionState == SatelliteConnectionState.On ||
+ connectionState == SatelliteConnectionState.Connected
+ }
}
}
}
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLog,
+ columnPrefix = "vm",
+ columnName = COL_VISIBLE,
+ initialValue = false,
+ )
+ } else {
+ flowOf(false)
}
- .distinctUntilChanged()
- .logDiffsForTable(
- tableLog,
- columnPrefix = "vm",
- columnName = COL_VISIBLE,
- initialValue = false,
- )
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val icon: StateFlow<Icon?> =
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index d53f949..fcb7934 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -60,3 +60,12 @@
bug: "331749778"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_restricted_mode_changes"
+ namespace: "onboarding"
+ description: "Enables the new framework behavior of not putting apps in restricted mode for "
+ "B&R operations in certain cases."
+ bug: "376661510"
+ is_fixed_read_only: true
+}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 466d477..5de2fb3 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -43,6 +43,7 @@
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AppGlobals;
+import android.app.ApplicationThreadConstants;
import android.app.IActivityManager;
import android.app.IBackupAgent;
import android.app.PendingIntent;
@@ -59,6 +60,9 @@
import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreSession;
import android.app.backup.ISelectBackupTransportCallback;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -298,6 +302,15 @@
private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
private static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+ /**
+ * Enables the OS making a decision on whether backup restricted mode should be used for apps
+ * that haven't explicitly opted in or out. See
+ * {@link PackageManager#PROPERTY_USE_RESTRICTED_BACKUP_MODE} for details.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
+ public static final long OS_DECIDES_BACKUP_RESTRICTED_MODE = 376661510;
+
// Time delay for initialization operations that can be delayed so as not to consume too much
// CPU on bring-up and increase time-to-UI.
private static final long INITIALIZATION_DELAY_MILLIS = 3000;
@@ -352,6 +365,9 @@
// Backups that we haven't started yet. Keys are package names.
private final HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
+ private final ArraySet<String> mRestoreNoRestrictedModePackages = new ArraySet<>();
+ private final ArraySet<String> mBackupNoRestrictedModePackages = new ArraySet<>();
+
// locking around the pending-backup management
private final Object mQueueLock = new Object();
@@ -523,7 +539,8 @@
@VisibleForTesting
UserBackupManagerService(Context context, PackageManager packageManager,
LifecycleOperationStorage operationStorage, TransportManager transportManager,
- BackupHandler backupHandler, BackupManagerConstants backupManagerConstants) {
+ BackupHandler backupHandler, BackupManagerConstants backupManagerConstants,
+ IActivityManager activityManager, ActivityManagerInternal activityManagerInternal) {
mContext = context;
mUserId = 0;
@@ -534,6 +551,8 @@
mFullBackupQueue = new ArrayList<>();
mBackupHandler = backupHandler;
mConstants = backupManagerConstants;
+ mActivityManager = activityManager;
+ mActivityManagerInternal = activityManagerInternal;
mBaseStateDir = null;
mDataDir = null;
@@ -543,13 +562,11 @@
mRunInitReceiver = null;
mRunInitIntent = null;
mAgentTimeoutParameters = null;
- mActivityManagerInternal = null;
mAlarmManager = null;
mWakelock = null;
mBackupPreferences = null;
mBackupPasswordManager = null;
mPackageManagerBinder = null;
- mActivityManager = null;
mBackupManagerBinder = null;
mScheduledBackupEligibility = null;
}
@@ -1651,9 +1668,11 @@
synchronized (mAgentConnectLock) {
mConnecting = true;
mConnectedAgent = null;
+ boolean useRestrictedMode = shouldUseRestrictedBackupModeForPackage(mode,
+ app.packageName);
try {
if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId,
- backupDestination)) {
+ backupDestination, useRestrictedMode)) {
Slog.d(TAG, addUserIdToLogMessage(mUserId, "awaiting agent for " + app));
// success; wait for the agent to arrive
@@ -3103,6 +3122,91 @@
}
}
+ /**
+ * Marks the given set of packages as packages that should not be put into restricted mode if
+ * they are started for the given {@link BackupAnnotations.OperationType}.
+ */
+ public void setNoRestrictedModePackages(Set<String> packageNames,
+ @BackupAnnotations.OperationType int opType) {
+ if (opType == BackupAnnotations.OperationType.BACKUP) {
+ mBackupNoRestrictedModePackages.clear();
+ mBackupNoRestrictedModePackages.addAll(packageNames);
+ } else if (opType == BackupAnnotations.OperationType.RESTORE) {
+ mRestoreNoRestrictedModePackages.clear();
+ mRestoreNoRestrictedModePackages.addAll(packageNames);
+ } else {
+ throw new IllegalArgumentException("opType must be BACKUP or RESTORE");
+ }
+ }
+
+ /**
+ * Clears the list of packages that should not be put into restricted mode for either backup or
+ * restore.
+ */
+ public void clearNoRestrictedModePackages() {
+ mBackupNoRestrictedModePackages.clear();
+ mRestoreNoRestrictedModePackages.clear();
+ }
+
+ /**
+ * If the app has specified {@link PackageManager#PROPERTY_USE_RESTRICTED_BACKUP_MODE}, then
+ * its value is returned. If it hasn't and it targets an SDK below
+ * {@link Build.VERSION_CODES#BAKLAVA} then returns true. If it targets a newer SDK, then
+ * returns the decision made by the {@link android.app.backup.BackupTransport}.
+ *
+ * <p>When this method is called, we should have already asked the transport and cached its
+ * response in {@link #mBackupNoRestrictedModePackages} or
+ * {@link #mRestoreNoRestrictedModePackages} so this method will immediately return without
+ * any IPC to the transport.
+ */
+ private boolean shouldUseRestrictedBackupModeForPackage(
+ @BackupAnnotations.OperationType int mode, String packageName) {
+ if (!Flags.enableRestrictedModeChanges()) {
+ return true;
+ }
+
+ // Key/Value apps are never put in restricted mode.
+ if (mode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
+ || mode == ApplicationThreadConstants.BACKUP_MODE_RESTORE) {
+ return false;
+ }
+
+ try {
+ PackageManager.Property property = mPackageManager.getPropertyAsUser(
+ PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE,
+ packageName, /* className= */ null,
+ mUserId);
+ if (property.isBoolean()) {
+ // If the package has explicitly specified, we won't ask the transport.
+ return property.getBoolean();
+ } else {
+ Slog.w(TAG, PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE
+ + "must be a boolean.");
+ }
+ } catch (NameNotFoundException e) {
+ // This is expected when the package has not defined the property in its manifest.
+ }
+
+ // The package has not specified the property. The behavior depends on the package's
+ // targetSdk.
+ // <36 gets the old behavior of always using restricted mode.
+ if (!CompatChanges.isChangeEnabled(OS_DECIDES_BACKUP_RESTRICTED_MODE, packageName,
+ UserHandle.of(mUserId))) {
+ return true;
+ }
+
+ // Apps targeting >=36 get the behavior decided by the transport.
+ // By this point, we should have asked the transport and cached its decision.
+ if ((mode == ApplicationThreadConstants.BACKUP_MODE_FULL
+ && mBackupNoRestrictedModePackages.contains(packageName))
+ || (mode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL
+ && mRestoreNoRestrictedModePackages.contains(packageName))) {
+ Slog.d(TAG, "Transport requested no restricted mode for: " + packageName);
+ return false;
+ }
+ return true;
+ }
+
private boolean startConfirmationUi(int token, String action) {
try {
Intent confIntent = new Intent(action);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index cca166b..be9cdc8 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -16,6 +16,8 @@
package com.android.server.backup.fullbackup;
+import static android.app.backup.BackupAnnotations.OperationType.BACKUP;
+
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
@@ -34,6 +36,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -388,6 +391,10 @@
}
}
+ // We ask the transport which packages should not be put in restricted mode and cache
+ // the result in UBMS to be used later when the apps are started for backup.
+ setNoRestrictedModePackages(transport, mPackages);
+
// Set up to send data to the transport
final int N = mPackages.size();
int chunkSizeInBytes = 8 * 1024; // 8KB
@@ -694,6 +701,9 @@
mUserBackupManagerService.scheduleNextFullBackupJob(backoff);
}
+ // Clear this to avoid using the memory until reboot.
+ mUserBackupManagerService.clearNoRestrictedModePackages();
+
Slog.i(TAG, "Full data backup pass finished.");
mUserBackupManagerService.getWakelock().release();
}
@@ -722,6 +732,21 @@
}
}
+ private void setNoRestrictedModePackages(BackupTransportClient transport,
+ List<PackageInfo> packages) {
+ try {
+ Set<String> packageNames = new ArraySet<>();
+ for (int i = 0; i < packages.size(); i++) {
+ packageNames.add(packages.get(i).packageName);
+ }
+ packageNames = transport.getPackagesThatShouldNotUseRestrictedMode(packageNames,
+ BACKUP);
+ mUserBackupManagerService.setNoRestrictedModePackages(packageNames, BACKUP);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Failed to retrieve no restricted mode packages from transport");
+ }
+ }
+
// Run the backup and pipe it back to the given socket -- expects to run on
// a standalone thread. The runner owns this half of the pipe, and closes
// it to indicate EOD to the other end.
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index e536876..5ee51a5 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -53,6 +53,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
@@ -482,6 +483,10 @@
return;
}
+ // We ask the transport which packages should not be put in restricted mode and cache
+ // the result in UBMS to be used later when the apps are started for restore.
+ setNoRestrictedModePackages(transport, packages);
+
RestoreDescription desc = transport.nextRestorePackage();
if (desc == null) {
Slog.e(TAG, "No restore metadata available; halting");
@@ -1358,6 +1363,9 @@
// Clear any ongoing session timeout.
backupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
+ // Clear this to avoid using the memory until reboot.
+ backupManagerService.clearNoRestrictedModePackages();
+
// If we have a PM token, we must under all circumstances be sure to
// handshake when we've finished.
if (mPmToken > 0) {
@@ -1819,4 +1827,20 @@
return packageInfo;
}
+
+ @VisibleForTesting
+ void setNoRestrictedModePackages(BackupTransportClient transport,
+ PackageInfo[] packages) {
+ try {
+ Set<String> packageNames = new ArraySet<>();
+ for (int i = 0; i < packages.length; i++) {
+ packageNames.add(packages[i].packageName);
+ }
+ packageNames = transport.getPackagesThatShouldNotUseRestrictedMode(packageNames,
+ RESTORE);
+ backupManagerService.setNoRestrictedModePackages(packageNames, RESTORE);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Failed to retrieve restricted mode packages from transport");
+ }
+ }
}
diff --git a/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java b/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
index daf34152..373811f 100644
--- a/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/BackupTransportClient.java
@@ -17,6 +17,7 @@
package com.android.server.backup.transport;
import android.annotation.Nullable;
+import android.app.backup.BackupAnnotations;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.RestoreDescription;
@@ -26,6 +27,7 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.backup.IBackupTransport;
@@ -375,6 +377,26 @@
}
/**
+ * See
+ * {@link IBackupTransport#getPackagesThatShouldNotUseRestrictedMode(List, int, AndroidFuture)}.
+ */
+ public Set<String> getPackagesThatShouldNotUseRestrictedMode(Set<String> packageNames,
+ @BackupAnnotations.OperationType
+ int operationType) throws RemoteException {
+ AndroidFuture<List<String>> resultFuture = mTransportFutures.newFuture();
+ mTransportBinder.getPackagesThatShouldNotUseRestrictedMode(List.copyOf(packageNames),
+ operationType,
+ resultFuture);
+ List<String> resultList = getFutureResult(resultFuture);
+ Set<String> set = new ArraySet<>();
+ if (resultList == null) {
+ return set;
+ }
+ set.addAll(resultList);
+ return set;
+ }
+
+ /**
* Allows the {@link TransportConnection} to notify this client
* if the underlying transport has become unusable. If that happens
* we want to cancel all active futures or callbacks.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dfddc08..d3e5942 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -478,7 +478,6 @@
import dalvik.annotation.optimization.NeverCompile;
import dalvik.system.VMRuntime;
-
import libcore.util.EmptyArray;
import java.io.File;
@@ -4493,16 +4492,11 @@
Slog.w(TAG, "Unattached app died before backup, skipping");
final int userId = app.userId;
final String packageName = app.info.packageName;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentDisconnectedForUser(userId, packageName);
- } catch (RemoteException e) {
- // Can't happen; the backup manager is local
- }
+ mHandler.post(() -> {
+ try {
+ getBackupManager().agentDisconnectedForUser(userId, packageName);
+ } catch (RemoteException e) {
+ // Can't happen; the backup manager is local
}
});
}
@@ -4673,7 +4667,8 @@
if (backupTarget != null && backupTarget.appInfo.packageName.equals(processName)) {
isRestrictedBackupMode = backupTarget.appInfo.uid >= FIRST_APPLICATION_UID
&& ((backupTarget.backupMode == BackupRecord.RESTORE_FULL)
- || (backupTarget.backupMode == BackupRecord.BACKUP_FULL));
+ || (backupTarget.backupMode == BackupRecord.BACKUP_FULL))
+ && backupTarget.useRestrictedMode;
}
final ActiveInstrumentation instr = app.getActiveInstrumentation();
@@ -13499,16 +13494,11 @@
if (backupTarget != null && pid == backupTarget.app.getPid()) {
if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
+ backupTarget.appInfo + " died during backup");
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentDisconnectedForUser(app.userId, app.info.packageName);
- } catch (RemoteException e) {
- // can't happen; backup manager is local
- }
+ mHandler.post(() -> {
+ try {
+ getBackupManager().agentDisconnectedForUser(app.userId, app.info.packageName);
+ } catch (RemoteException e) {
+ // can't happen; backup manager is local
}
});
}
@@ -14011,7 +14001,7 @@
// instantiated. The backup agent will invoke backupAgentCreated() on the
// activity manager to announce its creation.
public boolean bindBackupAgent(String packageName, int backupMode, int targetUserId,
- @BackupDestination int backupDestination) {
+ @BackupDestination int backupDestination, boolean useRestrictedMode) {
long startTimeNs = SystemClock.uptimeNanos();
if (DEBUG_BACKUP) {
Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode
@@ -14096,7 +14086,8 @@
+ app.packageName + ": " + e);
}
- BackupRecord r = new BackupRecord(app, backupMode, targetUserId, backupDestination);
+ BackupRecord r = new BackupRecord(app, backupMode, targetUserId, backupDestination,
+ useRestrictedMode);
ComponentName hostingName =
(backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
|| backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE)
@@ -14122,8 +14113,9 @@
// process, etc, then mark it as being in full backup so that certain calls to the
// process can be blocked. This is not reset to false anywhere because we kill the
// process after the full backup is done and the ProcessRecord will vaporize anyway.
- if (UserHandle.isApp(app.uid) &&
- backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL) {
+ if (UserHandle.isApp(app.uid)
+ && backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
+ && r.useRestrictedMode) {
proc.setInFullBackup(true);
}
r.app = proc;
@@ -14221,9 +14213,7 @@
final long oldIdent = Binder.clearCallingIdentity();
try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentConnectedForUser(userId, agentPackageName, agent);
+ getBackupManager().agentConnectedForUser(userId, agentPackageName, agent);
} catch (RemoteException e) {
// can't happen; the backup manager service is local
} catch (Exception e) {
@@ -19353,4 +19343,8 @@
}
return token;
}
+
+ private IBackupManager getBackupManager() {
+ return IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
+ }
}
diff --git a/services/core/java/com/android/server/am/BackupRecord.java b/services/core/java/com/android/server/am/BackupRecord.java
index 0b056d7..64cc6f0 100644
--- a/services/core/java/com/android/server/am/BackupRecord.java
+++ b/services/core/java/com/android/server/am/BackupRecord.java
@@ -32,15 +32,18 @@
final int userId; // user for which backup is performed
final int backupMode; // full backup / incremental / restore
@BackupDestination final int backupDestination; // see BackupAnnotations#BackupDestination
+ final boolean useRestrictedMode; // whether the app should be put into restricted backup mode
ProcessRecord app; // where this agent is running or null
// ----- Implementation -----
- BackupRecord(ApplicationInfo _appInfo, int _backupMode, int _userId, int _backupDestination) {
+ BackupRecord(ApplicationInfo _appInfo, int _backupMode, int _userId, int _backupDestination,
+ boolean _useRestrictedMode) {
appInfo = _appInfo;
backupMode = _backupMode;
userId = _userId;
backupDestination = _backupDestination;
+ useRestrictedMode = _useRestrictedMode;
}
public String toString() {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b51db13..98f738c 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -351,7 +351,8 @@
private String[] mIsolatedEntryPointArgs;
/**
- * Process is currently hosting a backup agent for backup or restore.
+ * Process is currently hosting a backup agent for backup or restore. Note that this is only set
+ * when the process is put into restricted backup mode.
*/
@GuardedBy("mService")
private boolean mInFullBackup;
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index 3644974..14d3fbc 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -393,6 +393,8 @@
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
} else if (adj < ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
adj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
+ } else if (Flags.addModifyRawOomAdjServiceLevel() && adj < ProcessList.SERVICE_ADJ) {
+ adj = ProcessList.SERVICE_ADJ;
} else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
adj = ProcessList.CACHED_APP_MIN_ADJ;
} else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 711b163..c59c40f 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -260,3 +260,13 @@
description: "Use PROCESS_CAPABILITY_CPU_TIME to control unfreeze state."
bug: "370817323"
}
+
+flag {
+ name: "add_modify_raw_oom_adj_service_level"
+ namespace: "backstage_power"
+ description: "Add a SERVICE_ADJ level to the modifyRawOomAdj method"
+ bug: "374810368"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6ba3569..5f71660 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -9224,6 +9224,9 @@
return;
}
+ // index values sent to APM are in the stream type SDK range, not *10
+ int indexMinVolCurve = MIN_STREAM_VOLUME[mStreamType];
+ int indexMaxVolCurve = MAX_STREAM_VOLUME[mStreamType];
synchronized (this) {
if (mStreamType == AudioSystem.STREAM_VOICE_CALL) {
if (MAX_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO]
@@ -9234,11 +9237,15 @@
if (!equalScoLeaVcIndexRange() && isStreamBluetoothSco(mStreamType)) {
// SCO devices have a different min index
mIndexMin = MIN_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO] * 10;
+ indexMinVolCurve = MIN_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO];
+ indexMaxVolCurve = MAX_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO];
mIndexStepFactor = 1.f;
} else if (equalScoLeaVcIndexRange() && isStreamBluetoothComm(mStreamType)) {
// For non SCO devices the stream state does not change the min index
if (mBtCommDeviceActive.get() == BT_COMM_DEVICE_ACTIVE_SCO) {
mIndexMin = MIN_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO] * 10;
+ indexMinVolCurve = MIN_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO];
+ indexMaxVolCurve = MAX_STREAM_VOLUME[AudioSystem.STREAM_BLUETOOTH_SCO];
} else {
mIndexMin = MIN_STREAM_VOLUME[mStreamType] * 10;
}
@@ -9259,7 +9266,7 @@
}
final int status = AudioSystem.initStreamVolume(
- mStreamType, mIndexMin / 10, mIndexMax / 10);
+ mStreamType, indexMinVolCurve, indexMaxVolCurve);
sVolumeLogger.enqueue(new EventLogger.StringEvent(
"updateIndexFactors() stream:" + mStreamType + " index min/max:"
+ mIndexMin / 10 + "/" + mIndexMax / 10 + " indexStepFactor:"
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index b63b07f..e92b518 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -20,6 +20,7 @@
import static android.media.AudioPlaybackConfiguration.MUTED_BY_APP_OPS;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_CLIENT_VOLUME;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_MASTER;
+import static android.media.AudioPlaybackConfiguration.MUTED_BY_PORT_VOLUME;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_STREAM_MUTED;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_STREAM_VOLUME;
import static android.media.AudioPlaybackConfiguration.MUTED_BY_VOLUME_SHAPER;
@@ -444,7 +445,7 @@
}
if (DEBUG) {
- Log.v(TAG, TextUtils.formatSimple("BLA portEvent(portId=%d, event=%s, extras=%s)",
+ Log.v(TAG, TextUtils.formatSimple("portEvent(portId=%d, event=%s, extras=%s)",
portId, AudioPlaybackConfiguration.playerStateToString(event), extras));
}
@@ -1381,6 +1382,9 @@
if ((mEventValue & MUTED_BY_VOLUME_SHAPER) != 0) {
builder.append("volumeShaper ");
}
+ if ((mEventValue & MUTED_BY_PORT_VOLUME) != 0) {
+ builder.append("portVolume ");
+ }
}
return builder.toString();
default:
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 586d594..e7ea868 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -4,6 +4,14 @@
# Important: Flags must be accessed through DisplayManagerFlags.
flag {
+ name: "is_always_on_available_api"
+ namespace: "display_manager"
+ description: "Allows querying of AOD availability"
+ bug: "324046664"
+ is_fixed_read_only: true
+}
+
+flag {
name: "enable_port_in_display_layout"
namespace: "display_manager"
description: "Allows refering to displays by port in display layout"
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index c5c8a5e..1f8a200 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -262,7 +262,7 @@
// TODO: implement
}
@Override
- public SoundProfile getSoundProfileById(String id) {
+ public SoundProfile getSoundProfile(int type, String id) {
return null;
}
@Override
@@ -313,6 +313,15 @@
}
@Override
+ public List<String> getSoundProfileAllowList() {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public void setSoundProfileAllowList(List<String> packages) {
+ }
+
+ @Override
public boolean isSupported() {
return false;
}
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 9f4b9f1..6d54be8 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -58,6 +58,7 @@
import android.os.storage.VolumeInfo;
import android.provider.DeviceConfig;
import android.stats.storage.StorageEnums;
+import android.text.TextUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -355,7 +356,8 @@
@Nullable int[] userIds,
@Nullable int[] instantUserIds,
@Nullable SparseArray<int[]> broadcastAllowList,
- @NonNull AndroidPackage pkg) {
+ @NonNull AndroidPackage pkg,
+ @NonNull String[] sharedUidPackages) {
final boolean isForWholeApp = componentNames.contains(packageName);
if (isForWholeApp || !android.content.pm.Flags.reduceBroadcastsForComponentStateChanges()) {
sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames,
@@ -374,20 +376,36 @@
exportedComponentNames.removeAll(notExportedComponentNames);
if (!notExportedComponentNames.isEmpty()) {
- // Limit sending of the PACKAGE_CHANGED broadcast to only the system and the
- // application itself when the component is not exported.
+ // Limit sending of the PACKAGE_CHANGED broadcast to only the system, the application
+ // itself and applications with the same UID when the component is not exported.
// First, send the PACKAGE_CHANGED broadcast to the system.
- sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
- notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
- broadcastAllowList, "android" /* targetPackageName */,
- new String[]{PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED});
+ if (!TextUtils.equals(packageName, "android")) {
+ sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
+ notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
+ broadcastAllowList, "android" /* targetPackageName */,
+ new String[]{
+ PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED});
+ }
// Second, send the PACKAGE_CHANGED broadcast to the application itself.
sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
broadcastAllowList, packageName /* targetPackageName */,
null /* requiredPermissions */);
+
+ // Third, send the PACKAGE_CHANGED broadcast to the applications with the same UID.
+ for (int i = 0; i < sharedUidPackages.length; i++) {
+ final String sharedPackage = sharedUidPackages[i];
+ if (TextUtils.equals(packageName, sharedPackage)) {
+ continue;
+ }
+ sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
+ notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
+ broadcastAllowList, sharedPackage /* targetPackageName */,
+ null /* requiredPermissions */);
+ }
+
}
if (!exportedComponentNames.isEmpty()) {
@@ -936,7 +954,8 @@
isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
mHandler.post(() -> sendPackageChangedBroadcastInternal(
packageName, dontKillApp, componentNames, packageUid, reason, userIds,
- instantUserIds, broadcastAllowList, setting.getPkg()));
+ instantUserIds, broadcastAllowList, setting.getPkg(),
+ snapshot.getSharedUserPackagesForPackage(packageName, userId)));
mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler);
}
diff --git a/services/core/java/com/android/server/pm/InstallDependencyHelper.java b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
index 745665b..527d680 100644
--- a/services/core/java/com/android/server/pm/InstallDependencyHelper.java
+++ b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
@@ -17,51 +17,240 @@
package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
+import static android.os.Process.SYSTEM_UID;
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
import android.content.pm.SharedLibraryInfo;
+import android.content.pm.dependencyinstaller.DependencyInstallerCallback;
+import android.content.pm.dependencyinstaller.IDependencyInstallerCallback;
+import android.content.pm.dependencyinstaller.IDependencyInstallerService;
import android.content.pm.parsing.PackageLite;
+import android.os.Handler;
import android.os.OutcomeReceiver;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.infra.ServiceConnector;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
* Helper class to interact with SDK Dependency Installer service.
*/
public class InstallDependencyHelper {
- private final SharedLibrariesImpl mSharedLibraries;
+ private static final String TAG = InstallDependencyHelper.class.getSimpleName();
+ private static final boolean DEBUG = true;
+ private static final String ACTION_INSTALL_DEPENDENCY =
+ "android.intent.action.INSTALL_DEPENDENCY";
+ // The maximum amount of time to wait before the system unbinds from the verifier.
+ private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6);
+ private static final long REQUEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(1);
- InstallDependencyHelper(SharedLibrariesImpl sharedLibraries) {
+ private final SharedLibrariesImpl mSharedLibraries;
+ private final Context mContext;
+ private final Object mRemoteServiceLock = new Object();
+
+ @GuardedBy("mRemoteServiceLock")
+ private ServiceConnector<IDependencyInstallerService> mRemoteService = null;
+
+ InstallDependencyHelper(Context context, SharedLibrariesImpl sharedLibraries) {
+ mContext = context;
mSharedLibraries = sharedLibraries;
}
- void resolveLibraryDependenciesIfNeeded(PackageLite pkg,
- OutcomeReceiver<Void, PackageManagerException> callback) {
- final List<SharedLibraryInfo> missing;
+ void resolveLibraryDependenciesIfNeeded(PackageLite pkg, Computer snapshot, int userId,
+ Handler handler, OutcomeReceiver<Void, PackageManagerException> origCallback) {
+ CallOnceProxy callback = new CallOnceProxy(handler, origCallback);
try {
- missing = mSharedLibraries.collectMissingSharedLibraryInfos(pkg);
+ resolveLibraryDependenciesIfNeededInternal(pkg, snapshot, userId, handler, callback);
} catch (PackageManagerException e) {
callback.onError(e);
- return;
+ } catch (Exception e) {
+ onError(callback, e.getMessage());
}
+ }
+
+
+ private void resolveLibraryDependenciesIfNeededInternal(PackageLite pkg, Computer snapshot,
+ int userId, Handler handler, CallOnceProxy callback) throws PackageManagerException {
+ final List<SharedLibraryInfo> missing =
+ mSharedLibraries.collectMissingSharedLibraryInfos(pkg);
if (missing.isEmpty()) {
+ if (DEBUG) {
+ Slog.i(TAG, "No missing dependency for " + pkg);
+ }
// No need for dependency resolution. Move to installation directly.
callback.onResult(null);
return;
}
- try {
- bindToDependencyInstaller();
- } catch (Exception e) {
- PackageManagerException pe = new PackageManagerException(
- INSTALL_FAILED_MISSING_SHARED_LIBRARY, e.getMessage());
- callback.onError(pe);
+ if (!bindToDependencyInstallerIfNeeded(userId, handler, snapshot)) {
+ onError(callback, "Dependency Installer Service not found");
+ return;
+ }
+
+ IDependencyInstallerCallback serviceCallback = new IDependencyInstallerCallback.Stub() {
+ @Override
+ public void onAllDependenciesResolved(int[] sessionIds) throws RemoteException {
+ // TODO(b/372862145): Implement waiting for sessions to finish installation
+ callback.onResult(null);
+ }
+
+ @Override
+ public void onFailureToResolveAllDependencies() throws RemoteException {
+ onError(callback, "Failed to resolve all dependencies automatically");
+ }
+ };
+
+ boolean scheduleSuccess;
+ synchronized (mRemoteServiceLock) {
+ scheduleSuccess = mRemoteService.run(service -> {
+ service.onDependenciesRequired(missing,
+ new DependencyInstallerCallback(serviceCallback.asBinder()));
+ });
+ }
+ if (!scheduleSuccess) {
+ onError(callback, "Failed to schedule job on Dependency Installer Service");
}
}
- private void bindToDependencyInstaller() {
- throw new IllegalStateException("Failed to bind to Dependency Installer");
+ private void onError(CallOnceProxy callback, String msg) {
+ PackageManagerException pe = new PackageManagerException(
+ INSTALL_FAILED_MISSING_SHARED_LIBRARY, msg);
+ callback.onError(pe);
}
+ private boolean bindToDependencyInstallerIfNeeded(int userId, Handler handler,
+ Computer snapshot) {
+ synchronized (mRemoteServiceLock) {
+ if (mRemoteService != null) {
+ if (DEBUG) {
+ Slog.i(TAG, "DependencyInstallerService already bound");
+ }
+ return true;
+ }
+ }
+ Intent serviceIntent = new Intent(ACTION_INSTALL_DEPENDENCY);
+ // TODO(b/372862145): Use RoleManager to find the package name
+ List<ResolveInfo> resolvedIntents = snapshot.queryIntentServicesInternal(
+ serviceIntent, /*resolvedType=*/ null, /*flags=*/0,
+ userId, SYSTEM_UID, Process.INVALID_PID,
+ /*includeInstantApps*/ false, /*resolveForStart*/ false);
+
+ if (resolvedIntents.isEmpty()) {
+ return false;
+ }
+
+
+ ResolveInfo resolveInfo = resolvedIntents.getFirst();
+ ComponentName componentName = resolveInfo.getComponentInfo().getComponentName();
+ serviceIntent.setComponent(componentName);
+
+ ServiceConnector<IDependencyInstallerService> serviceConnector =
+ new ServiceConnector.Impl<IDependencyInstallerService>(mContext, serviceIntent,
+ Context.BIND_AUTO_CREATE, userId,
+ IDependencyInstallerService.Stub::asInterface) {
+ @Override
+ protected Handler getJobHandler() {
+ return handler;
+ }
+
+ @Override
+ protected long getRequestTimeoutMs() {
+ return REQUEST_TIMEOUT_MILLIS;
+ }
+
+ @Override
+ protected long getAutoDisconnectTimeoutMs() {
+ return UNBIND_TIMEOUT_MILLIS;
+ }
+ };
+
+
+ synchronized (mRemoteServiceLock) {
+ // Some other thread managed to connect to the service first
+ if (mRemoteService != null) {
+ return true;
+ }
+ mRemoteService = serviceConnector;
+ mRemoteService.setServiceLifecycleCallbacks(
+ new ServiceConnector.ServiceLifecycleCallbacks<>() {
+ @Override
+ public void onDisconnected(@NonNull IDependencyInstallerService service) {
+ Slog.w(TAG,
+ "DependencyInstallerService " + componentName + " is disconnected");
+ destroy();
+ }
+
+ @Override
+ public void onBinderDied() {
+ Slog.w(TAG, "DependencyInstallerService " + componentName + " has died");
+ destroy();
+ }
+
+ private void destroy() {
+ synchronized (mRemoteServiceLock) {
+ if (mRemoteService != null) {
+ mRemoteService.unbind();
+ mRemoteService = null;
+ }
+ }
+ }
+
+ });
+ AndroidFuture<IDependencyInstallerService> unusedFuture = mRemoteService.connect();
+ }
+ return true;
+ }
+
+ /**
+ * Ensure we call one of the outcomes only once, on the right handler.
+ *
+ * Repeated calls will be no-op.
+ */
+ private static class CallOnceProxy implements OutcomeReceiver<Void, PackageManagerException> {
+ private final Handler mHandler;
+ private final OutcomeReceiver<Void, PackageManagerException> mCallback;
+ @GuardedBy("this")
+ private boolean mCalled = false;
+
+ CallOnceProxy(Handler handler, OutcomeReceiver<Void, PackageManagerException> callback) {
+ mHandler = handler;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onResult(Void result) {
+ synchronized (this) {
+ if (!mCalled) {
+ mHandler.post(() -> {
+ mCallback.onResult(null);
+ });
+ mCalled = true;
+ }
+ }
+ }
+
+ @Override
+ public void onError(@NonNull PackageManagerException error) {
+ synchronized (this) {
+ if (!mCalled) {
+ mHandler.post(() -> {
+ mCallback.onError(error);
+ });
+ mCalled = true;
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index eb70748..9b44f93 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -347,7 +347,7 @@
synchronized (mVerificationPolicyPerUser) {
mVerificationPolicyPerUser.put(USER_SYSTEM, DEFAULT_VERIFICATION_POLICY);
}
- mInstallDependencyHelper = new InstallDependencyHelper(
+ mInstallDependencyHelper = new InstallDependencyHelper(mContext,
mPm.mInjector.getSharedLibrariesImpl());
LocalServices.getService(SystemServiceManager.class).startService(
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index e156b31..505b7e6 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3428,8 +3428,8 @@
private void resolveLibraryDependenciesIfNeeded() {
synchronized (mLock) {
- // TODO(b/372862145): Callback should be called on a handler passed as parameter
mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(mPackageLite,
+ mPm.snapshotComputer(), userId, mHandler,
new OutcomeReceiver<>() {
@Override
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index fc54f68..17d7a14 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -1017,10 +1017,11 @@
boolean isSdkOrStatic = libraryType.equals(LIBRARY_TYPE_SDK)
|| libraryType.equals(LIBRARY_TYPE_STATIC);
if (isSdkOrStatic && outMissingSharedLibraryInfos != null) {
- // TODO(b/372862145): Pass the CertDigest too
// If Dependency Installation is supported, try that instead of failing.
+ final List<String> libCertDigests = Arrays.asList(requiredCertDigests[i]);
SharedLibraryInfo missingLibrary = new SharedLibraryInfo(
- libName, libVersion, SharedLibraryInfo.TYPE_SDK_PACKAGE
+ libName, libVersion, SharedLibraryInfo.TYPE_SDK_PACKAGE,
+ libCertDigests
);
outMissingSharedLibraryInfos.add(missingLibrary);
} else {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
index 1be5cef..acd34e3 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java
@@ -28,6 +28,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -60,6 +61,7 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.List;
@AppModeFull
@AppModeNonSdkSandbox
@@ -124,7 +126,8 @@
@Test
public void changeNonExportedComponent_sendPackageChangedBroadcastToSystem_withPermission()
throws Exception {
- changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */);
+ changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
+ new String[0] /* sharedPackages */);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockActivityManagerInternal).broadcastIntentWithCallback(
@@ -140,7 +143,8 @@
@Test
public void changeNonExportedComponent_sendPackageChangedBroadcastToApplicationItself()
throws Exception {
- changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */);
+ changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
+ new String[0] /* sharedPackages */);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockActivityManagerInternal).broadcastIntentWithCallback(captor.capture(), eq(null),
@@ -150,9 +154,45 @@
assertThat(intent.getPackage()).isEqualTo(PACKAGE_CHANGED_TEST_PACKAGE_NAME);
}
+ @RequiresFlagsEnabled(FLAG_REDUCE_BROADCASTS_FOR_COMPONENT_STATE_CHANGES)
+ @Test
+ public void changeNonExportedComponent_sendPackageChangedBroadcastToSharedUserIdApplications()
+ throws Exception {
+ changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */,
+ new String[]{"shared.package"} /* sharedPackages */);
+
+ ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+ ArgumentCaptor<String[]> captorRequiredPermissions = ArgumentCaptor.forClass(
+ String[].class);
+ verify(mMockActivityManagerInternal, times(3)).broadcastIntentWithCallback(
+ captorIntent.capture(), eq(null), captorRequiredPermissions.capture(), anyInt(),
+ eq(null), eq(null), eq(null));
+ List<Intent> intents = captorIntent.getAllValues();
+ List<String[]> requiredPermissions = captorRequiredPermissions.getAllValues();
+ assertNotNull(intents);
+ assertThat(intents.size()).isEqualTo(3);
+
+ final Intent intent1 = intents.get(0);
+ final String[] requiredPermission1 = requiredPermissions.get(0);
+ assertThat(intent1.getPackage()).isEqualTo("android");
+ assertThat(requiredPermission1).isEqualTo(
+ new String[]{PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED});
+
+ final Intent intent2 = intents.get(1);
+ final String[] requiredPermission2 = requiredPermissions.get(1);
+ assertThat(intent2.getPackage()).isEqualTo(PACKAGE_CHANGED_TEST_PACKAGE_NAME);
+ assertThat(requiredPermission2).isNull();
+
+ final Intent intent3 = intents.get(2);
+ final String[] requiredPermission3 = requiredPermissions.get(2);
+ assertThat(intent3.getPackage()).isEqualTo("shared.package");
+ assertThat(requiredPermission3).isNull();
+ }
+
@Test
public void changeExportedComponent_sendPackageChangedBroadcastToAll() throws Exception {
- changeComponentAndSendPackageChangedBroadcast(true /* changeExportedComponent */);
+ changeComponentAndSendPackageChangedBroadcast(true /* changeExportedComponent */,
+ new String[0] /* sharedPackages */);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mMockActivityManagerInternal).broadcastIntentWithCallback(captor.capture(), eq(null),
@@ -162,11 +202,14 @@
assertNull(intent.getPackage());
}
- private void changeComponentAndSendPackageChangedBroadcast(boolean changeExportedComponent) {
+ private void changeComponentAndSendPackageChangedBroadcast(boolean changeExportedComponent,
+ String[] sharedPackages) {
when(mMockSnapshot.getPackageStateInternal(eq(PACKAGE_CHANGED_TEST_PACKAGE_NAME),
anyInt())).thenReturn(mMockPackageStateInternal);
when(mMockSnapshot.isInstantAppInternal(any(), anyInt(), anyInt())).thenReturn(false);
when(mMockSnapshot.getVisibilityAllowLists(any(), any())).thenReturn(null);
+ when(mMockSnapshot.getSharedUserPackagesForPackage(eq(PACKAGE_CHANGED_TEST_PACKAGE_NAME),
+ anyInt())).thenReturn(sharedPackages);
when(mMockPackageStateInternal.getPkg()).thenReturn(mMockAndroidPackageInternal);
when(mMockParsedActivity.getClassName()).thenReturn(
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index dcbc234..5a872ea 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -47,10 +47,8 @@
import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK;
import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE;
import static com.android.server.am.ProcessList.NETWORK_STATE_UNBLOCK;
-
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -80,6 +78,7 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.ApplicationThreadConstants;
import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.ForegroundServiceDelegationOptions;
@@ -87,6 +86,7 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.SyncNotedAppOp;
+import android.app.backup.BackupAnnotations;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -111,6 +111,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.permission.IPermissionManager;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -133,6 +134,7 @@
import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
import com.android.server.am.UidObserverController.ChangeRecord;
import com.android.server.appop.AppOpsService;
+import com.android.server.job.JobSchedulerInternal;
import com.android.server.notification.NotificationManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerService;
@@ -228,6 +230,7 @@
@Mock private PackageManagerInternal mPackageManagerInternal;
@Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal;
@Mock private NotificationManagerInternal mNotificationManagerInternal;
+ @Mock private JobSchedulerInternal mJobSchedulerInternal;
@Mock private ContentResolver mContentResolver;
private TestInjector mInjector;
@@ -249,6 +252,7 @@
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInternal);
LocalServices.addService(NotificationManagerInternal.class, mNotificationManagerInternal);
+ LocalServices.addService(JobSchedulerInternal.class, mJobSchedulerInternal);
doReturn(new ComponentName("", "")).when(mPackageManagerInternal)
.getSystemUiServiceComponent();
@@ -308,6 +312,7 @@
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
LocalServices.removeServiceForTest(NotificationManagerInternal.class);
+ LocalServices.removeServiceForTest(JobSchedulerInternal.class);
if (mMockingSession != null) {
mMockingSession.finishMocking();
@@ -1548,6 +1553,50 @@
eq(notificationId), anyInt());
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void bindBackupAgent_fullBackup_shouldUseRestrictedMode_setsInFullBackup()
+ throws Exception {
+ ActivityManagerService spyAms = spy(mAms);
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = TEST_PACKAGE;
+ applicationInfo.processName = TEST_PACKAGE;
+ applicationInfo.uid = TEST_UID;
+ doReturn(applicationInfo).when(mPackageManager).getApplicationInfo(eq(TEST_PACKAGE),
+ anyLong(), anyInt());
+ ProcessRecord appRec = new ProcessRecord(mAms, applicationInfo, TAG, TEST_UID);
+ doReturn(appRec).when(spyAms).getProcessRecordLocked(eq(TEST_PACKAGE), eq(TEST_UID));
+
+ spyAms.bindBackupAgent(TEST_PACKAGE, ApplicationThreadConstants.BACKUP_MODE_FULL,
+ UserHandle.USER_SYSTEM,
+ BackupAnnotations.BackupDestination.CLOUD, /* shouldUseRestrictedMode= */
+ true);
+
+ assertThat(appRec.isInFullBackup()).isTrue();
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void bindBackupAgent_fullBackup_shouldNotUseRestrictedMode_doesNotSetInFullBackup()
+ throws Exception {
+ ActivityManagerService spyAms = spy(mAms);
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = TEST_PACKAGE;
+ applicationInfo.processName = TEST_PACKAGE;
+ applicationInfo.uid = TEST_UID;
+ doReturn(applicationInfo).when(mPackageManager).getApplicationInfo(eq(TEST_PACKAGE),
+ anyLong(), anyInt());
+ ProcessRecord appRec = new ProcessRecord(mAms, applicationInfo, TAG, TEST_UID);
+ doReturn(appRec).when(spyAms).getProcessRecordLocked(eq(TEST_PACKAGE), eq(TEST_UID));
+
+ spyAms.bindBackupAgent(TEST_PACKAGE, ApplicationThreadConstants.BACKUP_MODE_FULL,
+ UserHandle.USER_SYSTEM,
+ BackupAnnotations.BackupDestination.CLOUD, /* shouldUseRestrictedMode= */
+ false);
+
+ assertThat(appRec.isInFullBackup()).isFalse();
+ }
+
private static class TestHandler extends Handler {
private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec
private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index f82a860..a9569b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -1427,6 +1427,31 @@
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_DoOne_Service_NotPerceptible_AboveClient() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ ProcessRecord service = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+ MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+ bindService(app, client, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
+ bindService(service, app, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
+ mProcessStateController.setRunningRemoteAnimation(client, true);
+ mProcessStateController.updateHasAboveClientLocked(app.mServices);
+ setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ updateOomAdj(client, app, service);
+
+ final int expectedAdj;
+ if (Flags.addModifyRawOomAdjServiceLevel()) {
+ expectedAdj = SERVICE_ADJ;
+ } else {
+ expectedAdj = CACHED_APP_MIN_ADJ;
+ }
+ assertEquals(expectedAdj, app.mState.getSetAdj());
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoOne_Service_NotVisible() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
@@ -2906,7 +2931,7 @@
// Simulate binding to a service in the same process using BIND_ABOVE_CLIENT and
// verify that its OOM adjustment level is unaffected.
bindService(service, app, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
- app.mServices.updateHasAboveClientLocked();
+ mProcessStateController.updateHasAboveClientLocked(app.mServices);
assertTrue(app.mServices.hasAboveClient());
updateOomAdj(app);
@@ -2928,7 +2953,7 @@
// Simulate binding to a service in the same process using BIND_ABOVE_CLIENT and
// verify that its OOM adjustment level is unaffected.
bindService(app, app, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
- app.mServices.updateHasAboveClientLocked();
+ mProcessStateController.updateHasAboveClientLocked(app.mServices);
assertFalse(app.mServices.hasAboveClient());
updateOomAdj(app);
@@ -2983,7 +3008,7 @@
// Since sr.app is null, this service cannot be in the same process as the
// client so we expect the BIND_ABOVE_CLIENT adjustment to take effect.
- app.mServices.updateHasAboveClientLocked();
+ mProcessStateController.updateHasAboveClientLocked(app.mServices);
updateOomAdj(app);
assertTrue(app.mServices.hasAboveClient());
assertNotEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -3306,7 +3331,7 @@
if (Flags.pushGlobalStateToOomadjuster()) {
mProcessStateController.setBackupTarget(app, app.userId);
} else {
- BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
+ BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0, true);
backupTarget.app = app;
doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt());
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 65286d9..07f2188 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -18,9 +18,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -32,20 +30,27 @@
import static org.mockito.Mockito.when;
import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.app.ApplicationThreadConstants;
+import android.app.IActivityManager;
import android.app.backup.BackupAgent;
-import android.app.backup.BackupAnnotations;
import android.app.backup.BackupAnnotations.BackupDestination;
+import android.app.backup.BackupAnnotations.OperationType;
import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Handler;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.testing.TestableContext;
import android.util.FeatureFlagUtils;
@@ -68,7 +73,9 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -77,8 +84,12 @@
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
import java.util.function.IntConsumer;
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
@Presubmit
@RunWith(AndroidJUnit4.class)
public class UserBackupManagerServiceTest {
@@ -88,6 +99,11 @@
private static final int WORKER_THREAD_TIMEOUT_MILLISECONDS = 100;
@UserIdInt private static final int USER_ID = 0;
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock IBackupManagerMonitor mBackupManagerMonitor;
@Mock IBackupObserver mBackupObserver;
@Mock PackageManager mPackageManager;
@@ -99,10 +115,14 @@
@Mock JobScheduler mJobScheduler;
@Mock BackupHandler mBackupHandler;
@Mock BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
+ @Mock IActivityManager mActivityManager;
+ @Mock
+ ActivityManagerInternal mActivityManagerInternal;
private TestableContext mContext;
private MockitoSession mSession;
private TestBackupService mService;
+ private ApplicationInfo mTestPackageApplicationInfo;
@Before
public void setUp() throws Exception {
@@ -120,12 +140,14 @@
mContext.getTestablePermissions().setPermission(android.Manifest.permission.BACKUP,
PackageManager.PERMISSION_GRANTED);
- mService = new TestBackupService(mContext, mPackageManager, mOperationStorage,
- mTransportManager, mBackupHandler);
+ mService = new TestBackupService();
mService.setEnabled(true);
mService.setSetupComplete(true);
mService.enqueueFullBackup("com.test.backup.app", /* lastBackedUp= */ 0);
- }
+
+ mTestPackageApplicationInfo = new ApplicationInfo();
+ mTestPackageApplicationInfo.packageName = TEST_PACKAGE;
+ }
@After
public void tearDown() {
@@ -298,9 +320,160 @@
new DataTypeResult(/* dataType */ "type_2"));
mService.reportDelayedRestoreResult(TEST_PACKAGE, results);
-
verify(mBackupManagerMonitorEventSender).sendAgentLoggingResults(
- eq(packageInfo), eq(results), eq(BackupAnnotations.OperationType.RESTORE));
+ eq(packageInfo), eq(results), eq(OperationType.RESTORE));
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_restrictedModeChangesFlagOff_shouldUseRestrictedMode()
+ throws Exception {
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ // Make sure we never hit the code that checks the property.
+ verify(mPackageManager, never()).getPropertyAsUser(any(), any(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_keyValueBackup_shouldNotUseRestrictedMode()
+ throws Exception {
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ // Make sure we never hit the code that checks the property.
+ verify(mPackageManager, never()).getPropertyAsUser(any(), any(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_keyValueRestore_shouldNotUseRestrictedMode()
+ throws Exception {
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_RESTORE, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ // Make sure we never hit the code that checks the property.
+ verify(mPackageManager, never()).getPropertyAsUser(any(), any(), any(), anyInt());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_packageOptedIn_shouldUseRestrictedMode()
+ throws Exception {
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenReturn(new PackageManager.Property(
+ PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE, /* value= */ true,
+ TEST_PACKAGE, /* className= */ null));
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ public void bindToAgentSynchronous_packageOptedOut_shouldNotUseRestrictedMode()
+ throws Exception {
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenReturn(new PackageManager.Property(
+ PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE, /* value= */ false,
+ TEST_PACKAGE, /* className= */ null));
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @DisableCompatChanges({UserBackupManagerService.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_targetSdkBelowB_shouldUseRestrictedMode()
+ throws Exception {
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException()
+ );
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @EnableCompatChanges({UserBackupManagerService.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_targetSdkB_notInList_shouldUseRestrictedMode()
+ throws Exception {
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException()
+ );
+ mService.clearNoRestrictedModePackages();
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(true));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @EnableCompatChanges({UserBackupManagerService.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_forRestore_targetSdkB_inList_shouldNotUseRestrictedMode()
+ throws Exception {
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException()
+ );
+ mService.setNoRestrictedModePackages(Set.of(TEST_PACKAGE), OperationType.RESTORE);
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES)
+ @EnableCompatChanges({UserBackupManagerService.OS_DECIDES_BACKUP_RESTRICTED_MODE})
+ public void bindToAgentSynchronous_forBackup_targetSdkB_inList_shouldNotUseRestrictedMode()
+ throws Exception {
+ // Mock that the app has not explicitly set the property.
+ when(mPackageManager.getPropertyAsUser(
+ eq(PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE),
+ eq(TEST_PACKAGE), any(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException()
+ );
+ mService.setNoRestrictedModePackages(Set.of(TEST_PACKAGE), OperationType.BACKUP);
+
+ mService.bindToAgentSynchronous(mTestPackageApplicationInfo,
+ ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
+
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
}
private static PackageInfo getPackageInfo(String packageName) {
@@ -316,11 +489,9 @@
private volatile Thread mWorkerThread = null;
- TestBackupService(Context context, PackageManager packageManager,
- LifecycleOperationStorage operationStorage, TransportManager transportManager,
- BackupHandler backupHandler) {
- super(context, packageManager, operationStorage, transportManager, backupHandler,
- createConstants(context));
+ TestBackupService() {
+ super(mContext, mPackageManager, mOperationStorage, mTransportManager, mBackupHandler,
+ createConstants(mContext), mActivityManager, mActivityManagerInternal);
}
private static BackupManagerConstants createConstants(Context context) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
index 9474253..3310573 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/fullbackup/PerformFullTransportBackupTaskTest.java
@@ -18,34 +18,95 @@
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.when;
+import android.app.backup.BackupAnnotations;
+import android.app.backup.BackupTransport;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.transport.BackupTransportClient;
+import com.android.server.backup.transport.TransportConnection;
+import com.android.server.backup.utils.BackupEligibilityRules;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
@Presubmit
@RunWith(AndroidJUnit4.class)
public class PerformFullTransportBackupTaskTest {
+ private static final String TEST_PACKAGE_1 = "package1";
+ private static final String TEST_PACKAGE_2 = "package2";
+
+ @Mock
+ BackupAgentTimeoutParameters mBackupAgentTimeoutParameters;
+ @Mock
+ BackupEligibilityRules mBackupEligibilityRules;
@Mock
UserBackupManagerService mBackupManagerService;
@Mock
+ BackupTransportClient mBackupTransportClient;
+ @Mock
+ CountDownLatch mLatch;
+ @Mock
+ OperationStorage mOperationStorage;
+ @Mock
+ PackageManager mPackageManager;
+ @Mock
+ TransportConnection mTransportConnection;
+ @Mock
TransportManager mTransportManager;
+ @Mock
+ UserBackupManagerService.BackupWakeLock mWakeLock;
+
+ private final List<String> mEligiblePackages = new ArrayList<>();
+
+ private PerformFullTransportBackupTask mTask;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ when(mBackupManagerService.getPackageManager()).thenReturn(mPackageManager);
+ when(mBackupManagerService.getQueueLock()).thenReturn("something!");
+ when(mBackupManagerService.isEnabled()).thenReturn(true);
+ when(mBackupManagerService.getWakelock()).thenReturn(mWakeLock);
+ when(mBackupManagerService.isSetupComplete()).thenReturn(true);
+ when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(
+ mBackupAgentTimeoutParameters);
when(mBackupManagerService.getTransportManager()).thenReturn(mTransportManager);
+ when(mTransportManager.getCurrentTransportClient(any())).thenReturn(mTransportConnection);
+ when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransportClient);
+ when(mTransportConnection.connect(any())).thenReturn(mBackupTransportClient);
+ when(mBackupTransportClient.performFullBackup(any(), any(), anyInt())).thenReturn(
+ BackupTransport.TRANSPORT_ERROR);
+ when(mBackupEligibilityRules.appIsEligibleForBackup(
+ argThat(app -> mEligiblePackages.contains(app.packageName)))).thenReturn(
+ true);
+ when(mBackupEligibilityRules.appGetsFullBackup(
+ argThat(app -> mEligiblePackages.contains(app.packageName)))).thenReturn(
+ true);
}
@Test
@@ -70,4 +131,49 @@
/* backupEligibilityRules */ null);
});
}
+
+ @Test
+ public void run_setsAndClearsNoRestrictedModePackages() throws Exception {
+ mockPackageEligibleForFullBackup(TEST_PACKAGE_1);
+ mockPackageEligibleForFullBackup(TEST_PACKAGE_2);
+ createTask(new String[] {TEST_PACKAGE_1, TEST_PACKAGE_2});
+ when(mBackupTransportClient.getPackagesThatShouldNotUseRestrictedMode(any(),
+ anyInt())).thenReturn(Set.of("package1"));
+
+ mTask.run();
+
+ InOrder inOrder = inOrder(mBackupManagerService);
+ inOrder.verify(mBackupManagerService).setNoRestrictedModePackages(
+ eq(Set.of("package1")),
+ eq(BackupAnnotations.OperationType.BACKUP));
+ inOrder.verify(mBackupManagerService).clearNoRestrictedModePackages();
+ }
+
+ private void createTask(String[] packageNames) {
+ mTask = PerformFullTransportBackupTask
+ .newWithCurrentTransport(
+ mBackupManagerService,
+ mOperationStorage,
+ /* observer */ null,
+ /* whichPackages */ packageNames,
+ /* updateSchedule */ false,
+ /* runningJob */ null,
+ mLatch,
+ /* backupObserver */ null,
+ /* monitor */ null,
+ /* userInitiated */ false,
+ /* caller */ null,
+ mBackupEligibilityRules);
+ }
+
+ private void mockPackageEligibleForFullBackup(String packageName) throws Exception {
+ mEligiblePackages.add(packageName);
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = packageName;
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = packageName;
+ packageInfo.applicationInfo = appInfo;
+ when(mPackageManager.getPackageInfoAsUser(eq(packageName), anyInt(), anyInt())).thenReturn(
+ packageInfo);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
index 414532b..055adf6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -23,8 +23,10 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.backup.BackupAnnotations;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
@@ -91,6 +93,8 @@
private UserBackupManagerService mBackupManagerService;
@Mock
private TransportConnection mTransportConnection;
+ @Mock
+ private BackupTransportClient mBackupTransportClient;
private Set<String> mExcludedkeys = new HashSet<>();
private Map<String, String> mBackupData = new HashMap<>();
@@ -151,6 +155,23 @@
}
@Test
+ public void setNoRestrictedModePackages_callsTransportAndSetsValue() throws Exception {
+ PackageInfo packageInfo1 = new PackageInfo();
+ packageInfo1.packageName = "package1";
+ PackageInfo packageInfo2 = new PackageInfo();
+ packageInfo2.packageName = "package2";
+ when(mBackupTransportClient.getPackagesThatShouldNotUseRestrictedMode(any(),
+ anyInt())).thenReturn(Set.of("package1"));
+
+ mRestoreTask.setNoRestrictedModePackages(mBackupTransportClient,
+ new PackageInfo[]{packageInfo1, packageInfo2});
+
+ verify(mBackupManagerService).setNoRestrictedModePackages(
+ eq(Set.of("package1")),
+ eq(BackupAnnotations.OperationType.RESTORE));
+ }
+
+ @Test
public void testFilterExcludedKeys() throws Exception {
when(mBackupManagerService.getExcludedRestoreKeys(eq(PACKAGE_NAME)))
.thenReturn(mExcludedkeys);
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
index 2d7d46f..13e3207 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/transport/BackupTransportClientTest.java
@@ -19,7 +19,14 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import android.app.backup.BackupAnnotations.OperationType;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.RestoreDescription;
@@ -38,15 +45,31 @@
import com.android.internal.backup.ITransportStatusCallback;
import com.android.internal.infra.AndroidFuture;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.Set;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class BackupTransportClientTest {
+ @Mock
+ IBackupTransport mMockBackupTransport;
+
+ private BackupTransportClient mMockingTransportClient;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mMockingTransportClient = new BackupTransportClient(
+ mMockBackupTransport);
+ }
+
private static class TestFuturesFakeTransportBinder extends FakeTransportBinderBase {
public final Object mLock = new Object();
@@ -128,6 +151,70 @@
thread.join();
}
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_passesSetAsListToBinder()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(List.of("package1", "package2"));
+
+ mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of("package1", "package2"),
+ OperationType.BACKUP);
+
+ verify(mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(
+ argThat(list -> Set.copyOf(list).equals(Set.of("package1", "package2"))),
+ eq(OperationType.BACKUP), any());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_forRestore_callsBinderForRestore()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(null);
+
+ mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of(),
+ OperationType.RESTORE);
+
+ verify(mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(any(),
+ eq(OperationType.RESTORE), any());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_forBackup_callsBinderForBackup()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(null);
+
+ mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of(),
+ OperationType.BACKUP);
+
+ verify(mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(any(),
+ eq(OperationType.BACKUP), any());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_nullResult_returnsEmptySet()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(null);
+
+ Set<String> result = mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of(),
+ OperationType.BACKUP);
+
+ assertThat(result).isEqualTo(Set.of());
+ }
+
+ @Test
+ public void getPackagesThatShouldNotUseRestrictedMode_returnsResultAsSet()
+ throws Exception {
+ mockGetPackagesThatShouldNotUseRestrictedModeReturn(List.of("package1", "package2"));
+
+ Set<String> result = mMockingTransportClient.getPackagesThatShouldNotUseRestrictedMode(
+ Set.of("package1", "package2"),
+ OperationType.BACKUP);
+
+ assertThat(result).isEqualTo(Set.of("package1", "package2"));
+ }
+
private static class TestCallbacksFakeTransportBinder extends FakeTransportBinderBase {
public final Object mLock = new Object();
@@ -158,7 +245,6 @@
assertThat(status).isEqualTo(123);
}
-
@Test
public void testFinishBackup_completesLater_returnsStatus() throws Exception {
TestCallbacksFakeTransportBinder binder = new TestCallbacksFakeTransportBinder();
@@ -211,6 +297,14 @@
thread.join();
}
+ private void mockGetPackagesThatShouldNotUseRestrictedModeReturn(List<String> returnList)
+ throws Exception {
+ doAnswer(
+ i -> ((AndroidFuture<List<String>>) i.getArguments()[2]).complete(returnList)).when(
+ mMockBackupTransport).getPackagesThatShouldNotUseRestrictedMode(any(), anyInt(),
+ any());
+ }
+
// Convenience layer so we only need to fake specific methods useful for each test case.
private static class FakeTransportBinderBase implements IBackupTransport {
@Override public void name(AndroidFuture<String> f) throws RemoteException {}
@@ -258,6 +352,10 @@
@Override
public void getBackupManagerMonitor(AndroidFuture<IBackupManagerMonitor> resultFuture)
throws RemoteException {}
+ @Override
+ public void getPackagesThatShouldNotUseRestrictedMode(List<String> packageNames,
+ int operationType, AndroidFuture<List<String>> resultFuture)
+ throws RemoteException {}
@Override public IBinder asBinder() {
return null;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java
index f6c644e..20ac078 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.content.Context;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
@@ -34,6 +35,8 @@
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Looper;
import android.os.OutcomeReceiver;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -71,13 +74,17 @@
private static final String PUSH_FILE_DIR = "/data/local/tmp/tests/smockingservicestest/pm/";
private static final String TEST_APP_USING_SDK1_AND_SDK2 = "HelloWorldUsingSdk1And2.apk";
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
@Mock private SharedLibrariesImpl mSharedLibraries;
+ @Mock private Context mContext;
+ @Mock private Computer mComputer;
private InstallDependencyHelper mInstallDependencyHelper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mInstallDependencyHelper = new InstallDependencyHelper(mSharedLibraries);
+ mInstallDependencyHelper = new InstallDependencyHelper(mContext, mSharedLibraries);
}
@Test
@@ -88,7 +95,8 @@
PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2);
CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false);
- mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback);
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer,
+ 0, mHandler, callback);
callback.assertFailure();
assertThat(callback.error).hasMessageThat().contains("xyz");
@@ -104,11 +112,12 @@
.thenReturn(missingDependency);
CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false);
- mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback);
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer,
+ 0, mHandler, callback);
callback.assertFailure();
assertThat(callback.error).hasMessageThat().contains(
- "Failed to bind to Dependency Installer");
+ "Dependency Installer Service not found");
}
@@ -121,7 +130,8 @@
.thenReturn(missingDependency);
CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ true);
- mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback);
+ mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, mComputer,
+ 0, mHandler, callback);
callback.assertSuccess();
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index f68ae2c..74a907f 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -309,7 +309,7 @@
*/
@TestApi
@NonNull
- @FlaggedApi("com.android.wifi.flags.shared_connectivity_broadcast_receiver_test_api")
+ @SuppressLint("UnflaggedApi") // Exempt: Test API for already shipped feature
public BroadcastReceiver getBroadcastReceiver() {
return mBroadcastReceiver;
}