Merge "Implement polling loop filter register to route custom polling loop annotations to services that can handle them." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 217101e7..f5bf437 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -15,6 +15,7 @@
aconfig_srcjars = [
// !!! KEEP THIS LIST ALPHABETICAL !!!
":aconfig_mediacodec_flags_java_lib{.generated_srcjars}",
+ ":android.adaptiveauth.flags-aconfig-java{.generated_srcjars}",
":android.app.flags-aconfig-java{.generated_srcjars}",
":android.app.smartspace.flags-aconfig-java{.generated_srcjars}",
":android.app.usage.flags-aconfig-java{.generated_srcjars}",
@@ -1033,3 +1034,16 @@
aconfig_declarations: "android.content.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Adaptive Auth
+aconfig_declarations {
+ name: "android.adaptiveauth.flags-aconfig",
+ package: "android.adaptiveauth",
+ srcs: ["core/java/android/adaptiveauth/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.adaptiveauth.flags-aconfig-java",
+ aconfig_declarations: "android.adaptiveauth.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 485f1be..9c56733 100644
--- a/Android.bp
+++ b/Android.bp
@@ -216,7 +216,6 @@
"apex_aidl_interface-java",
"packagemanager_aidl-java",
"framework-protos",
- "libtombstone_proto_java",
"updatable-driver-protos",
"ota_metadata_proto_java",
"android.hidl.base-V1.0-java",
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index d03bbd2..e7adf20 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -34,6 +34,7 @@
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libstats_atom_message_protos",
+ ":libtombstone_proto-src",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
],
diff --git a/STABILITY_OWNERS b/STABILITY_OWNERS
deleted file mode 100644
index a7ecb4d..0000000
--- a/STABILITY_OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-gaillard@google.com
-
diff --git a/api/Android.bp b/api/Android.bp
index 126176d..b3b18b6 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -60,8 +60,40 @@
metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
metalava_cmd += " --quiet "
+soong_config_module_type {
+ name: "enable_crashrecovery_module",
+ module_type: "combined_apis_defaults",
+ config_namespace: "ANDROID",
+ bool_variables: ["release_crashrecovery_module"],
+ properties: [
+ "bootclasspath",
+ "system_server_classpath",
+ ],
+}
+
+soong_config_bool_variable {
+ name: "release_crashrecovery_module",
+}
+
+enable_crashrecovery_module {
+ name: "crashrecovery_module_defaults",
+ soong_config_variables: {
+ release_crashrecovery_module: {
+ bootclasspath: [
+ "framework-crashrecovery",
+ ],
+ system_server_classpath: [
+ "service-crashrecovery",
+ ],
+ },
+ },
+}
+
combined_apis {
name: "frameworks-base-api",
+ defaults: [
+ "crashrecovery_module_defaults",
+ ],
bootclasspath: [
"android.net.ipsec.ike",
"art.module.public.api",
diff --git a/boot/Android.bp b/boot/Android.bp
index c4a1139b7..228d060 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -30,6 +30,7 @@
bool_variables: [
"car_bootclasspath_fragment",
"nfc_apex_bootclasspath_fragment",
+ "release_crashrecovery_module",
],
properties: [
"fragments",
@@ -165,6 +166,15 @@
},
],
},
+ release_crashrecovery_module: {
+ fragments: [
+ // only used when crashrecovery is enabled
+ {
+ apex: "com.android.crashrecovery",
+ module: "com.android.crashrecovery-bootclasspath-fragment",
+ },
+ ],
+ },
},
// Additional information needed by hidden api processing.
diff --git a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
index 16bb896..55ea15d 100644
--- a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
+++ b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
@@ -468,9 +468,9 @@
entry.name().c_str());
const auto& res_value = entry.res_value();
result.pairs.emplace_back(OverlayData::Value{
- name, TargetValueWithConfig{.config = entry.configuration(), .value = TargetValue{
+ name, TargetValueWithConfig{.value = TargetValue{
.data_type = static_cast<uint8_t>(res_value.data_type()),
- .data_value = res_value.data_value()}}});
+ .data_value = res_value.data_value()}, .config = entry.configuration()}});
}
}
}
diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp
index 7869fbd..3c0e118 100644
--- a/cmds/idmap2/libidmap2/ResourceContainer.cpp
+++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp
@@ -227,9 +227,9 @@
} else {
overlay_data.pairs.emplace_back(
OverlayData::Value{*target_resource, TargetValueWithConfig{
- .config = std::string(),
.value = TargetValue{.data_type = overlay_resource->dataType,
- .data_value = overlay_resource->data}}});
+ .data_value = overlay_resource->data},
+ .config = std::string()}});
}
}
@@ -268,10 +268,11 @@
std::unique_ptr<AssetManager2> am;
ZipAssetsProvider* zip_assets;
- static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> zip) {
+ static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> zip,
+ package_property_t flags) {
ResState state;
state.zip_assets = zip.get();
- if ((state.apk_assets = ApkAssets::Load(std::move(zip))) == nullptr) {
+ if ((state.apk_assets = ApkAssets::Load(std::move(zip), flags)) == nullptr) {
return Error("failed to load apk asset");
}
@@ -284,7 +285,7 @@
}
state.am = std::make_unique<AssetManager2>();
- if (!state.am->SetApkAssets({state.apk_assets})) {
+ if (!state.am->SetApkAssets({state.apk_assets}, false)) {
return Error("failed to create asset manager");
}
@@ -343,8 +344,8 @@
return state;
}
- auto state =
- ResState::Initialize(std::move(std::get<std::unique_ptr<ZipAssetsProvider>>(state_)));
+ auto state = ResState::Initialize(std::move(std::get<std::unique_ptr<ZipAssetsProvider>>(state_)),
+ PROPERTY_OPTIMIZE_NAME_LOOKUPS);
if (!state) {
return state.GetError();
}
diff --git a/core/api/current.txt b/core/api/current.txt
index d4cec2d..f55de5a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12445,7 +12445,7 @@
method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback);
method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler);
method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void reportUnarchivalStatus(int, int, long, @Nullable android.app.PendingIntent) throws android.content.pm.PackageManager.NameNotFoundException;
- method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String, @NonNull android.content.IntentSender) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull String, @NonNull android.content.IntentSender);
method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull android.content.pm.VersionedPackage, @NonNull android.content.IntentSender);
@@ -12872,7 +12872,6 @@
field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
field @FlaggedApi("android.content.pm.archiving") public static final int DELETE_ARCHIVE = 16; // 0x10
- field @FlaggedApi("android.content.pm.archiving") public static final int DELETE_SHOW_DIALOG = 32; // 0x20
field public static final int DONT_KILL_APP = 1; // 0x1
field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
@@ -24307,7 +24306,7 @@
method public void release();
method public void selectRoute(@NonNull android.media.MediaRoute2Info);
method public void setVolume(int);
- method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public boolean wasTransferRequestedBySelf();
+ method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public boolean wasTransferInitiatedBySelf();
}
public abstract static class MediaRouter2.TransferCallback {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index bd024bd..c394ab7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -295,6 +295,7 @@
field public static final String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES";
field public static final String READ_SAFETY_CENTER_STATUS = "android.permission.READ_SAFETY_CENTER_STATUS";
field public static final String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
+ field @FlaggedApi("android.app.system_terms_of_address_enabled") public static final String READ_SYSTEM_GRAMMATICAL_GENDER = "android.permission.READ_SYSTEM_GRAMMATICAL_GENDER";
field public static final String READ_SYSTEM_UPDATE_INFO = "android.permission.READ_SYSTEM_UPDATE_INFO";
field public static final String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
field public static final String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
@@ -1616,6 +1617,7 @@
field public static final int LEVEL_MEDIUM_HIGH = 4; // 0x4
field public static final int LEVEL_MEDIUM_LOW = 2; // 0x2
field public static final int LEVEL_UNKNOWN = 0; // 0x0
+ field @FlaggedApi("android.app.ambient_heart_rate") public static final int RATE_PER_MINUTE_UNKNOWN = -1; // 0xffffffff
}
public static final class AmbientContextEvent.Builder {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 0d1d8d7..a5c2af7 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -483,10 +483,15 @@
}
public class UiModeManager {
+ method @FlaggedApi("android.app.modes_api") @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public int getAttentionModeThemeOverlay();
method public boolean isNightModeLocked();
method public boolean isUiModeLocked();
method @RequiresPermission(value=android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional=true) public boolean releaseProjection(int);
method @RequiresPermission(value=android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional=true) public boolean requestProjection(int);
+ field @FlaggedApi("android.app.modes_api") public static final int MODE_ATTENTION_THEME_OVERLAY_DAY = 1002; // 0x3ea
+ field @FlaggedApi("android.app.modes_api") public static final int MODE_ATTENTION_THEME_OVERLAY_NIGHT = 1001; // 0x3e9
+ field @FlaggedApi("android.app.modes_api") public static final int MODE_ATTENTION_THEME_OVERLAY_OFF = 1000; // 0x3e8
+ field @FlaggedApi("android.app.modes_api") public static final int MODE_ATTENTION_THEME_OVERLAY_UNKNOWN = -1; // 0xffffffff
field public static final int PROJECTION_TYPE_ALL = -1; // 0xffffffff
field public static final int PROJECTION_TYPE_AUTOMOTIVE = 1; // 0x1
field public static final int PROJECTION_TYPE_NONE = 0; // 0x0
diff --git a/core/java/android/adaptiveauth/OWNERS b/core/java/android/adaptiveauth/OWNERS
new file mode 100644
index 0000000..0218a78
--- /dev/null
+++ b/core/java/android/adaptiveauth/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/adaptiveauth/OWNERS
\ No newline at end of file
diff --git a/core/java/android/adaptiveauth/flags.aconfig b/core/java/android/adaptiveauth/flags.aconfig
new file mode 100644
index 0000000..39e46bb
--- /dev/null
+++ b/core/java/android/adaptiveauth/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.adaptiveauth"
+
+flag {
+ name: "report_biometric_auth_attempts"
+ namespace: "biometrics"
+ description: "Control the usage of the biometric auth signal in adaptive auth"
+ bug: "285053096"
+}
\ No newline at end of file
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 669baf9..4b2e93f 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1540,9 +1540,16 @@
public static final int OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER =
AppProtoEnums.APP_OP_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER;
+ /**
+ * See {@link #OPSTR_READ_SYSTEM_GRAMMATICAL_GENDER}.
+ * @hide
+ */
+ public static final int OP_READ_SYSTEM_GRAMMATICAL_GENDER =
+ AppProtoEnums.APP_OP_READ_SYSTEM_GRAMMATICAL_GENDER;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 143;
+ public static final int _NUM_OP = 144;
/**
* All app ops represented as strings.
@@ -2375,6 +2382,14 @@
public static final String OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER =
"android:rapid_clear_notifications_by_listener";
+ /**
+ * Allows an application to read the system grammatical gender.
+ *
+ * @hide
+ */
+ public static final String OPSTR_READ_SYSTEM_GRAMMATICAL_GENDER =
+ "android:read_system_grammatical_gender";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2486,6 +2501,7 @@
OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
OP_MEDIA_ROUTING_CONTROL,
+ OP_READ_SYSTEM_GRAMMATICAL_GENDER,
};
static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
@@ -2938,6 +2954,10 @@
OPSTR_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER,
"RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER")
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+ new AppOpInfo.Builder(OP_READ_SYSTEM_GRAMMATICAL_GENDER,
+ OPSTR_READ_SYSTEM_GRAMMATICAL_GENDER, "READ_SYSTEM_GRAMMATICAL_GENDER")
+ .setPermission(Manifest.permission.READ_SYSTEM_GRAMMATICAL_GENDER)
+ .build(),
};
// The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index cc3eac1..afa513d 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -26,11 +26,19 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.Xml;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
import android.util.proto.WireTypeMismatchException;
+import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -40,6 +48,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Map;
+import java.util.Objects;
/**
* Describes information related to an application process's startup.
@@ -215,6 +224,11 @@
private int mDefiningUid;
/**
+ * @see #getPackageName
+ */
+ private String mPackageName;
+
+ /**
* @see #getProcessName
*/
private String mProcessName;
@@ -349,6 +363,14 @@
}
/**
+ * @see #getPackageName
+ * @hide
+ */
+ public void setPackageName(final String packageName) {
+ mPackageName = intern(packageName);
+ }
+
+ /**
* @see #getProcessName
* @hide
*/
@@ -461,6 +483,15 @@
}
/**
+ * Name of first package running in this process;
+ *
+ * @hide
+ */
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
* The actual process name it was running with.
*
* <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p>
@@ -555,6 +586,7 @@
dest.writeInt(mRealUid);
dest.writeInt(mPackageUid);
dest.writeInt(mDefiningUid);
+ dest.writeString(mPackageName);
dest.writeString(mProcessName);
dest.writeInt(mReason);
dest.writeInt(mStartupTimestampsNs == null ? 0 : mStartupTimestampsNs.size());
@@ -579,6 +611,7 @@
mRealUid = other.mRealUid;
mPackageUid = other.mPackageUid;
mDefiningUid = other.mDefiningUid;
+ mPackageName = other.mPackageName;
mProcessName = other.mProcessName;
mReason = other.mReason;
mStartupTimestampsNs = other.mStartupTimestampsNs;
@@ -593,6 +626,7 @@
mRealUid = in.readInt();
mPackageUid = in.readInt();
mDefiningUid = in.readInt();
+ mPackageName = intern(in.readString());
mProcessName = intern(in.readString());
mReason = in.readInt();
int starupTimestampCount = in.readInt();
@@ -624,6 +658,11 @@
}
};
+ private static final String PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMPS = "timestamps";
+ private static final String PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMP = "timestamp";
+ private static final String PROTO_SERIALIZER_ATTRIBUTE_KEY = "key";
+ private static final String PROTO_SERIALIZER_ATTRIBUTE_TS = "ts";
+
/**
* Write to a protocol buffer output stream. Protocol buffer message definition at {@link
* android.app.ApplicationStartInfoProto}
@@ -644,9 +683,22 @@
if (mStartupTimestampsNs != null && mStartupTimestampsNs.size() > 0) {
ByteArrayOutputStream timestampsBytes = new ByteArrayOutputStream();
ObjectOutputStream timestampsOut = new ObjectOutputStream(timestampsBytes);
- timestampsOut.writeObject(mStartupTimestampsNs);
+ TypedXmlSerializer serializer = Xml.resolveSerializer(timestampsOut);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMPS);
+ for (int i = 0; i < mStartupTimestampsNs.size(); i++) {
+ serializer.startTag(null, PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMP);
+ serializer.attributeInt(null, PROTO_SERIALIZER_ATTRIBUTE_KEY,
+ mStartupTimestampsNs.keyAt(i));
+ serializer.attributeLong(null, PROTO_SERIALIZER_ATTRIBUTE_TS,
+ mStartupTimestampsNs.valueAt(i));
+ serializer.endTag(null, PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMP);
+ }
+ serializer.endTag(null, PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMPS);
+ serializer.endDocument();
proto.write(ApplicationStartInfoProto.STARTUP_TIMESTAMPS,
timestampsBytes.toByteArray());
+ timestampsOut.close();
}
proto.write(ApplicationStartInfoProto.START_TYPE, mStartType);
if (mStartIntent != null) {
@@ -697,7 +749,24 @@
ByteArrayInputStream timestampsBytes = new ByteArrayInputStream(proto.readBytes(
ApplicationStartInfoProto.STARTUP_TIMESTAMPS));
ObjectInputStream timestampsIn = new ObjectInputStream(timestampsBytes);
- mStartupTimestampsNs = (ArrayMap<Integer, Long>) timestampsIn.readObject();
+ mStartupTimestampsNs = new ArrayMap<Integer, Long>();
+ try {
+ TypedXmlPullParser parser = Xml.resolvePullParser(timestampsIn);
+ XmlUtils.beginDocument(parser, PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMPS);
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMP.equals(parser.getName())) {
+ int key = parser.getAttributeInt(null,
+ PROTO_SERIALIZER_ATTRIBUTE_KEY);
+ long ts = parser.getAttributeLong(null,
+ PROTO_SERIALIZER_ATTRIBUTE_TS);
+ mStartupTimestampsNs.put(key, ts);
+ }
+ }
+ } catch (XmlPullParserException e) {
+ // Timestamps lost
+ }
+ timestampsIn.close();
break;
case (int) ApplicationStartInfoProto.START_TYPE:
mStartType = proto.readInt(ApplicationStartInfoProto.START_TYPE);
@@ -734,6 +803,7 @@
.append(" definingUid=").append(mDefiningUid)
.append(" user=").append(UserHandle.getUserId(mPackageUid))
.append('\n')
+ .append(" package=").append(mPackageName)
.append(" process=").append(mProcessName)
.append(" startupState=").append(mStartupState)
.append(" reason=").append(reasonToString(mReason))
@@ -782,4 +852,35 @@
default -> "";
};
}
+
+ /** @hide */
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (other == null || !(other instanceof ApplicationStartInfo)) {
+ return false;
+ }
+ final ApplicationStartInfo o = (ApplicationStartInfo) other;
+ return mPid == o.mPid && mRealUid == o.mRealUid && mPackageUid == o.mPackageUid
+ && mDefiningUid == o.mDefiningUid && mReason == o.mReason
+ && mStartupState == o.mStartupState && mStartType == o.mStartType
+ && mLaunchMode == o.mLaunchMode && TextUtils.equals(mProcessName, o.mProcessName)
+ && timestampsEquals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPid, mRealUid, mPackageUid, mDefiningUid, mReason, mStartupState,
+ mStartType, mLaunchMode, mProcessName,
+ mStartupTimestampsNs);
+ }
+
+ private boolean timestampsEquals(@NonNull ApplicationStartInfo other) {
+ if (mStartupTimestampsNs == null && other.mStartupTimestampsNs == null) {
+ return true;
+ }
+ if (mStartupTimestampsNs == null || other.mStartupTimestampsNs == null) {
+ return false;
+ }
+ return mStartupTimestampsNs.equals(other.mStartupTimestampsNs);
+ }
}
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index 60b34cd..3b83024 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -95,6 +95,34 @@
int getNightModeCustomType();
/**
+ * Overlays current Night Mode value.
+ * {@code attentionModeThemeOverlayType}.
+ *
+ * @param attentionModeThemeOverlayType
+ * @hide
+ */
+ @EnforcePermission("MODIFY_DAY_NIGHT_MODE")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)")
+ void setAttentionModeThemeOverlay(int attentionModeThemeOverlayType);
+
+
+ /**
+ * Returns current Attention Mode overlay type.
+ * <p>
+ * returns
+ * <ul>
+ * <li>{@link #MODE_ATTENTION_OFF}</li>
+ * <li>{@link #MODE_ATTENTION_NIGHT}</li>
+ * <li>{@link #MODE_ATTENTION_DAY}</li>
+ * </ul>
+ * </p>
+ * @hide
+ */
+ @EnforcePermission("MODIFY_DAY_NIGHT_MODE")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)")
+ int getAttentionModeThemeOverlay();
+
+ /**
* Sets the dark mode for the given application. This setting is persisted and will override the
* system configuration for this application.
* 1 - notnight mode
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 0ccb9cd..a271328 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -265,6 +266,60 @@
*/
public static final int MODE_NIGHT_YES = 2;
+ /** @hide */
+ @IntDef(prefix = { "MODE_ATTENTION_THEME_OVERLAY_" }, value = {
+ MODE_ATTENTION_THEME_OVERLAY_OFF,
+ MODE_ATTENTION_THEME_OVERLAY_NIGHT,
+ MODE_ATTENTION_THEME_OVERLAY_DAY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AttentionModeThemeOverlayType {}
+
+ /** @hide */
+ @IntDef(prefix = { "MODE_ATTENTION_THEME_OVERLAY_" }, value = {
+ MODE_ATTENTION_THEME_OVERLAY_OFF,
+ MODE_ATTENTION_THEME_OVERLAY_NIGHT,
+ MODE_ATTENTION_THEME_OVERLAY_DAY,
+ MODE_ATTENTION_THEME_OVERLAY_UNKNOWN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AttentionModeThemeOverlayReturnType {}
+
+ /**
+ * Constant for {@link #setAttentionModeThemeOverlay(int)} (int)} and {@link
+ * #getAttentionModeThemeOverlay()}: Keeps night mode as set by {@link #setNightMode(int)}.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ @TestApi
+ public static final int MODE_ATTENTION_THEME_OVERLAY_OFF = 1000;
+
+ /**
+ * Constant for {@link #setAttentionModeThemeOverlay(int)} (int)} and {@link
+ * #getAttentionModeThemeOverlay()}: Maintains night mode always on.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ @TestApi
+ public static final int MODE_ATTENTION_THEME_OVERLAY_NIGHT = 1001;
+
+ /**
+ * Constant for {@link #setAttentionModeThemeOverlay(int)} (int)} and {@link
+ * #getAttentionModeThemeOverlay()}: Maintains night mode always off (Light).
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ @TestApi
+ public static final int MODE_ATTENTION_THEME_OVERLAY_DAY = 1002;
+
+ /**
+ * Constant for {@link #getAttentionModeThemeOverlay()}: Error communication with server.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ @TestApi
+ public static final int MODE_ATTENTION_THEME_OVERLAY_UNKNOWN = -1;
+
/**
* Granular types for {@link #setNightModeCustomType(int)}
* @hide
@@ -733,6 +788,55 @@
}
/**
+ * Overlays current Attention mode Night Mode overlay.
+ * {@code attentionModeThemeOverlayType}.
+ *
+ * @throws IllegalArgumentException if passed an unsupported type to
+ * {@code AttentionModeThemeOverlayType}.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+ public void setAttentionModeThemeOverlay(
+ @AttentionModeThemeOverlayType int attentionModeThemeOverlayType) {
+ if (sGlobals != null) {
+ try {
+ sGlobals.mService.setAttentionModeThemeOverlay(attentionModeThemeOverlayType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the currently configured Attention Mode theme overlay.
+ * <p>
+ * May be one of:
+ * <ul>
+ * <li>{@link #MODE_ATTENTION_THEME_OVERLAY_OFF}</li>
+ * <li>{@link #MODE_ATTENTION_THEME_OVERLAY_NIGHT}</li>
+ * <li>{@link #MODE_ATTENTION_THEME_OVERLAY_DAY}</li>
+ * <li>{@link #MODE_ATTENTION_THEME_OVERLAY_UNKNOWN}</li>
+ * </ul>
+ * </p>
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+ public @AttentionModeThemeOverlayReturnType int getAttentionModeThemeOverlay() {
+ if (sGlobals != null) {
+ try {
+ return sGlobals.mService.getAttentionModeThemeOverlay();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return MODE_ATTENTION_THEME_OVERLAY_UNKNOWN;
+ }
+
+ /**
* Sets and persist the night mode for this application.
* <p>
* The mode can be one of:
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
index f94987e..5ab7991 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEvent.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -89,8 +89,11 @@
*/
public static final String KEY_VENDOR_WEARABLE_EVENT_NAME = "wearable_event_name";
- /** Default value for the rate per minute data field. */
- private static final int RATE_PER_MINUTE_UNKNOWN = -1;
+ /**
+ * Default value for {@link #getRatePerMinute}. Indicates that the rate of the event is unknown.
+ */
+ @FlaggedApi(android.app.Flags.FLAG_AMBIENT_HEART_RATE)
+ public static final int RATE_PER_MINUTE_UNKNOWN = -1;
/** @hide */
@IntDef(prefix = { "EVENT_" }, value = {
@@ -636,10 +639,10 @@
}
@DataClass.Generated(
- time = 1704895515931L,
+ time = 1705575046107L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java",
- inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final @android.app.ambientcontext.AmbientContextEvent.Event @android.annotation.FlaggedApi int EVENT_HEART_RATE\npublic static final int EVENT_VENDOR_WEARABLE_START\npublic static final java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\nprivate static final int RATE_PER_MINUTE_UNKNOWN\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate final @android.annotation.IntRange int mRatePerMinute\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nprivate static android.os.PersistableBundle defaultVendorData()\nprivate static int defaultRatePerMinute()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final @android.app.ambientcontext.AmbientContextEvent.Event @android.annotation.FlaggedApi int EVENT_HEART_RATE\npublic static final int EVENT_VENDOR_WEARABLE_START\npublic static final java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\npublic static final @android.annotation.FlaggedApi int RATE_PER_MINUTE_UNKNOWN\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate final @android.annotation.NonNull android.os.PersistableBundle mVendorData\nprivate final @android.annotation.IntRange int mRatePerMinute\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nprivate static android.os.PersistableBundle defaultVendorData()\nprivate static int defaultRatePerMinute()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 32ecb58..1f25fd0 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -80,7 +80,7 @@
long timeout);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES,android.Manifest.permission.REQUEST_DELETE_PACKAGES})")
- void requestArchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle, int flags);
+ void requestArchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
void requestUnarchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 43322641..f0efed9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2352,7 +2352,6 @@
* communicated.
*
* @param statusReceiver Callback used to notify when the operation is completed.
- * @param flags Flags for archiving. Can be 0 or {@link PackageManager#DELETE_SHOW_DIALOG}.
* @throws PackageManager.NameNotFoundException If {@code packageName} isn't found or not
* available to the caller or isn't archived.
*/
@@ -2360,12 +2359,11 @@
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.REQUEST_DELETE_PACKAGES})
@FlaggedApi(Flags.FLAG_ARCHIVING)
- public void requestArchive(@NonNull String packageName, @NonNull IntentSender statusReceiver,
- @DeleteFlags int flags)
+ public void requestArchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
throws PackageManager.NameNotFoundException {
try {
mInstaller.requestArchive(packageName, mInstallerPackageName, statusReceiver,
- new UserHandle(mUserId), flags);
+ new UserHandle(mUserId));
} catch (ParcelableException e) {
e.maybeRethrow(PackageManager.NameNotFoundException.class);
} catch (RemoteException e) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8e5e825..aabbe69 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2596,7 +2596,6 @@
DELETE_SYSTEM_APP,
DELETE_DONT_KILL_APP,
DELETE_CHATTY,
- DELETE_SHOW_DIALOG,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeleteFlags {}
@@ -2649,12 +2648,6 @@
public static final int DELETE_ARCHIVE = 0x00000010;
/**
- * Show a confirmation dialog to the user when app is being deleted.
- */
- @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
- public static final int DELETE_SHOW_DIALOG = 0x00000020;
-
- /**
* Flag parameter for {@link #deletePackage} to indicate that package deletion
* should be chatty.
*
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index c7797c7..7c5d305 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -70,4 +70,11 @@
namespace: "profile_experiences"
description: "Add support for Private Space in resolver sheet"
bug: "307515485"
+}
+
+flag {
+ name: "move_quiet_mode_operations_to_separate_thread"
+ namespace: "profile_experiences"
+ description: "Move the quiet mode operations, happening on a background thread today, to a separate thread."
+ bug: "320483504"
}
\ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7d94dd2..58159c2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3243,17 +3243,9 @@
private static final String NAME_EQ_PLACEHOLDER = "name=?";
- // Cached values of queried settings.
- // Key is the setting's name, value is the setting's value.
// Must synchronize on 'this' to access mValues and mValuesVersion.
private final ArrayMap<String, String> mValues = new ArrayMap<>();
- // Cached values for queried prefixes.
- // Key is the prefix, value is all of the settings under the prefix, mapped from a setting's
- // name to a setting's value. The name string doesn't include the prefix.
- // Must synchronize on 'this' to access.
- private final ArrayMap<String, ArrayMap<String, String>> mPrefixToValues = new ArrayMap<>();
-
private final Uri mUri;
@UnsupportedAppUsage
private final ContentProviderHolder mProviderHolder;
@@ -3600,13 +3592,15 @@
|| applicationInfo.isSignedWithPlatformKey();
}
- private Map<String, String> getStringsForPrefixStripPrefix(
- ContentResolver cr, String prefix, List<String> names) {
+ private ArrayMap<String, String> getStringsForPrefixStripPrefix(
+ ContentResolver cr, String prefix, String[] names) {
String namespace = prefix.substring(0, prefix.length() - 1);
ArrayMap<String, String> keyValues = new ArrayMap<>();
int substringLength = prefix.length();
+
int currentGeneration = -1;
boolean needsGenerationTracker = false;
+
synchronized (NameValueCache.this) {
final GenerationTracker generationTracker = mGenerationTrackers.get(prefix);
if (generationTracker != null) {
@@ -3620,22 +3614,40 @@
// generation tracker and request a new one
generationTracker.destroy();
mGenerationTrackers.remove(prefix);
- mPrefixToValues.remove(prefix);
+ for (int i = mValues.size() - 1; i >= 0; i--) {
+ String key = mValues.keyAt(i);
+ if (key.startsWith(prefix)) {
+ mValues.remove(key);
+ }
+ }
needsGenerationTracker = true;
} else {
- final ArrayMap<String, String> cachedSettings = mPrefixToValues.get(prefix);
- if (cachedSettings != null) {
- if (!names.isEmpty()) {
+ boolean prefixCached = mValues.containsKey(prefix);
+ if (prefixCached) {
+ if (DEBUG) {
+ Log.i(TAG, "Cache hit for prefix:" + prefix);
+ }
+ if (names.length > 0) {
for (String name : names) {
- // The cache can contain "null" values, need to use containsKey.
- if (cachedSettings.containsKey(name)) {
+ // mValues can contain "null" values, need to use containsKey.
+ if (mValues.containsKey(name)) {
keyValues.put(
- name,
- cachedSettings.get(name));
+ name.substring(substringLength),
+ mValues.get(name));
}
}
} else {
- keyValues.putAll(cachedSettings);
+ for (int i = 0; i < mValues.size(); ++i) {
+ String key = mValues.keyAt(i);
+ // Explicitly exclude the prefix as it is only there to
+ // signal that the prefix has been cached.
+ if (key.startsWith(prefix) && !key.equals(prefix)) {
+ String value = mValues.valueAt(i);
+ keyValues.put(
+ key.substring(substringLength),
+ value);
+ }
+ }
}
return keyValues;
}
@@ -3645,6 +3657,7 @@
needsGenerationTracker = true;
}
}
+
if (mCallListCommand == null) {
// No list command specified, return empty map
return keyValues;
@@ -3689,23 +3702,20 @@
}
// All flags for the namespace
- HashMap<String, String> flagsToValues =
+ Map<String, String> flagsToValues =
(HashMap) b.getSerializable(Settings.NameValueTable.VALUE, java.util.HashMap.class);
- if (flagsToValues == null) {
- return keyValues;
- }
// Only the flags requested by the caller
- if (!names.isEmpty()) {
+ if (names.length > 0) {
for (String name : names) {
// flagsToValues can contain "null" values, need to use containsKey.
- final String key = Config.createCompositeName(namespace, name);
- if (flagsToValues.containsKey(key)) {
+ if (flagsToValues.containsKey(name)) {
keyValues.put(
- name,
- flagsToValues.get(key));
+ name.substring(substringLength),
+ flagsToValues.get(name));
}
}
} else {
+ keyValues.ensureCapacity(keyValues.size() + flagsToValues.size());
for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
keyValues.put(
flag.getKey().substring(substringLength),
@@ -3741,18 +3751,10 @@
if (DEBUG) {
Log.i(TAG, "Updating cache for prefix:" + prefix);
}
- // Cache the complete list of flags for the namespace for bulk queries.
- // In this cached list, the setting's name doesn't include the prefix.
- ArrayMap<String, String> namesToValues =
- new ArrayMap<>(flagsToValues.size() + 1);
- for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
- namesToValues.put(
- flag.getKey().substring(substringLength),
- flag.getValue());
- }
- // Legacy behavior, we return <"", null> when queried with name = ""
- namesToValues.put("", null);
- mPrefixToValues.put(prefix, namesToValues);
+ // cache the complete list of flags for the namespace
+ mValues.putAll(flagsToValues);
+ // Adding the prefix as a signal that the prefix is cached.
+ mValues.put(prefix, null);
}
}
return keyValues;
@@ -7319,6 +7321,28 @@
"bluetooth_le_broadcast_app_source_name";
/**
+ * This is used by LocalBluetoothLeBroadcast to downgrade the broadcast quality to improve
+ * compatibility.
+ *
+ * <ul>
+ * <li>0 = false
+ * <li>1 = true
+ * </ul>
+ *
+ * @hide
+ */
+ public static final String BLUETOOTH_LE_BROADCAST_IMPROVE_COMPATIBILITY =
+ "bluetooth_le_broadcast_improve_compatibility";
+
+ /**
+ * This is used by LocalBluetoothLeBroadcast to store the fallback active device address.
+ *
+ * @hide
+ */
+ public static final String BLUETOOTH_LE_BROADCAST_FALLBACK_ACTIVE_DEVICE_ADDRESS =
+ "bluetooth_le_broadcast_fallback_active_device_address";
+
+ /**
* Ringtone routing value for hearing aid. It routes ringtone to hearing aid or device
* speaker.
* <ul>
@@ -19916,9 +19940,16 @@
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
public static Map<String, String> getStrings(@NonNull ContentResolver resolver,
@NonNull String namespace, @NonNull List<String> names) {
+ String[] compositeNames = new String[names.size()];
+ for (int i = 0, size = names.size(); i < size; ++i) {
+ compositeNames[i] = createCompositeName(namespace, names.get(i));
+ }
+
String prefix = createPrefix(namespace);
- return sNameValueCache.getStringsForPrefixStripPrefix(resolver, prefix, names);
+ ArrayMap<String, String> keyValues = sNameValueCache.getStringsForPrefixStripPrefix(
+ resolver, prefix, compositeNames);
+ return keyValues;
}
/**
@@ -20240,7 +20271,7 @@
}
}
- static String createCompositeName(@NonNull String namespace, @NonNull String name) {
+ private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
Preconditions.checkNotNull(namespace);
Preconditions.checkNotNull(name);
var sb = new StringBuilder(namespace.length() + 1 + name.length());
diff --git a/core/java/android/tracing/transition/TransitionDataSource.java b/core/java/android/tracing/transition/TransitionDataSource.java
index b8daace..baece75 100644
--- a/core/java/android/tracing/transition/TransitionDataSource.java
+++ b/core/java/android/tracing/transition/TransitionDataSource.java
@@ -16,6 +16,7 @@
package android.tracing.transition;
+import android.tracing.perfetto.CreateTlsStateArgs;
import android.tracing.perfetto.DataSource;
import android.tracing.perfetto.DataSourceInstance;
import android.tracing.perfetto.FlushCallbackArguments;
@@ -23,10 +24,14 @@
import android.tracing.perfetto.StopCallbackArguments;
import android.util.proto.ProtoInputStream;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* @hide
*/
-public class TransitionDataSource extends DataSource {
+public class TransitionDataSource
+ extends DataSource<DataSourceInstance, TransitionDataSource.TlsState, Void> {
public static String DATA_SOURCE_NAME = "com.android.wm.shell.transition";
private final Runnable mOnStartStaticCallback;
@@ -41,6 +46,15 @@
}
@Override
+ protected TlsState createTlsState(CreateTlsStateArgs<DataSourceInstance> args) {
+ return new TlsState();
+ }
+
+ public class TlsState {
+ public final Map<String, Integer> handlerMapping = new HashMap<>();
+ }
+
+ @Override
public DataSourceInstance createInstance(ProtoInputStream configStream, int instanceIndex) {
return new DataSourceInstance(this, instanceIndex) {
@Override
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 7850554..ba7874e 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -39,8 +39,10 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
+import android.view.flags.Flags;
import dalvik.system.CloseGuard;
+import dalvik.system.VMRuntime;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -101,6 +103,10 @@
long nativeObject, float frameRate, int compatibility, int changeFrameRateStrategy);
private static native void nativeDestroy(long nativeObject);
+ // 5MB is a wild guess for what the average surface should be. On most new phones, a full-screen
+ // surface is about 9MB... but not all surfaces are screen size. This should be a nice balance.
+ private static final long SURFACE_NATIVE_ALLOCATION_SIZE_BYTES = 5_000_000;
+
public static final @android.annotation.NonNull Parcelable.Creator<Surface> CREATOR =
new Parcelable.Creator<Surface>() {
@Override
@@ -331,6 +337,7 @@
*/
@UnsupportedAppUsage
public Surface() {
+ registerNativeMemoryUsage();
}
/**
@@ -343,6 +350,7 @@
*/
public Surface(@NonNull SurfaceControl from) {
copyFrom(from);
+ registerNativeMemoryUsage();
}
/**
@@ -370,6 +378,7 @@
mName = surfaceTexture.toString();
setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
}
+ registerNativeMemoryUsage();
}
/* called from android_view_Surface_createFromIGraphicBufferProducer() */
@@ -378,6 +387,7 @@
synchronized (mLock) {
setNativeObjectLocked(nativeObject);
}
+ registerNativeMemoryUsage();
}
@Override
@@ -389,6 +399,7 @@
release();
} finally {
super.finalize();
+ freeNativeMemoryUsage();
}
}
@@ -1243,4 +1254,16 @@
return mIsWideColorGamut;
}
}
+
+ private static void registerNativeMemoryUsage() {
+ if (Flags.enableSurfaceNativeAllocRegistration()) {
+ VMRuntime.getRuntime().registerNativeAllocation(SURFACE_NATIVE_ALLOCATION_SIZE_BYTES);
+ }
+ }
+
+ private static void freeNativeMemoryUsage() {
+ if (Flags.enableSurfaceNativeAllocRegistration()) {
+ VMRuntime.getRuntime().registerNativeFree(SURFACE_NATIVE_ALLOCATION_SIZE_BYTES);
+ }
+ }
}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 2b96ee6..2c5fbd7 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -89,4 +89,12 @@
description: "Feature flag to enable a multi-instance system ui component property."
bug: "262864589"
is_fixed_read_only: true
+}
+
+flag {
+ name: "insets_decoupled_configuration"
+ namespace: "windowing_frontend"
+ description: "Configuration decoupled from insets"
+ bug: "151861875"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java
index e3bfb38..e419d13 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java
@@ -38,7 +38,7 @@
public class ParsedAttributionImpl implements ParsedAttribution, Parcelable {
/** Maximum amount of attributions per package */
- static final int MAX_NUM_ATTRIBUTIONS = 10000;
+ static final int MAX_NUM_ATTRIBUTIONS = 400;
/** Tag of the attribution */
private @NonNull String tag;
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index b87027c..4191936 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -120,8 +120,9 @@
public static @NonNull <I, O> List<O> map(@Nullable List<I> cur,
Function<? super I, ? extends O> f) {
if (isEmpty(cur)) return Collections.emptyList();
- final ArrayList<O> result = new ArrayList<>();
- for (int i = 0; i < cur.size(); i++) {
+ final int size = cur.size();
+ final ArrayList<O> result = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
result.add(f.apply(cur.get(i)));
}
return result;
@@ -133,7 +134,7 @@
public static @NonNull <I, O> Set<O> map(@Nullable Set<I> cur,
Function<? super I, ? extends O> f) {
if (isEmpty(cur)) return emptySet();
- ArraySet<O> result = new ArraySet<>();
+ ArraySet<O> result = new ArraySet<>(cur.size());
if (cur instanceof ArraySet) {
ArraySet<I> arraySet = (ArraySet<I>) cur;
int size = arraySet.size();
@@ -163,7 +164,7 @@
Function<? super I, ? extends O> f) {
if (isEmpty(cur)) return Collections.emptyList();
List<O> result = null;
- for (int i = 0; i < cur.size(); i++) {
+ for (int i = 0, size = cur.size(); i < size; i++) {
O transformed = f.apply(cur.get(i));
if (transformed != null) {
result = add(result, transformed);
@@ -239,7 +240,7 @@
public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) {
if (isEmpty(list)) return Collections.emptyList();
ArrayList<T> result = null;
- for (int i = 0; i < list.size(); i++) {
+ for (int i = 0, size = list.size(); i < size; i++) {
final Object item = list.get(i);
if (c.isInstance(item)) {
result = ArrayUtils.add(result, (T) item);
@@ -273,7 +274,7 @@
public static @Nullable <T> T find(@Nullable List<T> items,
java.util.function.Predicate<T> predicate) {
if (isEmpty(items)) return null;
- for (int i = 0; i < items.size(); i++) {
+ for (int i = 0, size = items.size(); i < size; i++) {
final T item = items.get(i);
if (predicate.test(item)) return item;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4a82675..762218b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3160,7 +3160,7 @@
types of interactions
@hide -->
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
- android:protectionLevel="signature|installer|role" />
+ android:protectionLevel="signature|installer|module|role" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<!-- Allows interaction across profiles in the same profile group. -->
@@ -5055,7 +5055,7 @@
<p>Intended for use by ROLE_ASSISTANT and signature apps only.
-->
<permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
- android:protectionLevel="signature|role"/>
+ android:protectionLevel="signature|module|role"/>
<!-- Must be required by a {@link android.service.autofill.AutofillService},
to ensure that only the system can bind to it.
@@ -5673,7 +5673,7 @@
<!-- @SystemApi Allows an application to manage the holders of a role.
@hide -->
<permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer|module" />
<!-- @SystemApi Allows an application to manage the holders of roles associated with default
applications.
@@ -5693,7 +5693,7 @@
<!-- @SystemApi Allows an application to observe role holder changes.
@hide -->
<permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer|module" />
<!-- Allows an application to manage the companion devices.
@hide -->
@@ -7908,6 +7908,14 @@
<permission android:name="android.permission.GET_BACKGROUND_INSTALLED_PACKAGES"
android:protectionLevel="signature|role" />
+ <!-- @SystemApi Allows an application to read the system grammatical gender.
+ @FlaggedApi("android.app.system_terms_of_address_enabled")
+ <p>Protection level: signature|privileged|appop
+ @hide
+ -->
+ <permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"
+ android:protectionLevel="signature|privileged|appop"/>
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 5346454..d0216b30 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -192,8 +192,13 @@
<!-- The identifier of the satellite's SIM profile. The identifier is composed of MCC and MNC
of the satellite PLMN with the format "mccmnc". -->
- <string name="config_satellite_sim_identifier" translatable="false"></string>
- <java-symbol type="string" name="config_satellite_sim_identifier" />
+ <string name="config_satellite_sim_plmn_identifier" translatable="false"></string>
+ <java-symbol type="string" name="config_satellite_sim_plmn_identifier" />
+
+ <!-- The identifier for the satellite's SIM profile. The identifier is the service provider name
+ (spn) from the profile metadata. -->
+ <string name="config_satellite_sim_spn_identifier" translatable="false"></string>
+ <java-symbol type="string" name="config_satellite_sim_spn_identifier" />
<!-- The app to which the emergency call will be handed over for OEM-enabled satellite
messaging. The format of the config string is "package_name;class_name". -->
diff --git a/data/etc/com.android.documentsui.xml b/data/etc/com.android.documentsui.xml
index d32cbec..2e521e3 100644
--- a/data/etc/com.android.documentsui.xml
+++ b/data/etc/com.android.documentsui.xml
@@ -23,5 +23,7 @@
<permission name="android.permission.MODIFY_QUIET_MODE"/>
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
<permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
+ <!-- Permissions required for reading device configs -->
+ <permission name="android.permission.READ_DEVICE_CONFIG" />
</privapp-permissions>
</permissions>
diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp
index 3dd9ba9..471acaa 100644
--- a/data/fonts/Android.bp
+++ b/data/fonts/Android.bp
@@ -48,12 +48,35 @@
// Copies the font configuration file into system/etc for the product as fonts.xml.
// Additional fonts should be installed to /product/fonts/ alongside a corresponding
// fonts_customiztion.xml in /product/etc/
-prebuilt_etc {
- name: "fonts.xml",
- src: "fonts.xml",
+
+soong_config_bool_variable {
+ name: "use_var_font",
}
-prebuilt_etc {
+soong_config_module_type {
+ name: "prebuilt_fonts_xml",
+ module_type: "prebuilt_etc",
+ config_namespace: "noto_sans_cjk_config",
+ bool_variables: ["use_var_font"],
+ properties: ["src"],
+}
+
+prebuilt_fonts_xml {
+ name: "fonts.xml",
+ src: "fonts.xml",
+ soong_config_variables: {
+ use_var_font: {
+ src: "fonts_cjkvf.xml",
+ },
+ },
+}
+
+prebuilt_fonts_xml {
name: "font_fallback.xml",
src: "font_fallback.xml",
+ soong_config_variables: {
+ use_var_font: {
+ src: "font_fallback_cjkvf.xml",
+ },
+ },
}
diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml
new file mode 100644
index 0000000..70474ba
--- /dev/null
+++ b/data/fonts/font_fallback_cjkvf.xml
@@ -0,0 +1,1015 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ In this file, all fonts without names are added to the default list.
+ Fonts are chosen based on a match: full BCP-47 language tag including
+ script, then just language, and finally order (the first font containing
+ the glyph).
+
+ Order of appearance is also the tiebreaker for weight matching. This is
+ the reason why the 900 weights of Roboto precede the 700 weights - we
+ prefer the former when an 800 weight is requested. Since bold spans
+ effectively add 300 to the weight, this ensures that 900 is the bold
+ paired with the 500 weight, ensuring adequate contrast.
+
+ TODO(rsheeter) update comment; ordering to match 800 to 900 is no longer required
+-->
+<familyset version="23">
+ <!-- first font is default -->
+ <family name="sans-serif" varFamilyType="2">
+ <font>Roboto-Regular.ttf
+ <axis tag="wdth" stylevalue="100" />
+ </font>
+ </family>
+
+
+ <!-- Note that aliases must come after the fonts they reference. -->
+ <alias name="sans-serif-thin" to="sans-serif" weight="100" />
+ <alias name="sans-serif-light" to="sans-serif" weight="300" />
+ <alias name="sans-serif-medium" to="sans-serif" weight="500" />
+ <alias name="sans-serif-black" to="sans-serif" weight="900" />
+ <alias name="arial" to="sans-serif" />
+ <alias name="helvetica" to="sans-serif" />
+ <alias name="tahoma" to="sans-serif" />
+ <alias name="verdana" to="sans-serif" />
+
+ <family name="sans-serif-condensed" varFamilyType="2">
+ <font>Roboto-Regular.ttf
+ <axis tag="wdth" stylevalue="75" />
+ </font>
+ </family>
+ <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" />
+ <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" />
+
+ <family name="serif">
+ <font weight="400" style="normal" postScriptName="NotoSerif">NotoSerif-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSerif-Bold.ttf</font>
+ <font weight="400" style="italic">NotoSerif-Italic.ttf</font>
+ <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font>
+ </family>
+ <alias name="serif-bold" to="serif" weight="700" />
+ <alias name="times" to="serif" />
+ <alias name="times new roman" to="serif" />
+ <alias name="palatino" to="serif" />
+ <alias name="georgia" to="serif" />
+ <alias name="baskerville" to="serif" />
+ <alias name="goudy" to="serif" />
+ <alias name="fantasy" to="serif" />
+ <alias name="ITC Stone Serif" to="serif" />
+
+ <family name="monospace">
+ <font weight="400" style="normal">DroidSansMono.ttf</font>
+ </family>
+ <alias name="sans-serif-monospace" to="monospace" />
+ <alias name="monaco" to="monospace" />
+
+ <family name="serif-monospace">
+ <font weight="400" style="normal" postScriptName="CutiveMono-Regular">CutiveMono.ttf</font>
+ </family>
+ <alias name="courier" to="serif-monospace" />
+ <alias name="courier new" to="serif-monospace" />
+
+ <family name="casual">
+ <font weight="400" style="normal" postScriptName="ComingSoon-Regular">ComingSoon.ttf</font>
+ </family>
+
+ <family name="cursive" varFamilyType="1">
+ <font>DancingScript-Regular.ttf</font>
+ </family>
+
+ <family name="sans-serif-smallcaps">
+ <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
+ </family>
+
+ <family name="source-sans-pro">
+ <font weight="400" style="normal">SourceSansPro-Regular.ttf</font>
+ <font weight="400" style="italic">SourceSansPro-Italic.ttf</font>
+ <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font>
+ <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font>
+ <font weight="700" style="normal">SourceSansPro-Bold.ttf</font>
+ <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font>
+ </family>
+ <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600"/>
+
+ <family name="roboto-flex" varFamilyType="2">
+ <font>RobotoFlex-Regular.ttf
+ <axis tag="wdth" stylevalue="100" />
+ </font>
+ </family>
+
+ <!-- fallback fonts -->
+ <family lang="und-Arab" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoNaskhArabic">
+ NotoNaskhArabic-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
+ </family>
+ <family lang="und-Arab" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI">
+ NotoNaskhArabicUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Ethi" varFamilyType="1">
+ <font postScriptName="NotoSansEthiopic-Regular">
+ NotoSansEthiopic-VF.ttf
+ </font>
+ <font fallbackFor="serif" postScriptName="NotoSerifEthiopic-Regular">
+ NotoSerifEthiopic-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Hebr">
+ <font weight="400" style="normal" postScriptName="NotoSansHebrew">
+ NotoSansHebrew-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font>
+ </family>
+ <family lang="und-Thai" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">
+ NotoSerifThai-Regular.ttf
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
+ </family>
+ <family lang="und-Thai" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansThaiUI">
+ NotoSansThaiUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Armn" varFamilyType="1">
+ <font postScriptName="NotoSansArmenian-Regular">
+ NotoSansArmenian-VF.ttf
+ </font>
+ <font fallbackFor="serif" postScriptName="NotoSerifArmenian-Regular">
+ NotoSerifArmenian-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Geor,und-Geok" varFamilyType="1">
+ <font postScriptName="NotoSansGeorgian-Regular">
+ NotoSansGeorgian-VF.ttf
+ </font>
+ <font fallbackFor="serif" postScriptName="NotoSerifGeorgian-Regular">
+ NotoSerifGeorgian-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Deva" variant="elegant" varFamilyType="1">
+ <font postScriptName="NotoSansDevanagari-Regular">
+ NotoSansDevanagari-VF.ttf
+ </font>
+ <font fallbackFor="serif" postScriptName="NotoSerifDevanagari-Regular">
+ NotoSerifDevanagari-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Deva" variant="compact" varFamilyType="1">
+ <font postScriptName="NotoSansDevanagariUI-Regular">
+ NotoSansDevanagariUI-VF.ttf
+ </font>
+ </family>
+
+ <!-- All scripts of India should come after Devanagari, due to shared
+ danda characters.
+ -->
+ <family lang="und-Gujr" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansGujarati">
+ NotoSansGujarati-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Gujr" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansGujaratiUI">
+ NotoSansGujaratiUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Guru" variant="elegant" varFamilyType="1">
+ <font postScriptName="NotoSansGurmukhi-Regular">
+ NotoSansGurmukhi-VF.ttf
+ </font>
+ <font fallbackFor="serif" postScriptName="NotoSerifGurmukhi-Regular">
+ NotoSerifGurmukhi-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Guru" variant="compact" varFamilyType="1">
+ <font postScriptName="NotoSansGurmukhiUI-Regular">
+ NotoSansGurmukhiUI-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Taml" variant="elegant" varFamilyType="1">
+ <font postScriptName="NotoSansTamil-Regular">
+ NotoSansTamil-VF.ttf
+ </font>
+ <font fallbackFor="serif" postScriptName="NotoSerifTamil-Regular">
+ NotoSerifTamil-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Taml" variant="compact" varFamilyType="1">
+ <font postScriptName="NotoSansTamilUI-Regular">
+ NotoSansTamilUI-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Mlym" variant="elegant" varFamilyType="1">
+ <font postScriptName="NotoSansMalayalam-Regular">
+ NotoSansMalayalam-VF.ttf
+ </font>
+ <font fallbackFor="serif" postScriptName="NotoSerifMalayalam-Regular">
+ NotoSerifMalayalam-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Mlym" variant="compact" varFamilyType="1">
+ <font postScriptName="NotoSansMalayalamUI-Regular">
+ NotoSansMalayalamUI-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Beng" variant="elegant" varFamilyType="1">
+ <font postScriptName="NotoSansBengali-Regular">
+ NotoSansBengali-VF.ttf
+ </font>
+ <font fallbackFor="serif" postScriptName="NotoSerifBengali-Regular">
+ NotoSerifBengali-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Beng" variant="compact" varFamilyType="1">
+ <font postScriptName="NotoSansBengaliUI-Regular">
+ NotoSansBengaliUI-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Telu" variant="elegant" varFamilyType="1">
+ <font postScriptName="NotoSansTelugu-Regular">
+ NotoSansTelugu-VF.ttf
+ </font>
+ <font fallbackFor="serif" postScriptName="NotoSerifTelugu-Regular">
+ NotoSerifTelugu-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Telu" variant="compact" varFamilyType="1">
+ <font postScriptName="NotoSansTeluguUI-Regular">
+ NotoSansTeluguUI-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Knda" variant="elegant" varFamilyType="1">
+ <font postScriptName="NotoSansKannada-Regular">
+ NotoSansKannada-VF.ttf
+ </font>
+ <font fallbackFor="serif" postScriptName="NotoSerifKannada-Regular">
+ NotoSerifKannada-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Knda" variant="compact" varFamilyType="1">
+ <font postScriptName="NotoSansKannadaUI-Regular">
+ NotoSansKannadaUI-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Orya" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansOriya">NotoSansOriya-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font>
+ </family>
+ <family lang="und-Orya" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansOriyaUI">
+ NotoSansOriyaUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Sinh" variant="elegant" varFamilyType="1">
+ <font postScriptName="NotoSansSinhala-Regular">
+ NotoSansSinhala-VF.ttf
+ </font>
+ <font fallbackFor="serif" postScriptName="NotoSerifSinhala-Regular">
+ NotoSerifSinhala-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Sinh" variant="compact" varFamilyType="1">
+ <font postScriptName="NotoSansSinhalaUI-Regular">
+ NotoSansSinhalaUI-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Khmr" variant="elegant">
+ <font weight="100" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="26.0"/>
+ </font>
+ <font weight="200" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="39.0"/>
+ </font>
+ <font weight="300" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="58.0"/>
+ </font>
+ <font weight="400" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="90.0"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="108.0"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="128.0"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="151.0"/>
+ </font>
+ <font weight="800" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="169.0"/>
+ </font>
+ <font weight="900" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="190.0"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font>
+ </family>
+ <family lang="und-Khmr" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansKhmerUI">
+ NotoSansKhmerUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Laoo" variant="elegant">
+ <font weight="400" style="normal">NotoSansLao-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">
+ NotoSerifLao-Regular.ttf
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
+ </family>
+ <family lang="und-Laoo" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Mymr" variant="elegant">
+ <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font>
+ <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font>
+ <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
+ </family>
+ <family lang="und-Mymr" variant="compact">
+ <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font>
+ <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font>
+ <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
+ </family>
+ <family lang="und-Thaa">
+ <font weight="400" style="normal" postScriptName="NotoSansThaana">
+ NotoSansThaana-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
+ </family>
+ <family lang="und-Cham">
+ <font weight="400" style="normal" postScriptName="NotoSansCham">NotoSansCham-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
+ </family>
+ <family lang="und-Ahom">
+ <font weight="400" style="normal">NotoSansAhom-Regular.otf</font>
+ </family>
+ <family lang="und-Adlm" varFamilyType="1">
+ <font postScriptName="NotoSansAdlam-Regular">
+ NotoSansAdlam-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Avst">
+ <font weight="400" style="normal" postScriptName="NotoSansAvestan">
+ NotoSansAvestan-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Bali">
+ <font weight="400" style="normal" postScriptName="NotoSansBalinese">
+ NotoSansBalinese-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Bamu">
+ <font weight="400" style="normal" postScriptName="NotoSansBamum">NotoSansBamum-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Batk">
+ <font weight="400" style="normal" postScriptName="NotoSansBatak">NotoSansBatak-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Brah">
+ <font weight="400" style="normal" postScriptName="NotoSansBrahmi">
+ NotoSansBrahmi-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Bugi">
+ <font weight="400" style="normal" postScriptName="NotoSansBuginese">
+ NotoSansBuginese-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Buhd">
+ <font weight="400" style="normal" postScriptName="NotoSansBuhid">NotoSansBuhid-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Cans">
+ <font weight="400" style="normal">
+ NotoSansCanadianAboriginal-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Cari">
+ <font weight="400" style="normal" postScriptName="NotoSansCarian">
+ NotoSansCarian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Cakm">
+ <font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
+ </family>
+ <family lang="und-Cher">
+ <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
+ </family>
+ <family lang="und-Copt">
+ <font weight="400" style="normal" postScriptName="NotoSansCoptic">
+ NotoSansCoptic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Xsux">
+ <font weight="400" style="normal" postScriptName="NotoSansCuneiform">
+ NotoSansCuneiform-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Cprt">
+ <font weight="400" style="normal" postScriptName="NotoSansCypriot">
+ NotoSansCypriot-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Dsrt">
+ <font weight="400" style="normal" postScriptName="NotoSansDeseret">
+ NotoSansDeseret-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Egyp">
+ <font weight="400" style="normal" postScriptName="NotoSansEgyptianHieroglyphs">
+ NotoSansEgyptianHieroglyphs-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Elba">
+ <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font>
+ </family>
+ <family lang="und-Glag">
+ <font weight="400" style="normal" postScriptName="NotoSansGlagolitic">
+ NotoSansGlagolitic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Goth">
+ <font weight="400" style="normal" postScriptName="NotoSansGothic">
+ NotoSansGothic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Hano">
+ <font weight="400" style="normal" postScriptName="NotoSansHanunoo">
+ NotoSansHanunoo-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Armi">
+ <font weight="400" style="normal" postScriptName="NotoSansImperialAramaic">
+ NotoSansImperialAramaic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Phli">
+ <font weight="400" style="normal" postScriptName="NotoSansInscriptionalPahlavi">
+ NotoSansInscriptionalPahlavi-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Prti">
+ <font weight="400" style="normal" postScriptName="NotoSansInscriptionalParthian">
+ NotoSansInscriptionalParthian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Java">
+ <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font>
+ </family>
+ <family lang="und-Kthi">
+ <font weight="400" style="normal" postScriptName="NotoSansKaithi">
+ NotoSansKaithi-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Kali">
+ <font weight="400" style="normal" postScriptName="NotoSansKayahLi">
+ NotoSansKayahLi-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Khar">
+ <font weight="400" style="normal" postScriptName="NotoSansKharoshthi">
+ NotoSansKharoshthi-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Lepc">
+ <font weight="400" style="normal" postScriptName="NotoSansLepcha">
+ NotoSansLepcha-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Limb">
+ <font weight="400" style="normal" postScriptName="NotoSansLimbu">NotoSansLimbu-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Linb">
+ <font weight="400" style="normal" postScriptName="NotoSansLinearB">
+ NotoSansLinearB-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Lisu">
+ <font weight="400" style="normal" postScriptName="NotoSansLisu">NotoSansLisu-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Lyci">
+ <font weight="400" style="normal" postScriptName="NotoSansLycian">
+ NotoSansLycian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Lydi">
+ <font weight="400" style="normal" postScriptName="NotoSansLydian">
+ NotoSansLydian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Mand">
+ <font weight="400" style="normal" postScriptName="NotoSansMandaic">
+ NotoSansMandaic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Mtei">
+ <font weight="400" style="normal" postScriptName="NotoSansMeeteiMayek">
+ NotoSansMeeteiMayek-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Talu">
+ <font weight="400" style="normal" postScriptName="NotoSansNewTaiLue">
+ NotoSansNewTaiLue-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Nkoo">
+ <font weight="400" style="normal" postScriptName="NotoSansNKo">NotoSansNKo-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Ogam">
+ <font weight="400" style="normal" postScriptName="NotoSansOgham">NotoSansOgham-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Olck">
+ <font weight="400" style="normal" postScriptName="NotoSansOlChiki">
+ NotoSansOlChiki-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Ital">
+ <font weight="400" style="normal" postScriptName="NotoSansOldItalic">
+ NotoSansOldItalic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Xpeo">
+ <font weight="400" style="normal" postScriptName="NotoSansOldPersian">
+ NotoSansOldPersian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Sarb">
+ <font weight="400" style="normal" postScriptName="NotoSansOldSouthArabian">
+ NotoSansOldSouthArabian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Orkh">
+ <font weight="400" style="normal" postScriptName="NotoSansOldTurkic">
+ NotoSansOldTurkic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Osge">
+ <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
+ </family>
+ <family lang="und-Osma">
+ <font weight="400" style="normal" postScriptName="NotoSansOsmanya">
+ NotoSansOsmanya-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Phnx">
+ <font weight="400" style="normal" postScriptName="NotoSansPhoenician">
+ NotoSansPhoenician-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Rjng">
+ <font weight="400" style="normal" postScriptName="NotoSansRejang">
+ NotoSansRejang-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Runr">
+ <font weight="400" style="normal" postScriptName="NotoSansRunic">NotoSansRunic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Samr">
+ <font weight="400" style="normal" postScriptName="NotoSansSamaritan">
+ NotoSansSamaritan-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Saur">
+ <font weight="400" style="normal" postScriptName="NotoSansSaurashtra">
+ NotoSansSaurashtra-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Shaw">
+ <font weight="400" style="normal" postScriptName="NotoSansShavian">
+ NotoSansShavian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Sund">
+ <font weight="400" style="normal" postScriptName="NotoSansSundanese">
+ NotoSansSundanese-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Sylo">
+ <font weight="400" style="normal" postScriptName="NotoSansSylotiNagri">
+ NotoSansSylotiNagri-Regular.ttf
+ </font>
+ </family>
+ <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. -->
+ <family lang="und-Syre">
+ <font weight="400" style="normal" postScriptName="NotoSansSyriacEstrangela">
+ NotoSansSyriacEstrangela-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Syrn">
+ <font weight="400" style="normal" postScriptName="NotoSansSyriacEastern">
+ NotoSansSyriacEastern-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Syrj">
+ <font weight="400" style="normal" postScriptName="NotoSansSyriacWestern">
+ NotoSansSyriacWestern-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Tglg">
+ <font weight="400" style="normal" postScriptName="NotoSansTagalog">
+ NotoSansTagalog-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Tagb">
+ <font weight="400" style="normal" postScriptName="NotoSansTagbanwa">
+ NotoSansTagbanwa-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Lana">
+ <font weight="400" style="normal" postScriptName="NotoSansTaiTham">
+ NotoSansTaiTham-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Tavt">
+ <font weight="400" style="normal" postScriptName="NotoSansTaiViet">
+ NotoSansTaiViet-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Tibt" varFamilyType="1">
+ <font postScriptName="NotoSerifTibetan-Regular">
+ NotoSerifTibetan-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Tfng">
+ <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font>
+ </family>
+ <family lang="und-Ugar">
+ <font weight="400" style="normal" postScriptName="NotoSansUgaritic">
+ NotoSansUgaritic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Vaii">
+ <font weight="400" style="normal" postScriptName="NotoSansVai">NotoSansVai-Regular.ttf
+ </font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
+ </family>
+ <family lang="zh-Hans">
+ <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="100"/>
+ </font>
+ <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="200"/>
+ </font>
+ <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="300"/>
+ </font>
+ <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="800"/>
+ </font>
+ <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="900"/>
+ </font>
+ <font weight="400" style="normal" index="2" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
+ </family>
+ <family lang="zh-Hant,zh-Bopo">
+ <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="100"/>
+ </font>
+ <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="200"/>
+ </font>
+ <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="300"/>
+ </font>
+ <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="800"/>
+ </font>
+ <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="900"/>
+ </font>
+ <font weight="400" style="normal" index="3" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
+ </family>
+ <family lang="ja">
+ <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="100"/>
+ </font>
+ <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="200"/>
+ </font>
+ <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="300"/>
+ </font>
+ <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="800"/>
+ </font>
+ <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="900"/>
+ </font>
+ <font weight="400" style="normal" index="0" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
+ </family>
+ <family lang="ko">
+ <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="100"/>
+ </font>
+ <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="200"/>
+ </font>
+ <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="300"/>
+ </font>
+ <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="800"/>
+ </font>
+ <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="900"/>
+ </font>
+ <font weight="400" style="normal" index="1" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
+ </family>
+ <family lang="und-Zsye">
+ <font weight="400" style="normal">NotoColorEmoji.ttf</font>
+ </family>
+ <family lang="und-Zsye">
+ <font weight="400" style="normal">NotoColorEmojiFlags.ttf</font>
+ </family>
+ <family lang="und-Zsym">
+ <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
+ </family>
+ <!--
+ Tai Le, Yi, Mongolian, and Phags-pa are intentionally kept last, to make sure they don't
+ override the East Asian punctuation for Chinese.
+ -->
+ <family lang="und-Tale">
+ <font weight="400" style="normal" postScriptName="NotoSansTaiLe">NotoSansTaiLe-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Yiii">
+ <font weight="400" style="normal" postScriptName="NotoSansYi">NotoSansYi-Regular.ttf</font>
+ </family>
+ <family lang="und-Mong">
+ <font weight="400" style="normal" postScriptName="NotoSansMongolian">
+ NotoSansMongolian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Phag">
+ <font weight="400" style="normal" postScriptName="NotoSansPhagsPa">
+ NotoSansPhagsPa-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Hluw">
+ <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font>
+ </family>
+ <family lang="und-Bass">
+ <font weight="400" style="normal">NotoSansBassaVah-Regular.otf</font>
+ </family>
+ <family lang="und-Bhks">
+ <font weight="400" style="normal">NotoSansBhaiksuki-Regular.otf</font>
+ </family>
+ <family lang="und-Hatr">
+ <font weight="400" style="normal">NotoSansHatran-Regular.otf</font>
+ </family>
+ <family lang="und-Lina">
+ <font weight="400" style="normal">NotoSansLinearA-Regular.otf</font>
+ </family>
+ <family lang="und-Mani">
+ <font weight="400" style="normal">NotoSansManichaean-Regular.otf</font>
+ </family>
+ <family lang="und-Marc">
+ <font weight="400" style="normal">NotoSansMarchen-Regular.otf</font>
+ </family>
+ <family lang="und-Merc">
+ <font weight="400" style="normal">NotoSansMeroitic-Regular.otf</font>
+ </family>
+ <family lang="und-Plrd">
+ <font weight="400" style="normal">NotoSansMiao-Regular.otf</font>
+ </family>
+ <family lang="und-Mroo">
+ <font weight="400" style="normal">NotoSansMro-Regular.otf</font>
+ </family>
+ <family lang="und-Mult">
+ <font weight="400" style="normal">NotoSansMultani-Regular.otf</font>
+ </family>
+ <family lang="und-Nbat">
+ <font weight="400" style="normal">NotoSansNabataean-Regular.otf</font>
+ </family>
+ <family lang="und-Newa">
+ <font weight="400" style="normal">NotoSansNewa-Regular.otf</font>
+ </family>
+ <family lang="und-Narb">
+ <font weight="400" style="normal">NotoSansOldNorthArabian-Regular.otf</font>
+ </family>
+ <family lang="und-Perm">
+ <font weight="400" style="normal">NotoSansOldPermic-Regular.otf</font>
+ </family>
+ <family lang="und-Hmng">
+ <font weight="400" style="normal">NotoSansPahawhHmong-Regular.otf</font>
+ </family>
+ <family lang="und-Palm">
+ <font weight="400" style="normal">NotoSansPalmyrene-Regular.otf</font>
+ </family>
+ <family lang="und-Pauc">
+ <font weight="400" style="normal">NotoSansPauCinHau-Regular.otf</font>
+ </family>
+ <family lang="und-Shrd">
+ <font weight="400" style="normal">NotoSansSharada-Regular.otf</font>
+ </family>
+ <family lang="und-Sora">
+ <font weight="400" style="normal">NotoSansSoraSompeng-Regular.otf</font>
+ </family>
+ <family lang="und-Gong">
+ <font weight="400" style="normal">NotoSansGunjalaGondi-Regular.otf</font>
+ </family>
+ <family lang="und-Rohg">
+ <font weight="400" style="normal">NotoSansHanifiRohingya-Regular.otf</font>
+ </family>
+ <family lang="und-Khoj">
+ <font weight="400" style="normal">NotoSansKhojki-Regular.otf</font>
+ </family>
+ <family lang="und-Gonm">
+ <font weight="400" style="normal">NotoSansMasaramGondi-Regular.otf</font>
+ </family>
+ <family lang="und-Wcho">
+ <font weight="400" style="normal">NotoSansWancho-Regular.otf</font>
+ </family>
+ <family lang="und-Wara">
+ <font weight="400" style="normal">NotoSansWarangCiti-Regular.otf</font>
+ </family>
+ <family lang="und-Gran">
+ <font weight="400" style="normal">NotoSansGrantha-Regular.ttf</font>
+ </family>
+ <family lang="und-Modi">
+ <font weight="400" style="normal">NotoSansModi-Regular.ttf</font>
+ </family>
+ <family lang="und-Dogr">
+ <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font>
+ </family>
+ <family lang="und-Medf" varFamilyType="1">
+ <font postScriptName="NotoSansMedefaidrin-Regular">
+ NotoSansMedefaidrin-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Soyo" varFamilyType="1">
+ <font postScriptName="NotoSansSoyombo-Regular">
+ NotoSansSoyombo-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Takr" varFamilyType="1">
+ <font postScriptName="NotoSansTakri-Regular">
+ NotoSansTakri-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Hmnp" varFamilyType="1">
+ <font postScriptName="NotoSerifHmongNyiakeng-Regular">
+ NotoSerifNyiakengPuachueHmong-VF.ttf
+ </font>
+ </family>
+ <family lang="und-Yezi" varFamilyType="1">
+ <font postScriptName="NotoSerifYezidi-Regular">
+ NotoSerifYezidi-VF.ttf
+ </font>
+ </family>
+</familyset>
diff --git a/data/fonts/fonts_cjkvf.xml b/data/fonts/fonts_cjkvf.xml
new file mode 100644
index 0000000..8d91edd
--- /dev/null
+++ b/data/fonts/fonts_cjkvf.xml
@@ -0,0 +1,1785 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ DEPRECATED: This XML file is no longer a source of the font files installed
+ in the system.
+
+ For the device vendors: please add your font configurations to the
+ platform/frameworks/base/data/font_fallback.xml and also add it to this XML
+ file as much as possible for apps that reads this XML file.
+
+ For the application developers: please stop reading this XML file and use
+ android.graphics.fonts.SystemFonts#getAvailableFonts Java API or
+ ASystemFontIterator_open NDK API for getting list of system installed
+ font files.
+
+ WARNING: Parsing of this file by third-party apps is not supported. The
+ file, and the font files it refers to, will be renamed and/or moved out
+ from their respective location in the next Android release, and/or the
+ format or syntax of the file may change significantly. If you parse this
+ file for information about system fonts, do it at your own risk. Your
+ application will almost certainly break with the next major Android
+ release.
+
+ In this file, all fonts without names are added to the default list.
+ Fonts are chosen based on a match: full BCP-47 language tag including
+ script, then just language, and finally order (the first font containing
+ the glyph).
+
+ Order of appearance is also the tiebreaker for weight matching. This is
+ the reason why the 900 weights of Roboto precede the 700 weights - we
+ prefer the former when an 800 weight is requested. Since bold spans
+ effectively add 300 to the weight, this ensures that 900 is the bold
+ paired with the 500 weight, ensuring adequate contrast.
+
+ TODO(rsheeter) update comment; ordering to match 800 to 900 is no longer required
+-->
+<familyset version="23">
+ <!-- first font is default -->
+ <family name="sans-serif">
+ <font weight="100" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="100" />
+ </font>
+ <font weight="200" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="200" />
+ </font>
+ <font weight="300" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="300" />
+ </font>
+ <font weight="400" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="800" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="800" />
+ </font>
+ <font weight="900" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="900" />
+ </font>
+ <font weight="100" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="100" />
+ </font>
+ <font weight="200" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="200" />
+ </font>
+ <font weight="300" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="300" />
+ </font>
+ <font weight="400" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="800" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="800" />
+ </font>
+ <font weight="900" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="900" />
+ </font>
+ </family>
+
+
+ <!-- Note that aliases must come after the fonts they reference. -->
+ <alias name="sans-serif-thin" to="sans-serif" weight="100" />
+ <alias name="sans-serif-light" to="sans-serif" weight="300" />
+ <alias name="sans-serif-medium" to="sans-serif" weight="500" />
+ <alias name="sans-serif-black" to="sans-serif" weight="900" />
+ <alias name="arial" to="sans-serif" />
+ <alias name="helvetica" to="sans-serif" />
+ <alias name="tahoma" to="sans-serif" />
+ <alias name="verdana" to="sans-serif" />
+
+ <family name="sans-serif-condensed">
+ <font weight="100" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="100" />
+ </font>
+ <font weight="200" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="200" />
+ </font>
+ <font weight="300" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="300" />
+ </font>
+ <font weight="400" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="800" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="800" />
+ </font>
+ <font weight="900" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="900" />
+ </font>
+ <font weight="100" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="100" />
+ </font>
+ <font weight="200" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="200" />
+ </font>
+ <font weight="300" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="300" />
+ </font>
+ <font weight="400" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="800" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="800" />
+ </font>
+ <font weight="900" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="900" />
+ </font>
+ </family>
+ <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" />
+ <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" />
+
+ <family name="serif">
+ <font weight="400" style="normal" postScriptName="NotoSerif">NotoSerif-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSerif-Bold.ttf</font>
+ <font weight="400" style="italic">NotoSerif-Italic.ttf</font>
+ <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font>
+ </family>
+ <alias name="serif-bold" to="serif" weight="700" />
+ <alias name="times" to="serif" />
+ <alias name="times new roman" to="serif" />
+ <alias name="palatino" to="serif" />
+ <alias name="georgia" to="serif" />
+ <alias name="baskerville" to="serif" />
+ <alias name="goudy" to="serif" />
+ <alias name="fantasy" to="serif" />
+ <alias name="ITC Stone Serif" to="serif" />
+
+ <family name="monospace">
+ <font weight="400" style="normal">DroidSansMono.ttf</font>
+ </family>
+ <alias name="sans-serif-monospace" to="monospace" />
+ <alias name="monaco" to="monospace" />
+
+ <family name="serif-monospace">
+ <font weight="400" style="normal" postScriptName="CutiveMono-Regular">CutiveMono.ttf</font>
+ </family>
+ <alias name="courier" to="serif-monospace" />
+ <alias name="courier new" to="serif-monospace" />
+
+ <family name="casual">
+ <font weight="400" style="normal" postScriptName="ComingSoon-Regular">ComingSoon.ttf</font>
+ </family>
+
+ <family name="cursive">
+ <font weight="400" style="normal">DancingScript-Regular.ttf
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="700" style="normal">DancingScript-Regular.ttf
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ </family>
+
+ <family name="sans-serif-smallcaps">
+ <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
+ </family>
+
+ <family name="source-sans-pro">
+ <font weight="400" style="normal">SourceSansPro-Regular.ttf</font>
+ <font weight="400" style="italic">SourceSansPro-Italic.ttf</font>
+ <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font>
+ <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font>
+ <font weight="700" style="normal">SourceSansPro-Bold.ttf</font>
+ <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font>
+ </family>
+ <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600"/>
+
+ <family name="roboto-flex">
+ <font weight="100" style="normal">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="100" />
+ </font>
+ <font weight="200" style="normal">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="200" />
+ </font>
+ <font weight="300" style="normal">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="300" />
+ </font>
+ <font weight="400" style="normal">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="800" style="normal">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="800" />
+ </font>
+ <font weight="900" style="normal">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="900" />
+ </font>
+ <font weight="100" style="italic">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="-10" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="100" />
+ </font>
+ <font weight="200" style="italic">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="-10" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="200" />
+ </font>
+ <font weight="300" style="italic">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="-10" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="300" />
+ </font>
+ <font weight="400" style="italic">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="-10" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="italic">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="-10" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="italic">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="-10" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="italic">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="-10" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="800" style="italic">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="-10" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="800" />
+ </font>
+ <font weight="900" style="italic">RobotoFlex-Regular.ttf
+ <axis tag="slnt" stylevalue="-10" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="900" />
+ </font>
+ </family>
+
+ <!-- fallback fonts -->
+ <family lang="und-Arab" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoNaskhArabic">
+ NotoNaskhArabic-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
+ </family>
+ <family lang="und-Arab" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI">
+ NotoNaskhArabicUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Ethi">
+ <font weight="400" style="normal" postScriptName="NotoSansEthiopic-Regular">
+ NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansEthiopic-Regular">
+ NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansEthiopic-Regular">
+ NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansEthiopic-Regular">
+ NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Hebr">
+ <font weight="400" style="normal" postScriptName="NotoSansHebrew">
+ NotoSansHebrew-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font>
+ </family>
+ <family lang="und-Thai" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">
+ NotoSerifThai-Regular.ttf
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
+ </family>
+ <family lang="und-Thai" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansThaiUI">
+ NotoSansThaiUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Armn">
+ <font weight="400" style="normal" postScriptName="NotoSansArmenian-Regular">
+ NotoSansArmenian-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansArmenian-Regular">
+ NotoSansArmenian-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansArmenian-Regular">
+ NotoSansArmenian-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansArmenian-Regular">
+ NotoSansArmenian-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Geor,und-Geok">
+ <font weight="400" style="normal" postScriptName="NotoSansGeorgian-Regular">
+ NotoSansGeorgian-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansGeorgian-Regular">
+ NotoSansGeorgian-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansGeorgian-Regular">
+ NotoSansGeorgian-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansGeorgian-Regular">
+ NotoSansGeorgian-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Deva" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansDevanagari-Regular">
+ NotoSansDevanagari-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansDevanagari-Regular">
+ NotoSansDevanagari-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansDevanagari-Regular">
+ NotoSansDevanagari-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansDevanagari-Regular">
+ NotoSansDevanagari-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Deva" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
+ NotoSansDevanagariUI-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
+ NotoSansDevanagariUI-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
+ NotoSansDevanagariUI-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
+ NotoSansDevanagariUI-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+
+ <!-- All scripts of India should come after Devanagari, due to shared
+ danda characters.
+ -->
+ <family lang="und-Gujr" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansGujarati">
+ NotoSansGujarati-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Gujr" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansGujaratiUI">
+ NotoSansGujaratiUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Guru" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansGurmukhi-Regular">
+ NotoSansGurmukhi-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansGurmukhi-Regular">
+ NotoSansGurmukhi-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansGurmukhi-Regular">
+ NotoSansGurmukhi-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansGurmukhi-Regular">
+ NotoSansGurmukhi-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Guru" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
+ NotoSansGurmukhiUI-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
+ NotoSansGurmukhiUI-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
+ NotoSansGurmukhiUI-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
+ NotoSansGurmukhiUI-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Taml" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansTamil-Regular">
+ NotoSansTamil-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansTamil-Regular">
+ NotoSansTamil-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansTamil-Regular">
+ NotoSansTamil-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansTamil-Regular">
+ NotoSansTamil-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Taml" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansTamilUI-Regular">
+ NotoSansTamilUI-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansTamilUI-Regular">
+ NotoSansTamilUI-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansTamilUI-Regular">
+ NotoSansTamilUI-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansTamilUI-Regular">
+ NotoSansTamilUI-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Mlym" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansMalayalam-Regular">
+ NotoSansMalayalam-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansMalayalam-Regular">
+ NotoSansMalayalam-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansMalayalam-Regular">
+ NotoSansMalayalam-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansMalayalam-Regular">
+ NotoSansMalayalam-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Mlym" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
+ NotoSansMalayalamUI-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
+ NotoSansMalayalamUI-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
+ NotoSansMalayalamUI-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
+ NotoSansMalayalamUI-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Beng" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansBengali-Regular">
+ NotoSansBengali-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansBengali-Regular">
+ NotoSansBengali-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansBengali-Regular">
+ NotoSansBengali-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansBengali-Regular">
+ NotoSansBengali-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Beng" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansBengaliUI-Regular">
+ NotoSansBengaliUI-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansBengaliUI-Regular">
+ NotoSansBengaliUI-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansBengaliUI-Regular">
+ NotoSansBengaliUI-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansBengaliUI-Regular">
+ NotoSansBengaliUI-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Telu" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansTelugu-Regular">
+ NotoSansTelugu-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansTelugu-Regular">
+ NotoSansTelugu-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansTelugu-Regular">
+ NotoSansTelugu-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansTelugu-Regular">
+ NotoSansTelugu-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Telu" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansTeluguUI-Regular">
+ NotoSansTeluguUI-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansTeluguUI-Regular">
+ NotoSansTeluguUI-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansTeluguUI-Regular">
+ NotoSansTeluguUI-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansTeluguUI-Regular">
+ NotoSansTeluguUI-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Knda" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansKannada-Regular">
+ NotoSansKannada-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansKannada-Regular">
+ NotoSansKannada-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansKannada-Regular">
+ NotoSansKannada-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansKannada-Regular">
+ NotoSansKannada-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Knda" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansKannadaUI-Regular">
+ NotoSansKannadaUI-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansKannadaUI-Regular">
+ NotoSansKannadaUI-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansKannadaUI-Regular">
+ NotoSansKannadaUI-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansKannadaUI-Regular">
+ NotoSansKannadaUI-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Orya" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansOriya">NotoSansOriya-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font>
+ </family>
+ <family lang="und-Orya" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansOriyaUI">
+ NotoSansOriyaUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Sinh" variant="elegant">
+ <font weight="400" style="normal" postScriptName="NotoSansSinhala-Regular">
+ NotoSansSinhala-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansSinhala-Regular">
+ NotoSansSinhala-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansSinhala-Regular">
+ NotoSansSinhala-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansSinhala-Regular">
+ NotoSansSinhala-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Sinh" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
+ NotoSansSinhalaUI-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
+ NotoSansSinhalaUI-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
+ NotoSansSinhalaUI-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
+ NotoSansSinhalaUI-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Khmr" variant="elegant">
+ <font weight="100" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="26.0"/>
+ </font>
+ <font weight="200" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="39.0"/>
+ </font>
+ <font weight="300" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="58.0"/>
+ </font>
+ <font weight="400" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="90.0"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="108.0"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="128.0"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="151.0"/>
+ </font>
+ <font weight="800" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="169.0"/>
+ </font>
+ <font weight="900" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
+ <axis tag="wdth" stylevalue="100.0"/>
+ <axis tag="wght" stylevalue="190.0"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font>
+ </family>
+ <family lang="und-Khmr" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansKhmerUI">
+ NotoSansKhmerUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Laoo" variant="elegant">
+ <font weight="400" style="normal">NotoSansLao-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif">
+ NotoSerifLao-Regular.ttf
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
+ </family>
+ <family lang="und-Laoo" variant="compact">
+ <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
+ </family>
+ <family lang="und-Mymr" variant="elegant">
+ <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font>
+ <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font>
+ <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
+ </family>
+ <family lang="und-Mymr" variant="compact">
+ <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font>
+ <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font>
+ <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
+ </family>
+ <family lang="und-Thaa">
+ <font weight="400" style="normal" postScriptName="NotoSansThaana">
+ NotoSansThaana-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
+ </family>
+ <family lang="und-Cham">
+ <font weight="400" style="normal" postScriptName="NotoSansCham">NotoSansCham-Regular.ttf
+ </font>
+ <font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
+ </family>
+ <family lang="und-Ahom">
+ <font weight="400" style="normal">NotoSansAhom-Regular.otf</font>
+ </family>
+ <family lang="und-Adlm">
+ <font weight="400" style="normal" postScriptName="NotoSansAdlam-Regular">
+ NotoSansAdlam-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansAdlam-Regular">
+ NotoSansAdlam-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansAdlam-Regular">
+ NotoSansAdlam-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansAdlam-Regular">
+ NotoSansAdlam-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Avst">
+ <font weight="400" style="normal" postScriptName="NotoSansAvestan">
+ NotoSansAvestan-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Bali">
+ <font weight="400" style="normal" postScriptName="NotoSansBalinese">
+ NotoSansBalinese-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Bamu">
+ <font weight="400" style="normal" postScriptName="NotoSansBamum">NotoSansBamum-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Batk">
+ <font weight="400" style="normal" postScriptName="NotoSansBatak">NotoSansBatak-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Brah">
+ <font weight="400" style="normal" postScriptName="NotoSansBrahmi">
+ NotoSansBrahmi-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Bugi">
+ <font weight="400" style="normal" postScriptName="NotoSansBuginese">
+ NotoSansBuginese-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Buhd">
+ <font weight="400" style="normal" postScriptName="NotoSansBuhid">NotoSansBuhid-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Cans">
+ <font weight="400" style="normal">
+ NotoSansCanadianAboriginal-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Cari">
+ <font weight="400" style="normal" postScriptName="NotoSansCarian">
+ NotoSansCarian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Cakm">
+ <font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
+ </family>
+ <family lang="und-Cher">
+ <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
+ </family>
+ <family lang="und-Copt">
+ <font weight="400" style="normal" postScriptName="NotoSansCoptic">
+ NotoSansCoptic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Xsux">
+ <font weight="400" style="normal" postScriptName="NotoSansCuneiform">
+ NotoSansCuneiform-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Cprt">
+ <font weight="400" style="normal" postScriptName="NotoSansCypriot">
+ NotoSansCypriot-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Dsrt">
+ <font weight="400" style="normal" postScriptName="NotoSansDeseret">
+ NotoSansDeseret-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Egyp">
+ <font weight="400" style="normal" postScriptName="NotoSansEgyptianHieroglyphs">
+ NotoSansEgyptianHieroglyphs-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Elba">
+ <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font>
+ </family>
+ <family lang="und-Glag">
+ <font weight="400" style="normal" postScriptName="NotoSansGlagolitic">
+ NotoSansGlagolitic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Goth">
+ <font weight="400" style="normal" postScriptName="NotoSansGothic">
+ NotoSansGothic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Hano">
+ <font weight="400" style="normal" postScriptName="NotoSansHanunoo">
+ NotoSansHanunoo-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Armi">
+ <font weight="400" style="normal" postScriptName="NotoSansImperialAramaic">
+ NotoSansImperialAramaic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Phli">
+ <font weight="400" style="normal" postScriptName="NotoSansInscriptionalPahlavi">
+ NotoSansInscriptionalPahlavi-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Prti">
+ <font weight="400" style="normal" postScriptName="NotoSansInscriptionalParthian">
+ NotoSansInscriptionalParthian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Java">
+ <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font>
+ </family>
+ <family lang="und-Kthi">
+ <font weight="400" style="normal" postScriptName="NotoSansKaithi">
+ NotoSansKaithi-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Kali">
+ <font weight="400" style="normal" postScriptName="NotoSansKayahLi">
+ NotoSansKayahLi-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Khar">
+ <font weight="400" style="normal" postScriptName="NotoSansKharoshthi">
+ NotoSansKharoshthi-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Lepc">
+ <font weight="400" style="normal" postScriptName="NotoSansLepcha">
+ NotoSansLepcha-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Limb">
+ <font weight="400" style="normal" postScriptName="NotoSansLimbu">NotoSansLimbu-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Linb">
+ <font weight="400" style="normal" postScriptName="NotoSansLinearB">
+ NotoSansLinearB-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Lisu">
+ <font weight="400" style="normal" postScriptName="NotoSansLisu">NotoSansLisu-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Lyci">
+ <font weight="400" style="normal" postScriptName="NotoSansLycian">
+ NotoSansLycian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Lydi">
+ <font weight="400" style="normal" postScriptName="NotoSansLydian">
+ NotoSansLydian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Mand">
+ <font weight="400" style="normal" postScriptName="NotoSansMandaic">
+ NotoSansMandaic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Mtei">
+ <font weight="400" style="normal" postScriptName="NotoSansMeeteiMayek">
+ NotoSansMeeteiMayek-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Talu">
+ <font weight="400" style="normal" postScriptName="NotoSansNewTaiLue">
+ NotoSansNewTaiLue-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Nkoo">
+ <font weight="400" style="normal" postScriptName="NotoSansNKo">NotoSansNKo-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Ogam">
+ <font weight="400" style="normal" postScriptName="NotoSansOgham">NotoSansOgham-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Olck">
+ <font weight="400" style="normal" postScriptName="NotoSansOlChiki">
+ NotoSansOlChiki-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Ital">
+ <font weight="400" style="normal" postScriptName="NotoSansOldItalic">
+ NotoSansOldItalic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Xpeo">
+ <font weight="400" style="normal" postScriptName="NotoSansOldPersian">
+ NotoSansOldPersian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Sarb">
+ <font weight="400" style="normal" postScriptName="NotoSansOldSouthArabian">
+ NotoSansOldSouthArabian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Orkh">
+ <font weight="400" style="normal" postScriptName="NotoSansOldTurkic">
+ NotoSansOldTurkic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Osge">
+ <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
+ </family>
+ <family lang="und-Osma">
+ <font weight="400" style="normal" postScriptName="NotoSansOsmanya">
+ NotoSansOsmanya-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Phnx">
+ <font weight="400" style="normal" postScriptName="NotoSansPhoenician">
+ NotoSansPhoenician-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Rjng">
+ <font weight="400" style="normal" postScriptName="NotoSansRejang">
+ NotoSansRejang-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Runr">
+ <font weight="400" style="normal" postScriptName="NotoSansRunic">NotoSansRunic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Samr">
+ <font weight="400" style="normal" postScriptName="NotoSansSamaritan">
+ NotoSansSamaritan-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Saur">
+ <font weight="400" style="normal" postScriptName="NotoSansSaurashtra">
+ NotoSansSaurashtra-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Shaw">
+ <font weight="400" style="normal" postScriptName="NotoSansShavian">
+ NotoSansShavian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Sund">
+ <font weight="400" style="normal" postScriptName="NotoSansSundanese">
+ NotoSansSundanese-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Sylo">
+ <font weight="400" style="normal" postScriptName="NotoSansSylotiNagri">
+ NotoSansSylotiNagri-Regular.ttf
+ </font>
+ </family>
+ <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. -->
+ <family lang="und-Syre">
+ <font weight="400" style="normal" postScriptName="NotoSansSyriacEstrangela">
+ NotoSansSyriacEstrangela-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Syrn">
+ <font weight="400" style="normal" postScriptName="NotoSansSyriacEastern">
+ NotoSansSyriacEastern-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Syrj">
+ <font weight="400" style="normal" postScriptName="NotoSansSyriacWestern">
+ NotoSansSyriacWestern-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Tglg">
+ <font weight="400" style="normal" postScriptName="NotoSansTagalog">
+ NotoSansTagalog-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Tagb">
+ <font weight="400" style="normal" postScriptName="NotoSansTagbanwa">
+ NotoSansTagbanwa-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Lana">
+ <font weight="400" style="normal" postScriptName="NotoSansTaiTham">
+ NotoSansTaiTham-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Tavt">
+ <font weight="400" style="normal" postScriptName="NotoSansTaiViet">
+ NotoSansTaiViet-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Tibt">
+ <font weight="400" style="normal" postScriptName="NotoSerifTibetan-Regular">
+ NotoSerifTibetan-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSerifTibetan-Regular">
+ NotoSerifTibetan-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSerifTibetan-Regular">
+ NotoSerifTibetan-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSerifTibetan-Regular">
+ NotoSerifTibetan-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Tfng">
+ <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font>
+ </family>
+ <family lang="und-Ugar">
+ <font weight="400" style="normal" postScriptName="NotoSansUgaritic">
+ NotoSansUgaritic-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Vaii">
+ <font weight="400" style="normal" postScriptName="NotoSansVai">NotoSansVai-Regular.ttf
+ </font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
+ </family>
+ <family lang="zh-Hans">
+ <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="100"/>
+ </font>
+ <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="200"/>
+ </font>
+ <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="300"/>
+ </font>
+ <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="800"/>
+ </font>
+ <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="900"/>
+ </font>
+ <font weight="400" style="normal" index="2" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
+ </family>
+ <family lang="zh-Hant,zh-Bopo">
+ <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="100"/>
+ </font>
+ <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="200"/>
+ </font>
+ <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="300"/>
+ </font>
+ <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="800"/>
+ </font>
+ <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="900"/>
+ </font>
+ <font weight="400" style="normal" index="3" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
+ </family>
+ <family lang="ja">
+ <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="100"/>
+ </font>
+ <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="200"/>
+ </font>
+ <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="300"/>
+ </font>
+ <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="800"/>
+ </font>
+ <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="900"/>
+ </font>
+ <font weight="400" style="normal" index="0" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
+ </family>
+ <family lang="ko">
+ <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="100"/>
+ </font>
+ <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="200"/>
+ </font>
+ <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="300"/>
+ </font>
+ <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="800"/>
+ </font>
+ <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ NotoSansCJK-Regular.ttc
+ <axis tag="wght" stylevalue="900"/>
+ </font>
+ <font weight="400" style="normal" index="1" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
+ </family>
+ <family lang="und-Zsye" ignore="true">
+ <font weight="400" style="normal">NotoColorEmojiLegacy.ttf</font>
+ </family>
+ <family lang="und-Zsye">
+ <font weight="400" style="normal">NotoColorEmoji.ttf</font>
+ </family>
+ <family lang="und-Zsye">
+ <font weight="400" style="normal">NotoColorEmojiFlags.ttf</font>
+ </family>
+ <family lang="und-Zsym">
+ <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
+ </family>
+ <!--
+ Tai Le, Yi, Mongolian, and Phags-pa are intentionally kept last, to make sure they don't
+ override the East Asian punctuation for Chinese.
+ -->
+ <family lang="und-Tale">
+ <font weight="400" style="normal" postScriptName="NotoSansTaiLe">NotoSansTaiLe-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Yiii">
+ <font weight="400" style="normal" postScriptName="NotoSansYi">NotoSansYi-Regular.ttf</font>
+ </family>
+ <family lang="und-Mong">
+ <font weight="400" style="normal" postScriptName="NotoSansMongolian">
+ NotoSansMongolian-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Phag">
+ <font weight="400" style="normal" postScriptName="NotoSansPhagsPa">
+ NotoSansPhagsPa-Regular.ttf
+ </font>
+ </family>
+ <family lang="und-Hluw">
+ <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font>
+ </family>
+ <family lang="und-Bass">
+ <font weight="400" style="normal">NotoSansBassaVah-Regular.otf</font>
+ </family>
+ <family lang="und-Bhks">
+ <font weight="400" style="normal">NotoSansBhaiksuki-Regular.otf</font>
+ </family>
+ <family lang="und-Hatr">
+ <font weight="400" style="normal">NotoSansHatran-Regular.otf</font>
+ </family>
+ <family lang="und-Lina">
+ <font weight="400" style="normal">NotoSansLinearA-Regular.otf</font>
+ </family>
+ <family lang="und-Mani">
+ <font weight="400" style="normal">NotoSansManichaean-Regular.otf</font>
+ </family>
+ <family lang="und-Marc">
+ <font weight="400" style="normal">NotoSansMarchen-Regular.otf</font>
+ </family>
+ <family lang="und-Merc">
+ <font weight="400" style="normal">NotoSansMeroitic-Regular.otf</font>
+ </family>
+ <family lang="und-Plrd">
+ <font weight="400" style="normal">NotoSansMiao-Regular.otf</font>
+ </family>
+ <family lang="und-Mroo">
+ <font weight="400" style="normal">NotoSansMro-Regular.otf</font>
+ </family>
+ <family lang="und-Mult">
+ <font weight="400" style="normal">NotoSansMultani-Regular.otf</font>
+ </family>
+ <family lang="und-Nbat">
+ <font weight="400" style="normal">NotoSansNabataean-Regular.otf</font>
+ </family>
+ <family lang="und-Newa">
+ <font weight="400" style="normal">NotoSansNewa-Regular.otf</font>
+ </family>
+ <family lang="und-Narb">
+ <font weight="400" style="normal">NotoSansOldNorthArabian-Regular.otf</font>
+ </family>
+ <family lang="und-Perm">
+ <font weight="400" style="normal">NotoSansOldPermic-Regular.otf</font>
+ </family>
+ <family lang="und-Hmng">
+ <font weight="400" style="normal">NotoSansPahawhHmong-Regular.otf</font>
+ </family>
+ <family lang="und-Palm">
+ <font weight="400" style="normal">NotoSansPalmyrene-Regular.otf</font>
+ </family>
+ <family lang="und-Pauc">
+ <font weight="400" style="normal">NotoSansPauCinHau-Regular.otf</font>
+ </family>
+ <family lang="und-Shrd">
+ <font weight="400" style="normal">NotoSansSharada-Regular.otf</font>
+ </family>
+ <family lang="und-Sora">
+ <font weight="400" style="normal">NotoSansSoraSompeng-Regular.otf</font>
+ </family>
+ <family lang="und-Gong">
+ <font weight="400" style="normal">NotoSansGunjalaGondi-Regular.otf</font>
+ </family>
+ <family lang="und-Rohg">
+ <font weight="400" style="normal">NotoSansHanifiRohingya-Regular.otf</font>
+ </family>
+ <family lang="und-Khoj">
+ <font weight="400" style="normal">NotoSansKhojki-Regular.otf</font>
+ </family>
+ <family lang="und-Gonm">
+ <font weight="400" style="normal">NotoSansMasaramGondi-Regular.otf</font>
+ </family>
+ <family lang="und-Wcho">
+ <font weight="400" style="normal">NotoSansWancho-Regular.otf</font>
+ </family>
+ <family lang="und-Wara">
+ <font weight="400" style="normal">NotoSansWarangCiti-Regular.otf</font>
+ </family>
+ <family lang="und-Gran">
+ <font weight="400" style="normal">NotoSansGrantha-Regular.ttf</font>
+ </family>
+ <family lang="und-Modi">
+ <font weight="400" style="normal">NotoSansModi-Regular.ttf</font>
+ </family>
+ <family lang="und-Dogr">
+ <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font>
+ </family>
+ <family lang="und-Medf">
+ <font weight="400" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
+ NotoSansMedefaidrin-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
+ NotoSansMedefaidrin-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
+ NotoSansMedefaidrin-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
+ NotoSansMedefaidrin-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Soyo">
+ <font weight="400" style="normal" postScriptName="NotoSansSoyombo-Regular">
+ NotoSansSoyombo-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansSoyombo-Regular">
+ NotoSansSoyombo-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansSoyombo-Regular">
+ NotoSansSoyombo-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansSoyombo-Regular">
+ NotoSansSoyombo-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Takr">
+ <font weight="400" style="normal" postScriptName="NotoSansTakri-Regular">
+ NotoSansTakri-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansTakri-Regular">
+ NotoSansTakri-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansTakri-Regular">
+ NotoSansTakri-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansTakri-Regular">
+ NotoSansTakri-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Hmnp">
+ <font weight="400" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
+ NotoSerifNyiakengPuachueHmong-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
+ NotoSerifNyiakengPuachueHmong-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
+ NotoSerifNyiakengPuachueHmong-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
+ NotoSerifNyiakengPuachueHmong-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+ <family lang="und-Yezi">
+ <font weight="400" style="normal" postScriptName="NotoSerifYezidi-Regular">
+ NotoSerifYezidi-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSerifYezidi-Regular">
+ NotoSerifYezidi-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSerifYezidi-Regular">
+ NotoSerifYezidi-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSerifYezidi-Regular">
+ NotoSerifYezidi-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ </family>
+</familyset>
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 45540e0..4cdc06a 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -162,6 +162,7 @@
"com_android_wm_shell_flags_lib",
"com.android.window.flags.window-aconfig-java",
"WindowManager-Shell-proto",
+ "perfetto_trace_java_protos",
"dagger2",
"jsr330",
],
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 901d5fa..0e04658 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -1,13 +1,6 @@
package: "com.android.wm.shell"
flag {
- name: "example_flag"
- namespace: "multitasking"
- description: "An Example Flag"
- bug: "300136750"
-}
-
-flag {
name: "enable_app_pairs"
namespace: "multitasking"
description: "Enables the ability to create and save app pairs to the Home screen"
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index e4abae4..9854e58 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -134,6 +134,13 @@
<!-- Whether the additional education about reachability is enabled -->
<bool name="config_letterboxIsReachabilityEducationEnabled">false</bool>
+ <!-- The minimum tolerance of the percentage of activity bounds within its task to hide
+ size compat restart button. Value lower than 0 or higher than 100 will be ignored.
+ 100 is the default value where the activity has to fit exactly within the task to allow
+ size compat restart button to be hidden. 0 means size compat restart button will always
+ be hidden. -->
+ <integer name="config_letterboxRestartButtonHideTolerance">100</integer>
+
<!-- Whether DragAndDrop capability is enabled -->
<bool name="config_enableShellDragDrop">true</bool>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
index 09d99b2..fa2e236 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java
@@ -71,6 +71,8 @@
private static final String HAS_SEEN_VERTICAL_REACHABILITY_EDUCATION_KEY_PREFIX =
"has_seen_vertical_reachability_education";
+ private static final int MAX_PERCENTAGE_VAL = 100;
+
/**
* The {@link SharedPreferences} instance for the restart dialog and the reachability
* education.
@@ -82,6 +84,12 @@
*/
private final SharedPreferences mLetterboxEduSharedPreferences;
+ /**
+ * The minimum tolerance of the percentage of activity bounds within its task to hide
+ * size compat restart button.
+ */
+ private final int mHideSizeCompatRestartButtonTolerance;
+
// Whether the extended restart dialog is enabled
private boolean mIsRestartDialogEnabled;
@@ -106,6 +114,9 @@
R.bool.config_letterboxIsRestartDialogEnabled);
mIsReachabilityEducationEnabled = context.getResources().getBoolean(
R.bool.config_letterboxIsReachabilityEducationEnabled);
+ final int tolerance = context.getResources().getInteger(
+ R.integer.config_letterboxRestartButtonHideTolerance);
+ mHideSizeCompatRestartButtonTolerance = getHideSizeCompatRestartButtonTolerance(tolerance);
mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG,
DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG);
@@ -179,6 +190,10 @@
|| !hasSeenVerticalReachabilityEducation(taskInfo));
}
+ int getHideSizeCompatRestartButtonTolerance() {
+ return mHideSizeCompatRestartButtonTolerance;
+ }
+
boolean getHasSeenLetterboxEducation(int userId) {
return mLetterboxEduSharedPreferences
.getBoolean(dontShowLetterboxEduKey(userId), /* default= */ false);
@@ -218,6 +233,15 @@
}
}
+ // Returns the minimum tolerance of the percentage of activity bounds within its task to hide
+ // size compat restart button. Value lower than 0 or higher than 100 will be ignored.
+ // 100 is the default value where the activity has to fit exactly within the task to allow
+ // size compat restart button to be hidden. 0 means size compat restart button will always
+ // be hidden.
+ private int getHideSizeCompatRestartButtonTolerance(int tolerance) {
+ return tolerance < 0 || tolerance > MAX_PERCENTAGE_VAL ? MAX_PERCENTAGE_VAL : tolerance;
+ }
+
private boolean isReachabilityEducationEnabled() {
return mIsReachabilityEducationOverrideEnabled || (mIsReachabilityEducationEnabled
&& mIsLetterboxReachabilityEducationAllowed);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 00e0cdb..2dd2743 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -22,7 +22,9 @@
import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppCompatTaskInfo;
import android.app.AppCompatTaskInfo.CameraCompatControlState;
import android.app.TaskInfo;
import android.content.Context;
@@ -33,6 +35,7 @@
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayLayout;
@@ -68,6 +71,8 @@
@VisibleForTesting
CompatUILayout mLayout;
+ private final float mHideScmTolerance;
+
CompatUIWindowManager(Context context, TaskInfo taskInfo,
SyncTransactionQueue syncQueue, CompatUICallback callback,
ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
@@ -75,11 +80,13 @@
Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartButtonClicked) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mCallback = callback;
- mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
+ mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat
+ && shouldShowSizeCompatRestartButton(taskInfo);
mCameraCompatControlState = taskInfo.appCompatTaskInfo.cameraCompatControlState;
mCompatUIHintsState = compatUIHintsState;
mCompatUIConfiguration = compatUIConfiguration;
mOnRestartButtonClicked = onRestartButtonClicked;
+ mHideScmTolerance = mCompatUIConfiguration.getHideSizeCompatRestartButtonTolerance();
}
@Override
@@ -107,6 +114,11 @@
mLayout = inflateLayout();
mLayout.inject(this);
+ final TaskInfo taskInfo = getLastTaskInfo();
+ if (taskInfo != null) {
+ mHasSizeCompat = mHasSizeCompat && shouldShowSizeCompatRestartButton(taskInfo);
+ }
+
updateVisibilityOfViews();
if (mHasSizeCompat) {
@@ -127,7 +139,8 @@
boolean canShow) {
final boolean prevHasSizeCompat = mHasSizeCompat;
final int prevCameraCompatControlState = mCameraCompatControlState;
- mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
+ mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat
+ && shouldShowSizeCompatRestartButton(taskInfo);
mCameraCompatControlState = taskInfo.appCompatTaskInfo.cameraCompatControlState;
if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) {
@@ -208,6 +221,30 @@
updateSurfacePosition(positionX, positionY);
}
+ @VisibleForTesting
+ boolean shouldShowSizeCompatRestartButton(@NonNull TaskInfo taskInfo) {
+ if (!Flags.allowHideScmButton()) {
+ return true;
+ }
+ final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo;
+ final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
+ final int letterboxArea = computeArea(appCompatTaskInfo.topActivityLetterboxWidth,
+ appCompatTaskInfo.topActivityLetterboxHeight);
+ final int taskArea = computeArea(taskBounds.width(), taskBounds.height());
+ if (letterboxArea == 0 || taskArea == 0) {
+ return false;
+ }
+ final float percentageAreaOfLetterboxInTask = (float) letterboxArea / taskArea * 100;
+ return percentageAreaOfLetterboxInTask < mHideScmTolerance;
+ }
+
+ private int computeArea(int width, int height) {
+ if (width == 0 || height == 0) {
+ return 0;
+ }
+ return width * height;
+ }
+
private void updateVisibilityOfViews() {
if (mLayout == null) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index 95d7ad5..c3a82ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -99,7 +99,11 @@
windowDecoration: DesktopModeWindowDecoration
) {
if (inProgress) {
- error("A drag to desktop is already in progress")
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DragToDesktop: Drag to desktop transition already in progress."
+ )
+ return
}
val options = ActivityOptions.makeBasic().apply {
@@ -144,6 +148,12 @@
* inside the desktop drop zone.
*/
fun finishDragToDesktopTransition(wct: WindowContainerTransaction) {
+ if (!inProgress) {
+ // Don't attempt to finish a drag to desktop transition since there is no transition in
+ // progress which means that the drag to desktop transition was never successfully
+ // started.
+ return
+ }
if (requireTransitionState().startAborted) {
// Don't attempt to complete the drag-to-desktop since the start transition didn't
// succeed as expected. Just reset the state as if nothing happened.
@@ -161,6 +171,12 @@
* means the user wants to remain in their current windowing mode.
*/
fun cancelDragToDesktopTransition() {
+ if (!inProgress) {
+ // Don't attempt to cancel a drag to desktop transition since there is no transition in
+ // progress which means that the drag to desktop transition was never successfully
+ // started.
+ return
+ }
val state = requireTransitionState()
if (state.startAborted) {
// Don't attempt to cancel the drag-to-desktop since the start transition didn't
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index b0d8b47..3fb0dbf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -83,6 +83,9 @@
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.tracing.LegacyTransitionTracer;
+import com.android.wm.shell.transition.tracing.PerfettoTransitionTracer;
+import com.android.wm.shell.transition.tracing.TransitionTracer;
import com.android.wm.shell.util.TransitionUtil;
import java.io.PrintWriter;
@@ -184,7 +187,7 @@
private final ShellController mShellController;
private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
private final SleepHandler mSleepHandler = new SleepHandler();
- private final Tracer mTracer = new Tracer();
+ private final TransitionTracer mTransitionTracer;
private boolean mIsRegistered = false;
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
@@ -307,6 +310,12 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
shellInit.addInitCallback(this::onInit, this);
mHomeTransitionObserver = observer;
+
+ if (android.tracing.Flags.perfettoTransitionTracing()) {
+ mTransitionTracer = new PerfettoTransitionTracer();
+ } else {
+ mTransitionTracer = new LegacyTransitionTracer();
+ }
}
private void onInit() {
@@ -868,7 +877,7 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
+ " %s is still animating. Notify the animating transition"
+ " in case they can be merged", ready, playing);
- mTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
+ mTransitionTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT,
playing.mToken, (wct) -> onMerged(playing, ready));
}
@@ -902,7 +911,7 @@
for (int i = 0; i < mObservers.size(); ++i) {
mObservers.get(i).onTransitionMerged(merged.mToken, playing.mToken);
}
- mTracer.logMerged(merged.mInfo.getDebugId(), playing.mInfo.getDebugId());
+ mTransitionTracer.logMerged(merged.mInfo.getDebugId(), playing.mInfo.getDebugId());
// See if we should merge another transition.
processReadyQueue(track);
}
@@ -923,7 +932,7 @@
active.mStartT, active.mFinishT, (wct) -> onFinish(active, wct));
if (consumed) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
- mTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
+ mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
return;
}
}
@@ -948,7 +957,7 @@
if (consumed) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
mHandlers.get(i));
- mTracer.logDispatched(info.getDebugId(), mHandlers.get(i));
+ mTransitionTracer.logDispatched(info.getDebugId(), mHandlers.get(i));
return mHandlers.get(i);
}
}
@@ -978,7 +987,7 @@
final Track track = mTracks.get(transition.getTrack());
transition.mAborted = true;
- mTracer.logAborted(transition.mInfo.getDebugId());
+ mTransitionTracer.logAborted(transition.mInfo.getDebugId());
if (transition.mHandler != null) {
// Notifies to clean-up the aborted transition.
@@ -1506,12 +1515,18 @@
}
}
-
@Override
public boolean onShellCommand(String[] args, PrintWriter pw) {
switch (args[0]) {
case "tracing": {
- mTracer.onShellCommand(Arrays.copyOfRange(args, 1, args.length), pw);
+ if (!android.tracing.Flags.perfettoTransitionTracing()) {
+ ((LegacyTransitionTracer) mTransitionTracer)
+ .onShellCommand(Arrays.copyOfRange(args, 1, args.length), pw);
+ } else {
+ pw.println("Command not supported. Use the Perfetto command instead to start "
+ + "and stop this trace instead.");
+ return false;
+ }
return true;
}
default: {
@@ -1524,8 +1539,10 @@
@Override
public void printShellCommandHelp(PrintWriter pw, String prefix) {
- pw.println(prefix + "tracing");
- mTracer.printShellCommandHelp(pw, prefix + " ");
+ if (!android.tracing.Flags.perfettoTransitionTracing()) {
+ pw.println(prefix + "tracing");
+ ((LegacyTransitionTracer) mTransitionTracer).printShellCommandHelp(pw, prefix + " ");
+ }
}
private void dump(@NonNull PrintWriter pw, String prefix) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/LegacyTransitionTracer.java
similarity index 88%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/LegacyTransitionTracer.java
index 5919aad..9c84886 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/LegacyTransitionTracer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.transition;
+package com.android.wm.shell.transition.tracing;
import static android.os.Build.IS_USER;
@@ -29,6 +29,7 @@
import com.android.internal.util.TraceBuffer;
import com.android.wm.shell.sysui.ShellCommandHandler;
+import com.android.wm.shell.transition.Transitions;
import com.google.protobuf.nano.MessageNano;
@@ -45,7 +46,8 @@
/**
* Helper class to collect and dump transition traces.
*/
-public class Tracer implements ShellCommandHandler.ShellCommandActionHandler {
+public class LegacyTransitionTracer
+ implements ShellCommandHandler.ShellCommandActionHandler, TransitionTracer {
private static final int ALWAYS_ON_TRACING_CAPACITY = 15 * 1024; // 15 KB
private static final int ACTIVE_TRACING_BUFFER_CAPACITY = 5000 * 1024; // 5 MB
@@ -60,33 +62,33 @@
private final TraceBuffer.ProtoProvider mProtoProvider =
new TraceBuffer.ProtoProvider<MessageNano,
- com.android.wm.shell.nano.WmShellTransitionTraceProto,
- com.android.wm.shell.nano.Transition>() {
- @Override
- public int getItemSize(MessageNano proto) {
- return proto.getCachedSize();
- }
+ com.android.wm.shell.nano.WmShellTransitionTraceProto,
+ com.android.wm.shell.nano.Transition>() {
+ @Override
+ public int getItemSize(MessageNano proto) {
+ return proto.getCachedSize();
+ }
- @Override
- public byte[] getBytes(MessageNano proto) {
- return MessageNano.toByteArray(proto);
- }
+ @Override
+ public byte[] getBytes(MessageNano proto) {
+ return MessageNano.toByteArray(proto);
+ }
- @Override
- public void write(
- com.android.wm.shell.nano.WmShellTransitionTraceProto encapsulatingProto,
- Queue<com.android.wm.shell.nano.Transition> buffer, OutputStream os)
+ @Override
+ public void write(
+ com.android.wm.shell.nano.WmShellTransitionTraceProto encapsulatingProto,
+ Queue<com.android.wm.shell.nano.Transition> buffer, OutputStream os)
throws IOException {
- encapsulatingProto.transitions = buffer.toArray(
- new com.android.wm.shell.nano.Transition[0]);
- os.write(getBytes(encapsulatingProto));
- }
- };
+ encapsulatingProto.transitions = buffer.toArray(
+ new com.android.wm.shell.nano.Transition[0]);
+ os.write(getBytes(encapsulatingProto));
+ }
+ };
private final TraceBuffer<MessageNano,
com.android.wm.shell.nano.WmShellTransitionTraceProto,
- com.android.wm.shell.nano.Transition> mTraceBuffer
- = new TraceBuffer(ALWAYS_ON_TRACING_CAPACITY, mProtoProvider,
- (proto) -> handleOnEntryRemovedFromTrace(proto));
+ com.android.wm.shell.nano.Transition> mTraceBuffer =
+ new TraceBuffer(ALWAYS_ON_TRACING_CAPACITY, mProtoProvider,
+ this::handleOnEntryRemovedFromTrace);
private final Map<Object, Runnable> mRemovedFromTraceCallbacks = new HashMap<>();
private final Map<Transitions.TransitionHandler, Integer> mHandlerIds = new HashMap<>();
@@ -99,6 +101,7 @@
* @param transitionId The id of the transition being dispatched.
* @param handler The handler the transition is being dispatched to.
*/
+ @Override
public void logDispatched(int transitionId, Transitions.TransitionHandler handler) {
final int handlerId;
if (mHandlerIds.containsKey(handler)) {
@@ -130,6 +133,7 @@
*
* @param mergeRequestedTransitionId The id of the transition we are requesting to be merged.
*/
+ @Override
public void logMergeRequested(int mergeRequestedTransitionId, int playingTransitionId) {
com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition();
proto.id = mergeRequestedTransitionId;
@@ -145,6 +149,7 @@
* @param mergedTransitionId The id of the transition that was merged.
* @param playingTransitionId The id of the transition the transition was merged into.
*/
+ @Override
public void logMerged(int mergedTransitionId, int playingTransitionId) {
com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition();
proto.id = mergedTransitionId;
@@ -159,6 +164,7 @@
*
* @param transitionId The id of the transition that was aborted.
*/
+ @Override
public void logAborted(int transitionId) {
com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition();
proto.id = transitionId;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
new file mode 100644
index 0000000..99df6a3
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition.tracing;
+
+import android.internal.perfetto.protos.PerfettoTrace;
+import android.os.SystemClock;
+import android.tracing.perfetto.DataSourceInstance;
+import android.tracing.perfetto.DataSourceParams;
+import android.tracing.perfetto.InitArguments;
+import android.tracing.perfetto.Producer;
+import android.tracing.perfetto.TracingContext;
+import android.tracing.transition.TransitionDataSource;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Helper class to collect and dump transition traces.
+ */
+public class PerfettoTransitionTracer implements TransitionTracer {
+ private final AtomicInteger mActiveTraces = new AtomicInteger(0);
+ private final TransitionDataSource mDataSource = new TransitionDataSource(
+ mActiveTraces::incrementAndGet,
+ this::onFlush,
+ mActiveTraces::decrementAndGet);
+
+ public PerfettoTransitionTracer() {
+ Producer.init(InitArguments.DEFAULTS);
+ mDataSource.register(DataSourceParams.DEFAULTS);
+ }
+
+ /**
+ * Adds an entry in the trace to log that a transition has been dispatched to a handler.
+ *
+ * @param transitionId The id of the transition being dispatched.
+ * @param handler The handler the transition is being dispatched to.
+ */
+ @Override
+ public void logDispatched(int transitionId, Transitions.TransitionHandler handler) {
+ if (!isTracing()) {
+ return;
+ }
+
+ mDataSource.trace(ctx -> {
+ final int handlerId = getHandlerId(handler, ctx);
+
+ final ProtoOutputStream os = ctx.newTracePacket();
+ final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
+ os.write(PerfettoTrace.ShellTransition.ID, transitionId);
+ os.write(PerfettoTrace.ShellTransition.DISPATCH_TIME_NS,
+ SystemClock.elapsedRealtimeNanos());
+ os.write(PerfettoTrace.ShellTransition.HANDLER, handlerId);
+ os.end(token);
+ });
+ }
+
+ private static int getHandlerId(Transitions.TransitionHandler handler,
+ TracingContext<DataSourceInstance, TransitionDataSource.TlsState, Void> ctx) {
+ final Map<String, Integer> handlerMapping =
+ ctx.getCustomTlsState().handlerMapping;
+ final int handlerId;
+ if (handlerMapping.containsKey(handler.getClass().getName())) {
+ handlerId = handlerMapping.get(handler.getClass().getName());
+ } else {
+ // + 1 to avoid 0 ids which can be confused with missing value when dumped to proto
+ handlerId = handlerMapping.size() + 1;
+ handlerMapping.put(handler.getClass().getName(), handlerId);
+ }
+ return handlerId;
+ }
+
+ /**
+ * Adds an entry in the trace to log that a request to merge a transition was made.
+ *
+ * @param mergeRequestedTransitionId The id of the transition we are requesting to be merged.
+ */
+ @Override
+ public void logMergeRequested(int mergeRequestedTransitionId, int playingTransitionId) {
+ if (!isTracing()) {
+ return;
+ }
+
+ mDataSource.trace(ctx -> {
+ final ProtoOutputStream os = ctx.newTracePacket();
+ final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
+ os.write(PerfettoTrace.ShellTransition.ID, mergeRequestedTransitionId);
+ os.write(PerfettoTrace.ShellTransition.MERGE_REQUEST_TIME_NS,
+ SystemClock.elapsedRealtimeNanos());
+ os.write(PerfettoTrace.ShellTransition.MERGE_TARGET, playingTransitionId);
+ os.end(token);
+ });
+ }
+
+ /**
+ * Adds an entry in the trace to log that a transition was merged by the handler.
+ *
+ * @param mergedTransitionId The id of the transition that was merged.
+ * @param playingTransitionId The id of the transition the transition was merged into.
+ */
+ @Override
+ public void logMerged(int mergedTransitionId, int playingTransitionId) {
+ if (!isTracing()) {
+ return;
+ }
+
+ mDataSource.trace(ctx -> {
+ final ProtoOutputStream os = ctx.newTracePacket();
+ final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
+ os.write(PerfettoTrace.ShellTransition.ID, mergedTransitionId);
+ os.write(PerfettoTrace.ShellTransition.MERGE_TIME_NS,
+ SystemClock.elapsedRealtimeNanos());
+ os.write(PerfettoTrace.ShellTransition.MERGE_TARGET, playingTransitionId);
+ os.end(token);
+ });
+ }
+
+ /**
+ * Adds an entry in the trace to log that a transition was aborted.
+ *
+ * @param transitionId The id of the transition that was aborted.
+ */
+ @Override
+ public void logAborted(int transitionId) {
+ if (!isTracing()) {
+ return;
+ }
+
+ mDataSource.trace(ctx -> {
+ final ProtoOutputStream os = ctx.newTracePacket();
+ final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION);
+ os.write(PerfettoTrace.ShellTransition.ID, transitionId);
+ os.write(PerfettoTrace.ShellTransition.SHELL_ABORT_TIME_NS,
+ SystemClock.elapsedRealtimeNanos());
+ os.end(token);
+ });
+ }
+
+ private boolean isTracing() {
+ return mActiveTraces.get() > 0;
+ }
+
+ private void onFlush() {
+ mDataSource.trace(ctx -> {
+ final ProtoOutputStream os = ctx.newTracePacket();
+
+ final Map<String, Integer> handlerMapping = ctx.getCustomTlsState().handlerMapping;
+ for (String handler : handlerMapping.keySet()) {
+ final long token = os.start(PerfettoTrace.TracePacket.SHELL_HANDLER_MAPPINGS);
+ os.write(PerfettoTrace.ShellHandlerMapping.ID, handlerMapping.get(handler));
+ os.write(PerfettoTrace.ShellHandlerMapping.NAME, handler);
+ os.end(token);
+ }
+
+ ctx.flush();
+ });
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/TransitionTracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/TransitionTracer.java
new file mode 100644
index 0000000..5857ad8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/TransitionTracer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition.tracing;
+
+import com.android.wm.shell.transition.Transitions;
+
+public interface TransitionTracer {
+ /**
+ * Adds an entry in the trace to log that a transition has been dispatched to a handler.
+ *
+ * @param transitionId The id of the transition being dispatched.
+ * @param handler The handler the transition is being dispatched to.
+ */
+ void logDispatched(int transitionId, Transitions.TransitionHandler handler);
+
+ /**
+ * Adds an entry in the trace to log that a request to merge a transition was made.
+ *
+ * @param mergeRequestedTransitionId The id of the transition we are requesting to be merged.
+ */
+ void logMergeRequested(int mergeRequestedTransitionId, int playingTransitionId);
+
+ /**
+ * Adds an entry in the trace to log that a transition was merged by the handler.
+ *
+ * @param mergedTransitionId The id of the transition that was merged.
+ * @param playingTransitionId The id of the transition the transition was merged into.
+ */
+ void logMerged(int mergedTransitionId, int playingTransitionId);
+
+ /**
+ * Adds an entry in the trace to log that a transition was aborted.
+ *
+ * @param transitionId The id of the transition that was aborted.
+ */
+ void logAborted(int transitionId);
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index d4b97ed..2acfd83 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -20,6 +20,8 @@
import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.WindowInsets.Type.navigationBars;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -27,6 +29,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
@@ -39,6 +42,7 @@
import android.app.TaskInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import android.view.DisplayInfo;
@@ -50,6 +54,7 @@
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
@@ -59,6 +64,7 @@
import junit.framework.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -76,6 +82,8 @@
@RunWith(AndroidTestingRunner.class)
@SmallTest
public class CompatUIWindowManagerTest extends ShellTestCase {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
private static final int TASK_ID = 1;
@@ -107,6 +115,7 @@
public void testCreateSizeCompatButton() {
// Doesn't create layout if show is false.
mWindowManager.mHasSizeCompat = true;
+ doReturn(true).when(mWindowManager).shouldShowSizeCompatRestartButton(mTaskInfo);
assertTrue(mWindowManager.createLayout(/* canShow= */ false));
verify(mWindowManager, never()).inflateLayout();
@@ -199,6 +208,7 @@
// No diff
clearInvocations(mWindowManager);
TaskInfo taskInfo = createTaskInfo(/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN);
+ doReturn(true).when(mWindowManager).shouldShowSizeCompatRestartButton(any());
assertTrue(mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true));
verify(mWindowManager, never()).updateSurfacePosition();
@@ -284,6 +294,7 @@
@Test
public void testUpdateCompatInfoLayoutNotInflatedYet() {
mWindowManager.mHasSizeCompat = true;
+ doReturn(true).when(mWindowManager).shouldShowSizeCompatRestartButton(any());
mWindowManager.createLayout(/* canShow= */ false);
verify(mWindowManager, never()).inflateLayout();
@@ -353,6 +364,7 @@
// Create button if it is not created.
mWindowManager.mLayout = null;
mWindowManager.mHasSizeCompat = true;
+ doReturn(true).when(mWindowManager).shouldShowSizeCompatRestartButton(mTaskInfo);
mWindowManager.updateVisibility(/* canShow= */ true);
verify(mWindowManager).createLayout(/* canShow= */ true);
@@ -464,6 +476,37 @@
Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener));
}
+ @Test
+ public void testShouldShowSizeCompatRestartButton() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_HIDE_SCM_BUTTON);
+
+ doReturn(86).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance();
+ mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
+ mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
+ mCompatUIConfiguration, mOnRestartButtonClicked);
+
+ // Simulate rotation of activity in square display
+ TaskInfo taskInfo = createTaskInfo(true, CAMERA_COMPAT_CONTROL_HIDDEN);
+ taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 2000, 2000));
+ taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = 2000;
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1850;
+
+ assertFalse(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo));
+
+ // Simulate exiting split screen/folding
+ taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1000;
+ assertTrue(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo));
+
+ // Simulate folding
+ taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 1000, 2000));
+ assertFalse(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo));
+
+ taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1000;
+ taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = 500;
+ assertTrue(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo));
+ }
+
private static TaskInfo createTaskInfo(boolean hasSizeCompat,
@AppCompatTaskInfo.CameraCompatControlState int cameraCompatControlState) {
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index 3bc90ad..be639e8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -34,6 +34,7 @@
import org.mockito.Mock
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
+import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyZeroInteractions
import org.mockito.kotlin.whenever
@@ -150,6 +151,23 @@
}
@Test
+ fun startDragToDesktop_anotherTransitionInProgress_startDropped() {
+ val task = createTask()
+ val dragAnimator = mock<MoveToDesktopAnimator>()
+
+ // Simulate attempt to start two drag to desktop transitions.
+ startDragToDesktopTransition(task, dragAnimator)
+ startDragToDesktopTransition(task, dragAnimator)
+
+ // Verify transition only started once.
+ verify(transitions, times(1)).startTransition(
+ eq(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP),
+ any(),
+ eq(handler)
+ )
+ }
+
+ @Test
fun cancelDragToDesktop_startWasReady_cancel() {
val task = createTask()
val dragAnimator = mock<MoveToDesktopAnimator>()
@@ -189,6 +207,32 @@
verifyZeroInteractions(dragAnimator)
}
+ @Test
+ fun cancelDragToDesktop_transitionNotInProgress_dropCancel() {
+ // Then cancel is called before the transition was started.
+ handler.cancelDragToDesktopTransition()
+
+ // Verify cancel is dropped.
+ verify(transitions, never()).startTransition(
+ eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP),
+ any(),
+ eq(handler)
+ )
+ }
+
+ @Test
+ fun finishDragToDesktop_transitionNotInProgress_dropFinish() {
+ // Then finish is called before the transition was started.
+ handler.finishDragToDesktopTransition(WindowContainerTransaction())
+
+ // Verify finish is dropped.
+ verify(transitions, never()).startTransition(
+ eq(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP),
+ any(),
+ eq(handler)
+ )
+ }
+
private fun startDragToDesktopTransition(
task: RunningTaskInfo,
dragAnimator: MoveToDesktopAnimator
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c9d5e07..d9166a1 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <cstddef>
#include <limits>
+#include <optional>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
@@ -50,7 +51,9 @@
// contiguous block of memory to store both the TypeSpec struct and
// the Type structs.
struct TypeSpecBuilder {
- explicit TypeSpecBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header) : header_(header) {}
+ explicit TypeSpecBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header) : header_(header) {
+ type_entries.reserve(dtohs(header_->typesCount));
+ }
void AddType(incfs::verified_map_ptr<ResTable_type> type) {
TypeSpec::TypeEntry& entry = type_entries.emplace_back();
@@ -59,6 +62,7 @@
}
TypeSpec Build() {
+ type_entries.shrink_to_fit();
return {header_, std::move(type_entries)};
}
@@ -450,7 +454,8 @@
std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
package_property_t property_flags) {
ATRACE_NAME("LoadedPackage::Load");
- std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
+ const bool optimize_name_lookups = (property_flags & PROPERTY_OPTIMIZE_NAME_LOOKUPS) != 0;
+ std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage(optimize_name_lookups));
// typeIdOffset was added at some point, but we still must recognize apps built before this
// was added.
@@ -499,7 +504,7 @@
// A map of TypeSpec builders, each associated with an type index.
// We use these to accumulate the set of Types available for a TypeSpec, and later build a single,
// contiguous block of memory that holds all the Types together with the TypeSpec.
- std::unordered_map<int, std::unique_ptr<TypeSpecBuilder>> type_builder_map;
+ std::unordered_map<int, std::optional<TypeSpecBuilder>> type_builder_map;
ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
while (iter.HasNext()) {
@@ -567,14 +572,14 @@
return {};
}
- if (entry_count * sizeof(uint32_t) > chunk.data_size()) {
+ if (entry_count * sizeof(uint32_t) > child_chunk.data_size()) {
LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small to hold entries.";
return {};
}
- std::unique_ptr<TypeSpecBuilder>& builder_ptr = type_builder_map[type_spec->id];
- if (builder_ptr == nullptr) {
- builder_ptr = util::make_unique<TypeSpecBuilder>(type_spec.verified());
+ auto& maybe_type_builder = type_builder_map[type_spec->id];
+ if (!maybe_type_builder) {
+ maybe_type_builder.emplace(type_spec.verified());
loaded_package->resource_ids_.set(type_spec->id, entry_count);
} else {
LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
@@ -594,9 +599,9 @@
}
// Type chunks must be preceded by their TypeSpec chunks.
- std::unique_ptr<TypeSpecBuilder>& builder_ptr = type_builder_map[type->id];
- if (builder_ptr != nullptr) {
- builder_ptr->AddType(type.verified());
+ auto& maybe_type_builder = type_builder_map[type->id];
+ if (maybe_type_builder) {
+ maybe_type_builder->AddType(type.verified());
} else {
LOG(ERROR) << StringPrintf(
"RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.",
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 4c992be..2c99f1a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -447,15 +447,19 @@
// --------------------------------------------------------------------
// --------------------------------------------------------------------
-ResStringPool::ResStringPool()
- : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
-{
+ResStringPool::ResStringPool() : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL) {
}
-ResStringPool::ResStringPool(const void* data, size_t size, bool copyData)
- : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
-{
- setTo(data, size, copyData);
+ResStringPool::ResStringPool(bool optimize_name_lookups) : ResStringPool() {
+ if (optimize_name_lookups) {
+ mIndexLookupCache.emplace();
+ }
+}
+
+ResStringPool::ResStringPool(const void* data, size_t size, bool copyData,
+ bool optimize_name_lookups)
+ : ResStringPool(optimize_name_lookups) {
+ setTo(data, size, copyData);
}
ResStringPool::~ResStringPool()
@@ -683,6 +687,14 @@
mStylePoolSize = 0;
}
+ if (mIndexLookupCache) {
+ if ((mHeader->flags & ResStringPool_header::UTF8_FLAG) != 0) {
+ mIndexLookupCache->first.reserve(mHeader->stringCount);
+ } else {
+ mIndexLookupCache->second.reserve(mHeader->stringCount);
+ }
+ }
+
return (mError=NO_ERROR);
}
@@ -708,6 +720,10 @@
free(mOwnedData);
mOwnedData = NULL;
}
+ if (mIndexLookupCache) {
+ mIndexLookupCache->first.clear();
+ mIndexLookupCache->second.clear();
+ }
}
/**
@@ -824,11 +840,11 @@
// encLen must be less than 0x7FFF due to encoding.
if ((uint32_t)(u8str+*u8len-strings) < mStringPoolSize) {
- AutoMutex lock(mDecodeLock);
+ AutoMutex lock(mCachesLock);
- if (mCache != NULL && mCache[idx] != NULL) {
- return StringPiece16(mCache[idx], *u16len);
- }
+ if (mCache != NULL && mCache[idx] != NULL) {
+ return StringPiece16(mCache[idx], *u16len);
+ }
// Retrieve the actual length of the utf8 string if the
// encoded length was truncated
@@ -1093,12 +1109,24 @@
// block, start searching at the back.
String8 str8(str, strLen);
const size_t str8Len = str8.size();
+ std::optional<AutoMutex> cacheLock;
+ if (mIndexLookupCache) {
+ cacheLock.emplace(mCachesLock);
+ if (auto it = mIndexLookupCache->first.find(std::string_view(str8));
+ it != mIndexLookupCache->first.end()) {
+ return it->second;
+ }
+ }
+
for (int i=mHeader->stringCount-1; i>=0; i--) {
const base::expected<StringPiece, NullOrIOError> s = string8At(i);
if (UNLIKELY(IsIOError(s))) {
return base::unexpected(s.error());
}
if (s.has_value()) {
+ if (mIndexLookupCache) {
+ mIndexLookupCache->first.insert({*s, i});
+ }
if (kDebugStringPoolNoisy) {
ALOGI("Looking at %s, i=%d\n", s->data(), i);
}
@@ -1151,20 +1179,32 @@
// most often this happens because we want to get IDs for style
// span tags; since those always appear at the end of the string
// block, start searching at the back.
+ std::optional<AutoMutex> cacheLock;
+ if (mIndexLookupCache) {
+ cacheLock.emplace(mCachesLock);
+ if (auto it = mIndexLookupCache->second.find({str, strLen});
+ it != mIndexLookupCache->second.end()) {
+ return it->second;
+ }
+ }
for (int i=mHeader->stringCount-1; i>=0; i--) {
const base::expected<StringPiece16, NullOrIOError> s = stringAt(i);
if (UNLIKELY(IsIOError(s))) {
return base::unexpected(s.error());
}
if (kDebugStringPoolNoisy) {
- ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).c_str(), i);
+ ALOGI("Looking16 at %s, i=%d\n", String8(s->data(), s->size()).c_str(), i);
}
- if (s.has_value() && strLen == s->size() &&
- strzcmp16(s->data(), s->size(), str, strLen) == 0) {
+ if (s.has_value()) {
+ if (mIndexLookupCache) {
+ mIndexLookupCache->second.insert({*s, i});
+ }
+ if (strLen == s->size() && strzcmp16(s->data(), s->size(), str, strLen) == 0) {
if (kDebugStringPoolNoisy) {
- ALOGI("MATCH!");
+ ALOGI("MATCH16!");
}
return i;
+ }
}
}
}
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index 6068912..d9f7c2a 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -71,7 +71,7 @@
// Rewrites a compile-time overlay resource id to the runtime resource id of corresponding target
// resource.
- virtual status_t lookupResourceIdNoRewrite(uint32_t* resId) const;
+ status_t lookupResourceIdNoRewrite(uint32_t* resId) const;
const Idmap_data_header* data_header_;
const Idmap_overlay_entry* entries_;
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 3a72871..413b278 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -99,6 +99,9 @@
// The apk assets only contain the overlayable declarations information.
PROPERTY_ONLY_OVERLAYABLES = 1U << 5U,
+
+ // Optimize the resource lookups by name via an in-memory lookup table.
+ PROPERTY_OPTIMIZE_NAME_LOOKUPS = 1U << 6U,
};
struct OverlayableInfo {
@@ -285,7 +288,9 @@
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
- LoadedPackage() = default;
+ explicit LoadedPackage(bool optimize_name_lookups = false)
+ : type_string_pool_(optimize_name_lookups), key_string_pool_(optimize_name_lookups) {
+ }
ResStringPool type_string_pool_;
ResStringPool key_string_pool_;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index c0514fd..3d1403d 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -22,27 +22,28 @@
#include <android-base/expected.h>
#include <android-base/unique_fd.h>
-
+#include <android/configuration.h>
#include <androidfw/Asset.h>
#include <androidfw/Errors.h>
#include <androidfw/LocaleData.h>
#include <androidfw/StringPiece.h>
#include <utils/ByteOrder.h>
#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
#include <utils/String16.h>
#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
-
#include <utils/threads.h>
#include <stdint.h>
#include <sys/types.h>
-#include <android/configuration.h>
-
#include <array>
#include <map>
#include <memory>
+#include <optional>
+#include <string_view>
+#include <unordered_map>
+#include <utility>
namespace android {
@@ -512,23 +513,24 @@
class ResStringPool
{
public:
- ResStringPool();
- ResStringPool(const void* data, size_t size, bool copyData=false);
- virtual ~ResStringPool();
+ ResStringPool();
+ explicit ResStringPool(bool optimize_name_lookups);
+ ResStringPool(const void* data, size_t size, bool copyData = false,
+ bool optimize_name_lookups = false);
+ virtual ~ResStringPool();
- void setToEmpty();
- status_t setTo(incfs::map_ptr<void> data, size_t size, bool copyData=false);
+ void setToEmpty();
+ status_t setTo(incfs::map_ptr<void> data, size_t size, bool copyData = false);
- status_t getError() const;
+ status_t getError() const;
- void uninit();
+ void uninit();
- // Return string entry as UTF16; if the pool is UTF8, the string will
- // be converted before returning.
- inline base::expected<StringPiece16, NullOrIOError> stringAt(
- const ResStringPool_ref& ref) const {
- return stringAt(ref.index);
- }
+ // Return string entry as UTF16; if the pool is UTF8, the string will
+ // be converted before returning.
+ inline base::expected<StringPiece16, NullOrIOError> stringAt(const ResStringPool_ref& ref) const {
+ return stringAt(ref.index);
+ }
virtual base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const;
// Note: returns null if the string pool is not UTF8.
@@ -557,7 +559,7 @@
void* mOwnedData;
incfs::verified_map_ptr<ResStringPool_header> mHeader;
size_t mSize;
- mutable Mutex mDecodeLock;
+ mutable Mutex mCachesLock;
incfs::map_ptr<uint32_t> mEntries;
incfs::map_ptr<uint32_t> mEntryStyles;
incfs::map_ptr<void> mStrings;
@@ -566,6 +568,10 @@
incfs::map_ptr<uint32_t> mStyles;
uint32_t mStylePoolSize; // number of uint32_t
+ mutable std::optional<std::pair<std::unordered_map<std::string_view, int>,
+ std::unordered_map<std::u16string_view, int>>>
+ mIndexLookupCache;
+
base::expected<StringPiece, NullOrIOError> stringDecodeAt(
size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const;
};
@@ -1401,8 +1407,8 @@
// Must be 0.
uint8_t res0;
- // Must be 0.
- uint16_t res1;
+ // Used to be reserved, if >0 specifies the number of ResTable_type entries for this spec.
+ uint16_t typesCount;
// Number of uint32_t entry configuration masks that follow.
uint32_t entryCount;
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index f4ee36ec..bbb1420 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -131,11 +131,6 @@
const std::vector<minikin::FontVariation>& variations) const {
SkFontArguments args;
- int ttcIndex;
- std::unique_ptr<SkStreamAsset> stream(mTypeface->openStream(&ttcIndex));
- LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed");
-
- args.setCollectionIndex(ttcIndex);
std::vector<SkFontArguments::VariationPosition::Coordinate> skVariation;
skVariation.resize(variations.size());
for (size_t i = 0; i < variations.size(); i++) {
@@ -143,11 +138,10 @@
skVariation[i].value = SkFloatToScalar(variations[i].value);
}
args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
- sk_sp<SkFontMgr> fm = android::FreeTypeFontMgr();
- sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), args));
+ sk_sp<SkTypeface> face = mTypeface->makeClone(args);
return std::make_shared<MinikinFontSkia>(std::move(face), mSourceId, mFontData, mFontSize,
- mFilePath, ttcIndex, variations);
+ mFilePath, mTtcIndex, variations);
}
// hinting<<16 | edging<<8 | bools:5bits
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index c5ffbb7..c8d5987 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -118,7 +118,7 @@
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler,
- const HardwareBufferRenderParams& bufferParams) {
+ const HardwareBufferRenderParams& bufferParams, std::mutex& profilerLock) {
if (!isCapturingSkp() && !mHardwareBuffer) {
mEglManager.damageFrame(frame, dirty);
}
@@ -171,6 +171,7 @@
// Draw visual debugging features
if (CC_UNLIKELY(Properties::showDirtyRegions ||
ProfileType::None != Properties::getProfileType())) {
+ std::scoped_lock lock(profilerLock);
SkCanvas* profileCanvas = surface->getCanvas();
SkiaProfileRenderer profileRenderer(profileCanvas, frame.width(), frame.height());
profiler->draw(profileRenderer);
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 098a746..ebe8b6e 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -42,7 +42,8 @@
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler,
- const renderthread::HardwareBufferRenderParams& bufferParams) override;
+ const renderthread::HardwareBufferRenderParams& bufferParams,
+ std::mutex& profilerLock) override;
GrSurfaceOrigin getSurfaceOrigin() override { return kBottomLeft_GrSurfaceOrigin; }
bool swapBuffers(const renderthread::Frame& frame, IRenderPipeline::DrawResult& drawResult,
const SkRect& screenDirty, FrameInfo* currentFrameInfo,
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index d747489..fd0a8e0 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -75,7 +75,7 @@
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler,
- const HardwareBufferRenderParams& bufferParams) {
+ const HardwareBufferRenderParams& bufferParams, std::mutex& profilerLock) {
sk_sp<SkSurface> backBuffer;
SkMatrix preTransform;
if (mHardwareBuffer) {
@@ -103,6 +103,7 @@
// Draw visual debugging features
if (CC_UNLIKELY(Properties::showDirtyRegions ||
ProfileType::None != Properties::getProfileType())) {
+ std::scoped_lock lock(profilerLock);
SkCanvas* profileCanvas = backBuffer->getCanvas();
SkAutoCanvasRestore saver(profileCanvas, true);
profileCanvas->concat(preTransform);
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index e2ea57d..624eaa5 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -42,7 +42,8 @@
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler,
- const renderthread::HardwareBufferRenderParams& bufferParams) override;
+ const renderthread::HardwareBufferRenderParams& bufferParams,
+ std::mutex& profilerLock) override;
GrSurfaceOrigin getSurfaceOrigin() override { return kTopLeft_GrSurfaceOrigin; }
bool swapBuffers(const renderthread::Frame& frame, IRenderPipeline::DrawResult& drawResult,
const SkRect& screenDirty, FrameInfo* currentFrameInfo,
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 56b52dc..9c7f7cc 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -625,14 +625,9 @@
{
// FrameInfoVisualizer accesses the frame events, which cannot be mutated mid-draw
// or it can lead to memory corruption.
- // This lock is overly broad, but it's the quickest fix since this mutex is otherwise
- // not visible to IRenderPipeline much less FrameInfoVisualizer. And since this is
- // the thread we're primarily concerned about being responsive, this being too broad
- // shouldn't pose a performance issue.
- std::scoped_lock lock(mFrameMetricsReporterMutex);
- drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry,
- &mLayerUpdateQueue, mContentDrawBounds, mOpaque,
- mLightInfo, mRenderNodes, &(profiler()), mBufferParams);
+ drawResult = mRenderPipeline->draw(
+ frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, mContentDrawBounds,
+ mOpaque, mLightInfo, mRenderNodes, &(profiler()), mBufferParams, profilerLock());
}
uint64_t frameCompleteNr = getFrameNumber();
@@ -762,7 +757,7 @@
mCurrentFrameInfo->markFrameCompleted();
mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted)
= mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted);
- std::scoped_lock lock(mFrameMetricsReporterMutex);
+ std::scoped_lock lock(mFrameInfoMutex);
mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter, frameCompleteNr,
mSurfaceControlGenerationId);
}
@@ -791,7 +786,7 @@
void CanvasContext::reportMetricsWithPresentTime() {
{ // acquire lock
- std::scoped_lock lock(mFrameMetricsReporterMutex);
+ std::scoped_lock lock(mFrameInfoMutex);
if (mFrameMetricsReporter == nullptr) {
return;
}
@@ -826,7 +821,7 @@
forthBehind->set(FrameInfoIndex::DisplayPresentTime) = presentTime;
{ // acquire lock
- std::scoped_lock lock(mFrameMetricsReporterMutex);
+ std::scoped_lock lock(mFrameInfoMutex);
if (mFrameMetricsReporter != nullptr) {
mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/,
frameNumber, surfaceControlId);
@@ -835,7 +830,7 @@
}
void CanvasContext::addFrameMetricsObserver(FrameMetricsObserver* observer) {
- std::scoped_lock lock(mFrameMetricsReporterMutex);
+ std::scoped_lock lock(mFrameInfoMutex);
if (mFrameMetricsReporter.get() == nullptr) {
mFrameMetricsReporter.reset(new FrameMetricsReporter());
}
@@ -849,7 +844,7 @@
}
void CanvasContext::removeFrameMetricsObserver(FrameMetricsObserver* observer) {
- std::scoped_lock lock(mFrameMetricsReporterMutex);
+ std::scoped_lock lock(mFrameInfoMutex);
if (mFrameMetricsReporter.get() != nullptr) {
mFrameMetricsReporter->removeObserver(observer);
if (!mFrameMetricsReporter->hasObservers()) {
@@ -886,7 +881,7 @@
FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber, surfaceControlId);
if (frameInfo != nullptr) {
- std::scoped_lock lock(instance->mFrameMetricsReporterMutex);
+ std::scoped_lock lock(instance->mFrameInfoMutex);
frameInfo->set(FrameInfoIndex::FrameCompleted) = std::max(gpuCompleteTime,
frameInfo->get(FrameInfoIndex::SwapBuffersCompleted));
frameInfo->set(FrameInfoIndex::GpuCompleted) = std::max(
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index be9b649..e2e3fa3 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -164,6 +164,7 @@
void notifyFramePending();
FrameInfoVisualizer& profiler() { return mProfiler; }
+ std::mutex& profilerLock() { return mFrameInfoMutex; }
void dumpFrames(int fd);
void resetFrameStats();
@@ -342,9 +343,8 @@
std::string mName;
JankTracker mJankTracker;
FrameInfoVisualizer mProfiler;
- std::unique_ptr<FrameMetricsReporter> mFrameMetricsReporter
- GUARDED_BY(mFrameMetricsReporterMutex);
- std::mutex mFrameMetricsReporterMutex;
+ std::unique_ptr<FrameMetricsReporter> mFrameMetricsReporter GUARDED_BY(mFrameInfoMutex);
+ std::mutex mFrameInfoMutex;
std::set<RenderNode*> mPrefetchedLayers;
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 9c879d5..b8c3a4d 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -68,7 +68,8 @@
const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler,
- const HardwareBufferRenderParams& bufferParams) = 0;
+ const HardwareBufferRenderParams& bufferParams,
+ std::mutex& profilerLock) = 0;
virtual bool swapBuffers(const Frame& frame, IRenderPipeline::DrawResult&,
const SkRect& screenDirty, FrameInfo* currentFrameInfo,
bool* requireSwap) = 0;
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 9616b5d..62412df 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -734,7 +734,7 @@
* request.
* @param transferInitiatorPackageName the package name of the app that initiated the transfer.
* This value is used with the user handle to populate {@link
- * RoutingController#wasTransferRequestedBySelf()}.
+ * RoutingController#wasTransferInitiatedBySelf()}.
* @hide
*/
@FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
@@ -1550,11 +1550,11 @@
}
/**
- * Returns whether the transfer was requested by the calling app (as determined by comparing
+ * Returns whether the transfer was initiated by the calling app (as determined by comparing
* {@link UserHandle} and package name).
*/
@FlaggedApi(FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES)
- public boolean wasTransferRequestedBySelf() {
+ public boolean wasTransferInitiatedBySelf() {
RoutingSessionInfo sessionInfo = getRoutingSessionInfo();
UserHandle transferInitiatorUserHandle = sessionInfo.getTransferInitiatorUserHandle();
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index a8ffd2b..2f6575e 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -149,4 +149,7 @@
// For CTS purpose only. Add/remove a TvInputHardware device
void addHardwareDevice(in int deviceId);
void removeHardwareDevice(in int deviceId);
+
+ // For freezing video playback
+ void setVideoFrozen(in IBinder sessionToken, boolean isFrozen, int userId);
}
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index e37ee6e..a93f18d 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -83,4 +83,7 @@
// For TV messages
void notifyTvMessage(int type, in Bundle data);
void setTvMessageEnabled(int type, boolean enabled);
+
+ // For freezing video
+ void setVideoFrozen(boolean isFrozen);
}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index ae3ee65..921104d 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -81,6 +81,7 @@
private static final int DO_NOTIFY_TV_MESSAGE = 32;
private static final int DO_STOP_PLAYBACK = 33;
private static final int DO_START_PLAYBACK = 34;
+ private static final int DO_SET_VIDEO_FROZEN = 35;
private final boolean mIsRecordingSession;
private final HandlerCaller mCaller;
@@ -296,6 +297,10 @@
mTvInputSessionImpl.startPlayback();
break;
}
+ case DO_SET_VIDEO_FROZEN: {
+ mTvInputSessionImpl.setVideoFrozen((Boolean) msg.obj);
+ break;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
break;
@@ -483,6 +488,11 @@
}
@Override
+ public void setVideoFrozen(boolean isFrozen) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_VIDEO_FROZEN, isFrozen));
+ }
+
+ @Override
public void notifyTvMessage(int type, Bundle data) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_NOTIFY_TV_MESSAGE, type, data));
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index c685a5a..51b2542 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -3334,6 +3334,18 @@
}
}
+ void setVideoFrozen(boolean isFrozen) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.setVideoFrozen(mToken, isFrozen, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Sends TV messages to the service for testing purposes
*/
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 55fa517..76d8e50 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1558,6 +1558,17 @@
}
/**
+ * Called when a request to freeze the video is received from the TV app. The audio should
+ * continue playback while the video is frozen.
+ *
+ * <p> This should freeze the video to the last frame when the state is set to {@code true}.
+ * @param isFrozen whether or not the video should be frozen.
+ * @hide
+ */
+ public void onSetVideoFrozen(boolean isFrozen) {
+ }
+
+ /**
* Called when the application requests to play a given recorded TV program.
*
* @param recordedProgramUri The URI of a recorded TV program.
@@ -2034,6 +2045,13 @@
}
/**
+ * Calls {@link #onSetVideoFrozen(boolean)}.
+ */
+ void setVideoFrozen(boolean isFrozen) {
+ onSetVideoFrozen(isFrozen);
+ }
+
+ /**
* Calls {@link #onTimeShiftPlay(Uri)}.
*/
void timeShiftPlay(Uri recordedProgramUri) {
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 233f966..cb45661 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -675,6 +675,23 @@
}
/**
+ * Sets whether or not the video is frozen. While the video is frozen, audio playback will
+ * continue.
+ *
+ * <p> This should be invoked after a {@link TvInteractiveAppService.Session#requestCommand} is
+ * received with the command to freeze the video.
+ *
+ * <p> This will freeze the video to the last frame when the state is set to {@code true}.
+ * @param isFrozen whether or not the video is frozen.
+ * @hide
+ */
+ public void setVideoFrozen(boolean isFrozen) {
+ if (mSession != null) {
+ mSession.setVideoFrozen(isFrozen);
+ }
+ }
+
+ /**
* Sends TV messages to the session for testing purposes
*
* @hide
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 5cc86ba..7936403 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -112,7 +112,8 @@
PLAYBACK_COMMAND_TYPE_TUNE_PREV,
PLAYBACK_COMMAND_TYPE_STOP,
PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME,
- PLAYBACK_COMMAND_TYPE_SELECT_TRACK
+ PLAYBACK_COMMAND_TYPE_SELECT_TRACK,
+ PLAYBACK_COMMAND_TYPE_FREEZE
})
public @interface PlaybackCommandType {}
@@ -142,8 +143,11 @@
* Playback command type: select the given track.
*/
public static final String PLAYBACK_COMMAND_TYPE_SELECT_TRACK = "select_track";
-
-
+ /**
+ * Playback command type: freeze the video playback on the current frame.
+ * @hide
+ */
+ public static final String PLAYBACK_COMMAND_TYPE_FREEZE = "freeze";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 09f09b9..f74edbf 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -30,6 +30,7 @@
import android.hardware.tv.tuner.Constant;
import android.hardware.tv.tuner.Constant64Bit;
import android.hardware.tv.tuner.FrontendScanType;
+import android.media.MediaCodec;
import android.media.tv.TvInputService;
import android.media.tv.tuner.dvr.DvrPlayback;
import android.media.tv.tuner.dvr.DvrRecorder;
@@ -272,8 +273,12 @@
try {
System.loadLibrary("media_tv_tuner");
nativeInit();
+ // Load and initialize MediaCodec to avoid flaky cts test result.
+ Class.forName(MediaCodec.class.getName());
} catch (UnsatisfiedLinkError e) {
Log.d(TAG, "tuner JNI library not found!");
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, "MediaCodec class not found!", e);
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index 2da8c8c..221ca4f 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -32,6 +32,7 @@
import android.os.Bundle;
import android.os.Flags;
import android.os.Process;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
@@ -131,7 +132,7 @@
final boolean isUpdate =
((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
final boolean isArchive =
- android.content.pm.Flags.archiving() && (
+ isArchivingEnabled() && (
(dialogInfo.deleteFlags & PackageManager.DELETE_ARCHIVE) != 0);
final UserHandle myUserHandle = Process.myUserHandle();
UserManager userManager = getContext().getSystemService(UserManager.class);
@@ -242,6 +243,11 @@
return dialogBuilder.create();
}
+ private static boolean isArchivingEnabled() {
+ return android.content.pm.Flags.archiving()
+ || SystemProperties.getBoolean("pm.archiving.enabled", false);
+ }
+
private boolean isCloneProfile(UserHandle userHandle) {
UserManager customUserManager = getContext()
.createContextAsUser(UserHandle.of(userHandle.getIdentifier()), 0)
diff --git a/packages/SettingsLib/ActionBarShadow/Android.bp b/packages/SettingsLib/ActionBarShadow/Android.bp
index 6f94458..77cbb00 100644
--- a/packages/SettingsLib/ActionBarShadow/Android.bp
+++ b/packages/SettingsLib/ActionBarShadow/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibActionBarShadow",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
index 1228555..c36b82d 100644
--- a/packages/SettingsLib/ActionButtonsPreference/Android.bp
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibActionButtonsPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/ActivityEmbedding/Android.bp b/packages/SettingsLib/ActivityEmbedding/Android.bp
index 41de29a..838a9e5 100644
--- a/packages/SettingsLib/ActivityEmbedding/Android.bp
+++ b/packages/SettingsLib/ActivityEmbedding/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibActivityEmbedding",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp
index 044ba87..67b6fb5 100644
--- a/packages/SettingsLib/AdaptiveIcon/Android.bp
+++ b/packages/SettingsLib/AdaptiveIcon/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibAdaptiveIcon",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/AppPreference/Android.bp b/packages/SettingsLib/AppPreference/Android.bp
index 69b9d44..c5b2ef6 100644
--- a/packages/SettingsLib/AppPreference/Android.bp
+++ b/packages/SettingsLib/AppPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibAppPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp
index da91344..07290de 100644
--- a/packages/SettingsLib/BannerMessagePreference/Android.bp
+++ b/packages/SettingsLib/BannerMessagePreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibBannerMessagePreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/BarChartPreference/Android.bp b/packages/SettingsLib/BarChartPreference/Android.bp
index be1e0cf..448ed56 100644
--- a/packages/SettingsLib/BarChartPreference/Android.bp
+++ b/packages/SettingsLib/BarChartPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibBarChartPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/ButtonPreference/Android.bp b/packages/SettingsLib/ButtonPreference/Android.bp
index 35572fad..0382829 100644
--- a/packages/SettingsLib/ButtonPreference/Android.bp
+++ b/packages/SettingsLib/ButtonPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibButtonPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
index 70f7554..87ec0b8 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibCollapsingToolbarBaseActivity",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/DisplayUtils/Android.bp b/packages/SettingsLib/DisplayUtils/Android.bp
index eab35a1..279bb70 100644
--- a/packages/SettingsLib/DisplayUtils/Android.bp
+++ b/packages/SettingsLib/DisplayUtils/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibDisplayUtils",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/EntityHeaderWidgets/Android.bp b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
index 17b662c..83f81c6 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/Android.bp
+++ b/packages/SettingsLib/EntityHeaderWidgets/Android.bp
@@ -10,13 +10,16 @@
android_library {
name: "SettingsLibEntityHeaderWidgets",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
static_libs: [
- "androidx.annotation_annotation",
- "SettingsLibSettingsTheme"
+ "androidx.annotation_annotation",
+ "SettingsLibSettingsTheme",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/FooterPreference/Android.bp b/packages/SettingsLib/FooterPreference/Android.bp
index b45cd65..d1ad80d 100644
--- a/packages/SettingsLib/FooterPreference/Android.bp
+++ b/packages/SettingsLib/FooterPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibFooterPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/HelpUtils/Android.bp b/packages/SettingsLib/HelpUtils/Android.bp
index 041fce2..284106e 100644
--- a/packages/SettingsLib/HelpUtils/Android.bp
+++ b/packages/SettingsLib/HelpUtils/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibHelpUtils",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index 4d4759b..6407810 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibIllustrationPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/LayoutPreference/Android.bp b/packages/SettingsLib/LayoutPreference/Android.bp
index 53ded23..8cf636a 100644
--- a/packages/SettingsLib/LayoutPreference/Android.bp
+++ b/packages/SettingsLib/LayoutPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibLayoutPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp
index 010a6ce..b984aaf 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibMainSwitchPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/ProfileSelector/Android.bp b/packages/SettingsLib/ProfileSelector/Android.bp
index 155ed2e..6dc07b2 100644
--- a/packages/SettingsLib/ProfileSelector/Android.bp
+++ b/packages/SettingsLib/ProfileSelector/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibProfileSelector",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/RestrictedLockUtils/Android.bp b/packages/SettingsLib/RestrictedLockUtils/Android.bp
index 3b04bd9..8d722eb 100644
--- a/packages/SettingsLib/RestrictedLockUtils/Android.bp
+++ b/packages/SettingsLib/RestrictedLockUtils/Android.bp
@@ -16,6 +16,9 @@
android_library {
name: "SettingsLibRestrictedLockUtils",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/SchedulesProvider/Android.bp b/packages/SettingsLib/SchedulesProvider/Android.bp
index 22e4e94..c0fc741e7 100644
--- a/packages/SettingsLib/SchedulesProvider/Android.bp
+++ b/packages/SettingsLib/SchedulesProvider/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibSchedulesProvider",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/SearchProvider/Android.bp b/packages/SettingsLib/SearchProvider/Android.bp
index c385d38..61ed65c 100644
--- a/packages/SettingsLib/SearchProvider/Android.bp
+++ b/packages/SettingsLib/SearchProvider/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibSearchProvider",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
index 702387e..2fe446d 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibSelectorWithWidgetPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/SettingsSpinner/Android.bp b/packages/SettingsLib/SettingsSpinner/Android.bp
index 0eec505..8fed61f 100644
--- a/packages/SettingsLib/SettingsSpinner/Android.bp
+++ b/packages/SettingsLib/SettingsSpinner/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibSettingsSpinner",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/SettingsTransition/Android.bp b/packages/SettingsLib/SettingsTransition/Android.bp
index 06493c0..e04af6c 100644
--- a/packages/SettingsLib/SettingsTransition/Android.bp
+++ b/packages/SettingsLib/SettingsTransition/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibSettingsTransition",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index ed90284..df5644b 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -74,8 +74,7 @@
android:exported="false">
</provider>
<activity
- android:name="com.android.settingslib.spa.gallery.SpaDialogActivity"
- android:excludeFromRecents="true"
+ android:name="com.android.settingslib.spa.gallery.GalleryDialogActivity"
android:exported="true"
android:theme="@style/Theme.SpaLib.Dialog">
</activity>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDialogActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDialogActivity.kt
new file mode 100644
index 0000000..e22ed35
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDialogActivity.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
+import com.android.settingslib.spa.SpaBaseDialogActivity
+import com.android.settingslib.spa.widget.dialog.AlertDialogButton
+import com.android.settingslib.spa.widget.dialog.SettingsAlertDialogWithIcon
+
+class GalleryDialogActivity : SpaBaseDialogActivity() {
+ @Composable
+ override fun Content() {
+ SettingsAlertDialogWithIcon(
+ onDismissRequest = { finish() },
+ confirmButton = AlertDialogButton("confirm") { finish() },
+ dismissButton = AlertDialogButton("dismiss") { finish() },
+ title = "title",
+ text = {
+ Text(
+ "text",
+ modifier = Modifier.fillMaxWidth(),
+ textAlign = TextAlign.Center
+ )
+ }
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaDialogActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaDialogActivity.kt
deleted file mode 100644
index 8b80fe2..0000000
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaDialogActivity.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.spa.gallery
-
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.foundation.layout.width
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.WarningAmber
-import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Button
-import androidx.compose.material3.Icon
-import androidx.compose.material3.OutlinedButton
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.vector.ImageVector
-import com.android.settingslib.spa.framework.common.LogCategory
-import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
-import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.widget.dialog.getDialogWidth
-
-
-class SpaDialogActivity : ComponentActivity() {
- private val spaEnvironment get() = SpaEnvironmentFactory.instance
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- spaEnvironment.logger.message(TAG, "onCreate", category = LogCategory.FRAMEWORK)
- setContent {
- SettingsTheme {
- Content()
- }
- }
- }
-
- @Composable
- fun Content() {
- var openAlertDialog by remember { mutableStateOf(false) }
- AlertDialog(openAlertDialog)
- LaunchedEffect(key1 = Unit) {
- openAlertDialog = true
- }
- }
-
- @Composable
- fun AlertDialog(openAlertDialog: Boolean) {
- when {
- openAlertDialog -> {
- AlertDialogExample(
- onDismissRequest = { finish() },
- onConfirmation = { finish() },
- dialogTitle = intent.getStringExtra(DIALOG_TITLE) ?: DIALOG_TITLE,
- dialogText = intent.getStringExtra(DIALOG_TEXT) ?: DIALOG_TEXT,
- icon = Icons.Default.WarningAmber
- )
- }
- }
- }
-
- @Composable
- fun AlertDialogExample(
- onDismissRequest: () -> Unit,
- onConfirmation: () -> Unit,
- dialogTitle: String,
- dialogText: String,
- icon: ImageVector,
- ) {
- AlertDialog(
- modifier = Modifier.width(getDialogWidth()),
- icon = {
- Icon(icon, contentDescription = null)
- },
- title = {
- Text(text = dialogTitle)
- },
- text = {
- Text(text = dialogText)
- },
- onDismissRequest = {
- onDismissRequest()
- },
- dismissButton = {
- OutlinedButton(
- onClick = {
- onDismissRequest()
- }
- ) {
- Text(intent.getStringExtra(DISMISS_TEXT) ?: DISMISS_TEXT)
- }
- },
- confirmButton = {
- Button(
- onClick = {
- onConfirmation()
- },
- ) {
- Text(intent.getStringExtra(CONFIRM_TEXT) ?: CONFIRM_TEXT)
- }
- }
- )
- }
-
- companion object {
- private const val TAG = "SpaDialogActivity"
- private const val DIALOG_TITLE = "dialogTitle"
- private const val DIALOG_TEXT = "dialogText"
- private const val CONFIRM_TEXT = "confirmText"
- private const val DISMISS_TEXT = "dismissText"
- }
-}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/SpaBaseDialogActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/SpaBaseDialogActivity.kt
new file mode 100644
index 0000000..dfb780a
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/SpaBaseDialogActivity.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.framework.common.LogCategory
+import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+abstract class SpaBaseDialogActivity : ComponentActivity() {
+ private val spaEnvironment get() = SpaEnvironmentFactory.instance
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ spaEnvironment.logger.message(TAG, "onCreate", category = LogCategory.FRAMEWORK)
+ setContent {
+ SettingsTheme {
+ Content()
+ }
+ }
+ }
+
+ @Composable
+ abstract fun Content()
+
+ companion object {
+ private const val TAG = "SpaBaseDialogActivity"
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt
index 8ffd799..de080e3 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt
@@ -102,8 +102,8 @@
fun getDialogWidth(): Dp {
val configuration = LocalConfiguration.current
return configuration.screenWidthDp.dp * when (configuration.orientation) {
- Configuration.ORIENTATION_LANDSCAPE -> 0.6f
- else -> 0.8f
+ Configuration.ORIENTATION_LANDSCAPE -> 0.65f
+ else -> 0.85f
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogWithIcon.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogWithIcon.kt
new file mode 100644
index 0000000..1695e4f
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogWithIcon.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.dialog
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.WarningAmber
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Button
+import androidx.compose.material3.Icon
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.window.DialogProperties
+
+@Composable
+fun SettingsAlertDialogWithIcon(
+ onDismissRequest: () -> Unit,
+ confirmButton: AlertDialogButton?,
+ dismissButton: AlertDialogButton?,
+ title: String?,
+ text: @Composable (() -> Unit)?,
+) {
+ AlertDialog(
+ onDismissRequest = onDismissRequest,
+ icon = { Icon(Icons.Default.WarningAmber, contentDescription = null) },
+ modifier = Modifier.width(getDialogWidth()),
+ confirmButton = {
+ confirmButton?.let {
+ Button(
+ onClick = {
+ it.onClick()
+ },
+ ) {
+ Text(it.text)
+ }
+ }
+ },
+ dismissButton = dismissButton?.let {
+ {
+ OutlinedButton(
+ onClick = {
+ it.onClick()
+ },
+ ) {
+ Text(it.text)
+ }
+ }
+ },
+ title = title?.let {
+ {
+ Text(
+ it,
+ modifier = Modifier.fillMaxWidth(),
+ textAlign = TextAlign.Center
+ )
+ }
+ },
+ text = text?.let {
+ {
+ Column(Modifier.verticalScroll(rememberScrollState())) {
+ text()
+ }
+ }
+ },
+ properties = DialogProperties(usePlatformDefaultWidth = false),
+ )
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
index abeffec..0a98791 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ApplicationInfoFlags
import android.content.pm.ResolveInfo
+import android.os.SystemProperties
import com.android.internal.R
import com.android.settingslib.spaprivileged.framework.common.userManager
import kotlinx.coroutines.async
@@ -110,7 +111,7 @@
): List<ApplicationInfo> {
val disabledComponentsFlag = (PackageManager.MATCH_DISABLED_COMPONENTS or
PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong()
- val archivedPackagesFlag: Long = if (featureFlags.archiving())
+ val archivedPackagesFlag: Long = if (isArchivingEnabled(featureFlags))
PackageManager.MATCH_ARCHIVED_PACKAGES else 0L
val regularFlags = ApplicationInfoFlags.of(
disabledComponentsFlag or
@@ -148,6 +149,9 @@
}
}
+ private fun isArchivingEnabled(featureFlags: FeatureFlags) =
+ featureFlags.archiving() || SystemProperties.getBoolean("pm.archiving.enabled", false)
+
override fun showSystemPredicate(
userIdFlow: Flow<Int>,
showSystemFlow: Flow<Boolean>,
diff --git a/packages/SettingsLib/Tile/Android.bp b/packages/SettingsLib/Tile/Android.bp
index 19c59dd..54b9748 100644
--- a/packages/SettingsLib/Tile/Android.bp
+++ b/packages/SettingsLib/Tile/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibTile",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/TopIntroPreference/Android.bp b/packages/SettingsLib/TopIntroPreference/Android.bp
index 77b7ac1..e70201b 100644
--- a/packages/SettingsLib/TopIntroPreference/Android.bp
+++ b/packages/SettingsLib/TopIntroPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibTopIntroPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/TwoTargetPreference/Android.bp b/packages/SettingsLib/TwoTargetPreference/Android.bp
index 5aa906e..70d9630 100644
--- a/packages/SettingsLib/TwoTargetPreference/Android.bp
+++ b/packages/SettingsLib/TwoTargetPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibTwoTargetPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/UsageProgressBarPreference/Android.bp b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
index 4cc90cc..0a83aab 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/Android.bp
+++ b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibUsageProgressBarPreference",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/Utils/Android.bp b/packages/SettingsLib/Utils/Android.bp
index d5a56c8..5d2aef7 100644
--- a/packages/SettingsLib/Utils/Android.bp
+++ b/packages/SettingsLib/Utils/Android.bp
@@ -10,6 +10,9 @@
android_library {
name: "SettingsLibUtils",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp
index 390c9d2e..1f8d1dd 100644
--- a/packages/SettingsLib/search/Android.bp
+++ b/packages/SettingsLib/search/Android.bp
@@ -17,6 +17,9 @@
android_library {
name: "SettingsLib-search",
use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
static_libs: [
"SettingsLib-search-interface",
],
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 5004f25..8ae50eb 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -241,6 +241,8 @@
Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO,
Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE,
Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME,
+ Settings.Secure.BLUETOOTH_LE_BROADCAST_IMPROVE_COMPATIBILITY,
+ Settings.Secure.BLUETOOTH_LE_BROADCAST_FALLBACK_ACTIVE_DEVICE_ADDRESS,
Settings.Secure.CUSTOM_BUGREPORT_HANDLER_APP,
Settings.Secure.CUSTOM_BUGREPORT_HANDLER_USER,
Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 0b0e182..285c8c9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -387,6 +387,11 @@
VALIDATORS.put(Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.BLUETOOTH_LE_BROADCAST_CODE, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(
+ Secure.BLUETOOTH_LE_BROADCAST_IMPROVE_COMPATIBILITY,
+ new DiscreteValueValidator(new String[] {"0", "1"}));
+ VALIDATORS.put(
+ Secure.BLUETOOTH_LE_BROADCAST_FALLBACK_ACTIVE_DEVICE_ADDRESS, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.CUSTOM_BUGREPORT_HANDLER_APP, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.CUSTOM_BUGREPORT_HANDLER_USER, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.LOCK_SCREEN_WEATHER_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index b464498..3236130 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -15,6 +15,13 @@
}
flag {
+ name: "notification_async_group_header_inflation"
+ namespace: "systemui"
+ description: "Inflates the notification group summary header views from the background thread."
+ bug: "217799515"
+}
+
+flag {
name: "notification_async_hybrid_view_inflation"
namespace: "systemui"
description: "Inflates hybrid (single-line) notification views from the background thread."
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 0a1a98f..a390305 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -69,7 +69,6 @@
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.alpha
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
@@ -90,9 +89,11 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Popup
+import androidx.core.view.setPadding
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.ui.compose.Dimensions.CardOutlineWidth
import com.android.systemui.communal.ui.compose.extensions.allowGestures
import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
import com.android.systemui.communal.ui.compose.extensions.observeTapsWithoutConsuming
@@ -474,7 +475,7 @@
Card(
modifier = modifier.size(Dp(size.width), Dp(size.height)),
colors = CardDefaults.cardColors(containerColor = Color.Transparent),
- border = BorderStroke(3.dp, LocalAndroidColorScheme.current.tertiaryFixed),
+ border = BorderStroke(CardOutlineWidth, LocalAndroidColorScheme.current.tertiaryFixed),
shape = RoundedCornerShape(16.dp)
) {}
}
@@ -488,7 +489,7 @@
) {
val colors = LocalAndroidColorScheme.current
Card(
- modifier = modifier.height(size.height.dp),
+ modifier = modifier.height(size.height.dp).padding(CardOutlineWidth),
colors =
CardDefaults.cardColors(
containerColor = colors.primary,
@@ -560,7 +561,7 @@
}
val colors = LocalAndroidColorScheme.current
Card(
- modifier = modifier.height(size.height.dp),
+ modifier = modifier.height(size.height.dp).padding(CardOutlineWidth),
colors = CardDefaults.cardColors(containerColor = Color.Transparent),
border = BorderStroke(1.dp, colors.primary),
shape = RoundedCornerShape(200.dp),
@@ -599,6 +600,7 @@
modifier = modifier.height(size.height.dp),
contentAlignment = Alignment.Center,
) {
+ val paddingInPx = with(LocalDensity.current) { CardOutlineWidth.toPx().toInt() }
AndroidView(
modifier = modifier.allowGestures(allowed = !viewModel.isEditMode),
factory = { context ->
@@ -613,6 +615,10 @@
.createViewForCommunal(context, model.appWidgetId, model.providerInfo)
.apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
model.appWidgetHost.setInteractionHandler(null)
+ // Remove the extra padding applied to AppWidgetHostView to allow widgets to
+ // occupy the entire box. The added padding is now adjusted to leave only sufficient
+ // space for displaying the outline around the box when the widget is selected.
+ view.setPadding(paddingInPx)
view
},
// For reusing composition in lazy lists.
@@ -726,6 +732,7 @@
val CardHeightFull = 630.dp
val CardHeightHalf = 307.dp
val CardHeightThird = 199.dp
+ val CardOutlineWidth = 3.dp
val GridHeight = CardHeightFull
val Spacing = 16.dp
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 085dcf4..1545372 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -42,7 +42,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.controls.ui.MediaCarouselController
+import com.android.systemui.media.controls.ui.MediaHierarchyManager
import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.MediaHostState
import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
@@ -126,6 +128,12 @@
modifier = modifier,
)
+ init {
+ mediaHost.expansion = MediaHostState.EXPANDED
+ mediaHost.showsOnlyActiveMedia = true
+ mediaHost.init(MediaHierarchyManager.LOCATION_QQS)
+ }
+
private fun destinationScenes(
up: SceneKey,
): Map<UserAction, SceneModel> {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index a08283d..1cd764e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -51,7 +51,6 @@
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.ui.MediaHost
import com.android.systemui.model.SysUiState
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -189,8 +188,6 @@
@Mock private lateinit var mediaDataManager: MediaDataManager
- @Mock private lateinit var mediaHost: MediaHost
-
private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager
private lateinit var telecomManager: TelecomManager
@@ -239,7 +236,6 @@
qsSceneAdapter = qsFlexiglassAdapter,
notifications = kosmos.notificationsPlaceholderViewModel,
mediaDataManager = mediaDataManager,
- mediaHost = mediaHost,
)
kosmos.fakeDeviceEntryRepository.setUnlocked(false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index bf873c2..e980165 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -28,7 +28,6 @@
import com.android.systemui.flags.Flags
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.ui.MediaHost
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.SceneKey
@@ -90,7 +89,6 @@
private lateinit var underTest: ShadeSceneViewModel
@Mock private lateinit var mediaDataManager: MediaDataManager
- @Mock private lateinit var mediaHost: MediaHost
@Before
fun setUp() {
@@ -113,7 +111,6 @@
qsSceneAdapter = qsFlexiglassAdapter,
notifications = kosmos.notificationsPlaceholderViewModel,
mediaDataManager = mediaDataManager,
- mediaHost = mediaHost,
)
}
diff --git a/packages/SystemUI/res/values-sw720dp-land/config.xml b/packages/SystemUI/res/values-sw720dp-land/config.xml
index be34a48..0955f67 100644
--- a/packages/SystemUI/res/values-sw720dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/config.xml
@@ -24,9 +24,6 @@
<!-- The maximum number of tiles in the QuickQSPanel -->
<integer name="quick_qs_panel_max_tiles">6</integer>
- <!-- Whether to use the split 2-column notification shade -->
- <bool name="config_use_split_notification_shade">true</bool>
-
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">3</integer>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
index b373f85..fede479 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
@@ -53,14 +53,14 @@
@SysUISingleton
@FaceAuthTableLog
fun provideFaceAuthTableLog(factory: TableLogBufferFactory): TableLogBuffer {
- return factory.create("FaceAuthTableLog", 100)
+ return factory.create("FaceAuthTableLog", 400)
}
@Provides
@SysUISingleton
@FaceDetectTableLog
fun provideFaceDetectTableLog(factory: TableLogBufferFactory): TableLogBuffer {
- return factory.create("FaceDetectTableLog", 100)
+ return factory.create("FaceDetectTableLog", 400)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 0d641ac..58e0428 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -624,7 +624,7 @@
}
if (!mIsEnabled) {
- mGestureNavigationSettingsObserver.unregister();
+ mBackgroundExecutor.execute(mGestureNavigationSettingsObserver::unregister);
if (DEBUG_MISSING_GESTURE) {
Log.d(DEBUG_MISSING_GESTURE_TAG, "Unregister display listener");
}
@@ -642,7 +642,7 @@
}
} else {
- mGestureNavigationSettingsObserver.register();
+ mBackgroundExecutor.execute(mGestureNavigationSettingsObserver::register);
updateDisplaySize();
if (DEBUG_MISSING_GESTURE) {
Log.d(DEBUG_MISSING_GESTURE_TAG, "Register display listener");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index aff4a67..2077d73 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -88,9 +88,9 @@
/** Called when the expansion of the Quick Settings changed. */
fun onQuickSettingsExpansionChanged(expansion: Float, isInSplitShade: Boolean) {
if (isInSplitShade) {
- // In split shade, we want to fade in the background only at the very end (see
- // b/240563302).
- val delay = 0.99f
+ // In split shade, we want to fade in the background when the QS background starts to
+ // show.
+ val delay = 0.15f
_alpha.value = expansion
_backgroundAlpha.value = max(0f, expansion - delay) / (1f - delay)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index d0da945..9af2d58 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -20,15 +20,10 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
-import com.android.systemui.media.dagger.MediaModule
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import javax.inject.Inject
-import javax.inject.Named
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -46,7 +41,6 @@
val shadeHeaderViewModel: ShadeHeaderViewModel,
val notifications: NotificationsPlaceholderViewModel,
val mediaDataManager: MediaDataManager,
- @Named(MediaModule.QUICK_QS_PANEL) private val mediaHost: MediaHost,
) {
/** The key of the scene we should switch to when swiping up. */
val upDestinationSceneKey: StateFlow<SceneKey> =
@@ -83,12 +77,6 @@
}
}
- init {
- mediaHost.expansion = MediaHostState.EXPANDED
- mediaHost.showsOnlyActiveMedia = true
- mediaHost.init(MediaHierarchyManager.LOCATION_QQS)
- }
-
fun isMediaVisible(): Boolean {
// TODO(b/296122467): handle updates to carousel visibility while scene is still visible
return mediaDataManager.hasActiveMediaOrRecommendation()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/AsyncGroupHeaderViewInflation.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/AsyncGroupHeaderViewInflation.kt
new file mode 100644
index 0000000..44fc77f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/AsyncGroupHeaderViewInflation.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the Async Group Header Inflation flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object AsyncGroupHeaderViewInflation {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATION_ASYNC_GROUP_HEADER_INFLATION
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the async inflation of group header views enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.notificationAsyncGroupHeaderInflation()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 68879a5..5e5273b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -55,8 +55,6 @@
import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -120,10 +118,6 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Spy
private SysuiTestableContext mSpyContext = getContext();
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index c711806..1cb3bf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -393,23 +393,23 @@
}
@Test
- fun backgroundAlpha_inSplitShade_followsExpansion_with_0_99_delay() {
+ fun backgroundAlpha_inSplitShade_followsExpansion_with_0_15_delay() {
val underTest = utils.footerActionsViewModel()
val floatTolerance = 0.01f
underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = true)
assertThat(underTest.backgroundAlpha.value).isEqualTo(0f)
- underTest.onQuickSettingsExpansionChanged(0.5f, isInSplitShade = true)
+ underTest.onQuickSettingsExpansionChanged(0.1f, isInSplitShade = true)
assertThat(underTest.backgroundAlpha.value).isEqualTo(0f)
- underTest.onQuickSettingsExpansionChanged(0.9f, isInSplitShade = true)
+ underTest.onQuickSettingsExpansionChanged(0.14f, isInSplitShade = true)
assertThat(underTest.backgroundAlpha.value).isEqualTo(0f)
- underTest.onQuickSettingsExpansionChanged(0.991f, isInSplitShade = true)
+ underTest.onQuickSettingsExpansionChanged(0.235f, isInSplitShade = true)
assertThat(underTest.backgroundAlpha.value).isWithin(floatTolerance).of(0.1f)
- underTest.onQuickSettingsExpansionChanged(0.995f, isInSplitShade = true)
+ underTest.onQuickSettingsExpansionChanged(0.575f, isInSplitShade = true)
assertThat(underTest.backgroundAlpha.value).isWithin(floatTolerance).of(0.5f)
underTest.onQuickSettingsExpansionChanged(1f, isInSplitShade = true)
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index 92e00ee..727721d 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -5,4 +5,11 @@
namespace: "autofill"
description: "Guards Autofill Framework against Autofill-Credman integration"
bug: "296907283"
+}
+
+flag {
+ name: "autofill_credman_integration_phase2"
+ namespace: "autofill"
+ description: "Guards against Autofill-Credman integration phase 2"
+ bug: "320730001"
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 329aac6..9f279b1 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -48,8 +48,6 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.am.DropboxRateLimiter;
-import com.android.server.os.TombstoneProtos;
-import com.android.server.os.TombstoneProtos.Tombstone;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -62,14 +60,11 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermissions;
-import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
/**
* Performs a number of miscellaneous, non-system-critical actions
@@ -337,12 +332,12 @@
*
* @param ctx Context
* @param tombstone path to the tombstone
- * @param tombstoneProto the parsed proto tombstone
+ * @param proto whether the tombstone is stored as proto
* @param processName the name of the process corresponding to the tombstone
* @param tmpFileLock the lock for reading/writing tmp files
*/
public static void addTombstoneToDropBox(
- Context ctx, File tombstone, Tombstone tombstoneProto, String processName,
+ Context ctx, File tombstone, boolean proto, String processName,
ReentrantLock tmpFileLock) {
final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
if (db == null) {
@@ -352,33 +347,31 @@
// Check if we should rate limit and abort early if needed.
DropboxRateLimiter.RateLimitResult rateLimitResult =
- sDropboxRateLimiter.shouldRateLimit(TAG_TOMBSTONE_PROTO_WITH_HEADERS, processName);
+ sDropboxRateLimiter.shouldRateLimit(
+ proto ? TAG_TOMBSTONE_PROTO_WITH_HEADERS : TAG_TOMBSTONE, processName);
if (rateLimitResult.shouldRateLimit()) return;
HashMap<String, Long> timestamps = readTimestamps();
try {
- // Remove the memory data from the proto.
- Tombstone tombstoneProtoWithoutMemory = removeMemoryFromTombstone(tombstoneProto);
-
- final byte[] tombstoneBytes = tombstoneProtoWithoutMemory.toByteArray();
-
- // Use JNI to call the c++ proto to text converter and add the headers to the tombstone.
- String tombstoneWithoutMemory = new StringBuilder(getBootHeadersToLogAndUpdate())
- .append(rateLimitResult.createHeader())
- .append(getTombstoneText(tombstoneBytes))
- .toString();
-
- // Add the tombstone without memory data to dropbox.
- db.addText(TAG_TOMBSTONE, tombstoneWithoutMemory);
-
- // Add the tombstone proto to dropbox.
- if (recordFileTimestamp(tombstone, timestamps)) {
- tmpFileLock.lock();
- try {
- addAugmentedProtoToDropbox(tombstone, tombstoneBytes, db, rateLimitResult);
- } finally {
- tmpFileLock.unlock();
+ if (proto) {
+ if (recordFileTimestamp(tombstone, timestamps)) {
+ // We need to attach the count indicating the number of dropped dropbox entries
+ // due to rate limiting. Do this by enclosing the proto tombsstone in a
+ // container proto that has the dropped entry count and the proto tombstone as
+ // bytes (to avoid the complexity of reading and writing nested protos).
+ tmpFileLock.lock();
+ try {
+ addAugmentedProtoToDropbox(tombstone, db, rateLimitResult);
+ } finally {
+ tmpFileLock.unlock();
+ }
}
+ } else {
+ // Add the header indicating how many events have been dropped due to rate limiting.
+ final String headers = getBootHeadersToLogAndUpdate()
+ + rateLimitResult.createHeader();
+ addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
+ TAG_TOMBSTONE);
}
} catch (IOException e) {
Slog.e(TAG, "Can't log tombstone", e);
@@ -387,8 +380,11 @@
}
private static void addAugmentedProtoToDropbox(
- File tombstone, byte[] tombstoneBytes, DropBoxManager db,
+ File tombstone, DropBoxManager db,
DropboxRateLimiter.RateLimitResult rateLimitResult) throws IOException {
+ // Read the proto tombstone file as bytes.
+ final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath());
+
final File tombstoneProtoWithHeaders = File.createTempFile(
tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR);
Files.setPosixFilePermissions(
@@ -421,8 +417,6 @@
}
}
- private static native String getTombstoneText(byte[] tombstoneBytes);
-
private static void addLastkToDropBox(
DropBoxManager db, HashMap<String, Long> timestamps,
String headers, String footers, String filename, int maxSize,
@@ -440,31 +434,6 @@
addFileWithFootersToDropBox(db, timestamps, headers, footers, filename, maxSize, tag);
}
- /** Removes memory information from the Tombstone proto. */
- @VisibleForTesting
- public static Tombstone removeMemoryFromTombstone(Tombstone tombstoneProto) {
- Tombstone.Builder tombstoneBuilder = tombstoneProto.toBuilder()
- .clearMemoryMappings()
- .clearThreads()
- .putAllThreads(tombstoneProto.getThreadsMap().entrySet()
- .stream()
- .map(BootReceiver::clearMemoryDump)
- .collect(Collectors.toMap(e->e.getKey(), e->e.getValue())));
-
- if (tombstoneProto.hasSignalInfo()) {
- tombstoneBuilder.setSignalInfo(
- tombstoneProto.getSignalInfo().toBuilder().clearFaultAdjacentMetadata());
- }
-
- return tombstoneBuilder.build();
- }
-
- private static AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread> clearMemoryDump(
- Map.Entry<Integer, TombstoneProtos.Thread> e) {
- return new AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread>(
- e.getKey(), e.getValue().toBuilder().clearMemoryDump().build());
- }
-
private static void addFileToDropBox(
DropBoxManager db, HashMap<String, Long> timestamps,
String headers, String filename, int maxSize, String tag) throws IOException {
diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
index 70bd4b3..c391642 100644
--- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
+++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
@@ -34,11 +34,11 @@
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.SensitiveContentPackages.PackageInfo;
import com.android.server.wm.WindowManagerInternal;
-import java.util.Collections;
import java.util.Set;
/**
@@ -54,6 +54,10 @@
private @Nullable MediaProjectionManager mProjectionManager;
private @Nullable WindowManagerInternal mWindowManager;
+ final Object mSensitiveContentProtectionLock = new Object();
+ @GuardedBy("mSensitiveContentProtectionLock")
+ private boolean mProjectionActive = false;
+
private final MediaProjectionManager.Callback mProjectionCallback =
new MediaProjectionManager.Callback() {
@Override
@@ -132,14 +136,23 @@
}
private void onProjectionStart() {
- StatusBarNotification[] notifications;
- try {
- notifications = mNotificationListener.getActiveNotifications();
- } catch (SecurityException e) {
- Log.e(TAG, "SensitiveContentProtectionManagerService doesn't have access.", e);
- notifications = new StatusBarNotification[0];
+ synchronized (mSensitiveContentProtectionLock) {
+ mProjectionActive = true;
+ updateAppsThatShouldBlockScreenCapture();
}
+ }
+ private void onProjectionEnd() {
+ synchronized (mSensitiveContentProtectionLock) {
+ mProjectionActive = false;
+
+ // notify windowmanager to clear any sensitive notifications observed during projection
+ // session
+ mWindowManager.clearBlockedApps();
+ }
+ }
+
+ private void updateAppsThatShouldBlockScreenCapture() {
RankingMap rankingMap;
try {
rankingMap = mNotificationListener.getCurrentRanking();
@@ -148,41 +161,98 @@
rankingMap = null;
}
- // notify windowmanager of any currently posted sensitive content notifications
- Set<PackageInfo> packageInfos = getSensitivePackagesFromNotifications(
- notifications,
- rankingMap);
-
- mWindowManager.setShouldBlockScreenCaptureForApp(packageInfos);
+ updateAppsThatShouldBlockScreenCapture(rankingMap);
}
- private void onProjectionEnd() {
- // notify windowmanager to clear any sensitive notifications observed during projection
- // session
- mWindowManager.setShouldBlockScreenCaptureForApp(Collections.emptySet());
- }
-
- private Set<PackageInfo> getSensitivePackagesFromNotifications(
- StatusBarNotification[] notifications, RankingMap rankingMap) {
- if (rankingMap == null) {
- Log.w(TAG, "Ranking map not initialized.");
- return Collections.emptySet();
+ private void updateAppsThatShouldBlockScreenCapture(RankingMap rankingMap) {
+ StatusBarNotification[] notifications;
+ try {
+ notifications = mNotificationListener.getActiveNotifications();
+ } catch (SecurityException e) {
+ Log.e(TAG, "SensitiveContentProtectionManagerService doesn't have access.", e);
+ notifications = new StatusBarNotification[0];
}
- Set<PackageInfo> sensitivePackages = new ArraySet<>();
+ // notify windowmanager of any currently posted sensitive content notifications
+ ArraySet<PackageInfo> packageInfos = getSensitivePackagesFromNotifications(
+ notifications, rankingMap);
+
+ mWindowManager.addBlockScreenCaptureForApps(packageInfos);
+ }
+
+ private ArraySet<PackageInfo> getSensitivePackagesFromNotifications(
+ @NonNull StatusBarNotification[] notifications, RankingMap rankingMap) {
+ ArraySet<PackageInfo> sensitivePackages = new ArraySet<>();
+ if (rankingMap == null) {
+ Log.w(TAG, "Ranking map not initialized.");
+ return sensitivePackages;
+ }
+
for (StatusBarNotification sbn : notifications) {
- NotificationListenerService.Ranking ranking =
- rankingMap.getRawRankingObject(sbn.getKey());
- if (ranking != null && ranking.hasSensitiveContent()) {
- PackageInfo info = new PackageInfo(sbn.getPackageName(), sbn.getUid());
+ PackageInfo info = getSensitivePackageFromNotification(sbn, rankingMap);
+ if (info != null) {
sensitivePackages.add(info);
}
}
return sensitivePackages;
}
- // TODO(b/317251408): add trigger that updates on onNotificationPosted,
- // onNotificationRankingUpdate and onListenerConnected
+ private PackageInfo getSensitivePackageFromNotification(StatusBarNotification sbn,
+ RankingMap rankingMap) {
+ if (sbn == null) {
+ Log.w(TAG, "Unable to protect null notification");
+ return null;
+ }
+ if (rankingMap == null) {
+ Log.w(TAG, "Ranking map not initialized.");
+ return null;
+ }
+
+ NotificationListenerService.Ranking ranking = rankingMap.getRawRankingObject(sbn.getKey());
+ if (ranking != null && ranking.hasSensitiveContent()) {
+ return new PackageInfo(sbn.getPackageName(), sbn.getUid());
+ }
+ return null;
+ }
+
@VisibleForTesting
- static class NotificationListener extends NotificationListenerService {}
+ class NotificationListener extends NotificationListenerService {
+ @Override
+ public void onListenerConnected() {
+ super.onListenerConnected();
+ // Projection started before notification listener was connected
+ synchronized (mSensitiveContentProtectionLock) {
+ if (mProjectionActive) {
+ updateAppsThatShouldBlockScreenCapture();
+ }
+ }
+ }
+
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+ super.onNotificationPosted(sbn, rankingMap);
+ synchronized (mSensitiveContentProtectionLock) {
+ if (!mProjectionActive) {
+ return;
+ }
+
+ // notify windowmanager of any currently posted sensitive content notifications
+ PackageInfo packageInfo = getSensitivePackageFromNotification(sbn, rankingMap);
+
+ if (packageInfo != null) {
+ mWindowManager.addBlockScreenCaptureForApps(new ArraySet(Set.of(packageInfo)));
+ }
+ }
+ }
+
+ @Override
+ public void onNotificationRankingUpdate(RankingMap rankingMap) {
+ super.onNotificationRankingUpdate(rankingMap);
+ synchronized (mSensitiveContentProtectionLock) {
+ if (mProjectionActive) {
+ updateAppsThatShouldBlockScreenCapture(rankingMap);
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 2b35231..ea1b0f5 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1076,6 +1076,7 @@
final UserManager userManager = mContext.getSystemService(UserManager.class);
final List<UserInfo> users = userManager.getUsers();
+ extendWatchdogTimeout("#onReset might be slow");
mStorageSessionController.onReset(mVold, () -> {
mHandler.removeCallbacksAndMessages(null);
});
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index f921b0b..bd67cf420 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1126,6 +1126,21 @@
return;
}
+ int phoneId = -1;
+ int subscriptionId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ if(Flags.preventSystemServerAndPhoneDeadlock()) {
+ // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
+ // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ if (DBG) {
+ log("invalid subscription id, use default id");
+ }
+ } else { //APP specify subID
+ subscriptionId = subId;
+ }
+ phoneId = getPhoneIdFromSubId(subscriptionId);
+ }
+
synchronized (mRecords) {
// register
IBinder b = callback.asBinder();
@@ -1145,17 +1160,23 @@
r.renounceFineLocationAccess = renounceFineLocationAccess;
r.callerUid = Binder.getCallingUid();
r.callerPid = Binder.getCallingPid();
- // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
- // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- if (DBG) {
- log("invalid subscription id, use default id");
+
+ if(!Flags.preventSystemServerAndPhoneDeadlock()) {
+ // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
+ // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ if (DBG) {
+ log("invalid subscription id, use default id");
+ }
+ r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ } else {//APP specify subID
+ r.subId = subId;
}
- r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
- } else {//APP specify subID
- r.subId = subId;
+ r.phoneId = getPhoneIdFromSubId(r.subId);
+ } else {
+ r.subId = subscriptionId;
+ r.phoneId = phoneId;
}
- r.phoneId = getPhoneIdFromSubId(r.subId);
r.eventList = events;
if (DBG) {
@@ -1893,8 +1914,14 @@
}
private void notifyCarrierNetworkChangeWithPermission(int subId, boolean active) {
+ int phoneId = -1;
+ if(Flags.preventSystemServerAndPhoneDeadlock()) {
+ phoneId = getPhoneIdFromSubId(subId);
+ }
synchronized (mRecords) {
- int phoneId = getPhoneIdFromSubId(subId);
+ if(!Flags.preventSystemServerAndPhoneDeadlock()) {
+ phoneId = getPhoneIdFromSubId(subId);
+ }
mCarrierNetworkChangeState[phoneId] = active;
if (VDBG) {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 66a10e4..cd8be33 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -16,8 +16,10 @@
package com.android.server;
+import static android.app.Flags.modesApi;
import static android.app.UiModeManager.ContrastUtils.CONTRAST_DEFAULT_VALUE;
import static android.app.UiModeManager.DEFAULT_PRIORITY;
+import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF;
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
@@ -50,6 +52,7 @@
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.UiModeManager;
+import android.app.UiModeManager.AttentionModeThemeOverlayType;
import android.app.UiModeManager.NightModeCustomReturnType;
import android.app.UiModeManager.NightModeCustomType;
import android.content.BroadcastReceiver;
@@ -134,6 +137,7 @@
private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private int mNightMode = UiModeManager.MODE_NIGHT_NO;
private int mNightModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
+ private int mAttentionModeThemeOverlay = UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF;
private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0);
private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0);
private LocalTime mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME;
@@ -839,6 +843,8 @@
? customModeType
: MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
mNightMode = mode;
+ //deactivates AttentionMode if user toggles DarkTheme
+ mAttentionModeThemeOverlay = MODE_ATTENTION_THEME_OVERLAY_OFF;
resetNightModeOverrideLocked();
persistNightMode(user);
// on screen off will update configuration instead
@@ -879,6 +885,29 @@
}
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+ @Override
+ public void setAttentionModeThemeOverlay(
+ @AttentionModeThemeOverlayType int attentionModeThemeOverlayType) {
+ setAttentionModeThemeOverlay_enforcePermission();
+
+ synchronized (mLock) {
+ if (mAttentionModeThemeOverlay != attentionModeThemeOverlayType) {
+ mAttentionModeThemeOverlay = attentionModeThemeOverlayType;
+ Binder.withCleanCallingIdentity(()-> updateLocked(0, 0));
+ }
+ }
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+ @Override
+ public @AttentionModeThemeOverlayType int getAttentionModeThemeOverlay() {
+ getAttentionModeThemeOverlay_enforcePermission();
+ synchronized (mLock) {
+ return mAttentionModeThemeOverlay;
+ }
+ }
+
@Override
public void setApplicationNightMode(@UiModeManager.NightMode int mode) {
switch (mode) {
@@ -1406,7 +1435,7 @@
pw.print(Shell.nightModeToStr(mNightMode, mNightModeCustomType)); pw.print(") ");
pw.print(" mOverrideOn/Off="); pw.print(mOverrideNightModeOn);
pw.print("/"); pw.print(mOverrideNightModeOff);
-
+ pw.print(" mAttentionModeThemeOverlay="); pw.print(mAttentionModeThemeOverlay);
pw.print(" mNightModeLocked="); pw.println(mNightModeLocked);
pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled);
@@ -1685,7 +1714,7 @@
}
@UiModeManager.NightMode
- private int getComputedUiModeConfiguration(@UiModeManager.NightMode int uiMode) {
+ private int getComputedUiModeConfiguration(int uiMode) {
uiMode |= mComputedNightMode ? Configuration.UI_MODE_NIGHT_YES
: Configuration.UI_MODE_NIGHT_NO;
uiMode &= mComputedNightMode ? ~Configuration.UI_MODE_NIGHT_NO
@@ -1980,18 +2009,26 @@
}
private void updateComputedNightModeLocked(boolean activate) {
- mComputedNightMode = activate;
- if (mNightMode == MODE_NIGHT_YES || mNightMode == UiModeManager.MODE_NIGHT_NO) {
- return;
+ boolean newComputedValue = activate;
+ if (mNightMode != MODE_NIGHT_YES && mNightMode != UiModeManager.MODE_NIGHT_NO) {
+ if (mOverrideNightModeOn && !newComputedValue) {
+ newComputedValue = true;
+ } else if (mOverrideNightModeOff && newComputedValue) {
+ newComputedValue = false;
+ }
}
- if (mOverrideNightModeOn && !mComputedNightMode) {
- mComputedNightMode = true;
- return;
+
+ if (modesApi()) {
+ // Computes final night mode values based on Attention Mode.
+ mComputedNightMode = switch (mAttentionModeThemeOverlay) {
+ case (UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT) -> true;
+ case (UiModeManager.MODE_ATTENTION_THEME_OVERLAY_DAY) -> false;
+ default -> newComputedValue; // case OFF
+ };
+ } else {
+ mComputedNightMode = newComputedValue;
}
- if (mOverrideNightModeOff && mComputedNightMode) {
- mComputedNightMode = false;
- return;
- }
+
if (mNightMode != MODE_NIGHT_AUTO || (mTwilightManager != null
&& mTwilightManager.getLastTwilightState() != null)) {
resetNightModeOverrideLocked();
diff --git a/services/core/java/com/android/server/adaptiveauth/OWNERS b/services/core/java/com/android/server/adaptiveauth/OWNERS
new file mode 100644
index 0000000..b188105
--- /dev/null
+++ b/services/core/java/com/android/server/adaptiveauth/OWNERS
@@ -0,0 +1 @@
+hainingc@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 979449f..9b1fade 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -5400,13 +5400,13 @@
return msg;
}
mAm.mProcessList.getAppStartInfoTracker().handleProcessServiceStart(startTimeNs, app, r,
- hostingRecord, true);
+ true);
if (isolated) {
r.isolationHostProc = app;
}
} else {
mAm.mProcessList.getAppStartInfoTracker().handleProcessServiceStart(startTimeNs, app, r,
- hostingRecord, false);
+ false);
}
if (r.fgRequired) {
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index b90e5cd..c857235 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -81,18 +81,18 @@
private static final int FOREACH_ACTION_REMOVE_ITEM = 1;
private static final int FOREACH_ACTION_STOP_ITERATION = 2;
- private static final int APP_START_INFO_HISTORY_LIST_SIZE = 16;
+ @VisibleForTesting static final int APP_START_INFO_HISTORY_LIST_SIZE = 16;
@VisibleForTesting static final String APP_START_STORE_DIR = "procstartstore";
@VisibleForTesting static final String APP_START_INFO_FILE = "procstartinfo";
- private final Object mLock = new Object();
+ @VisibleForTesting final Object mLock = new Object();
- private boolean mEnabled = false;
+ @VisibleForTesting boolean mEnabled = false;
/** Initialized in {@link #init} and read-only after that. */
- private ActivityManagerService mService;
+ @VisibleForTesting ActivityManagerService mService;
/** Initialized in {@link #init} and read-only after that. */
private Handler mHandler;
@@ -112,7 +112,7 @@
*
* <p>Initialized in {@link #init} and read-only after that. No lock is needed.
*/
- private int mAppStartInfoHistoryListSize;
+ @VisibleForTesting int mAppStartInfoHistoryListSize;
@GuardedBy("mLock")
private final ProcessMap<AppStartInfoContainer> mData;
@@ -146,7 +146,8 @@
* Key is timestamp of launch from {@link #ActivityMetricsLaunchObserver}.
*/
@GuardedBy("mLock")
- private ArrayMap<Long, ApplicationStartInfo> mInProgRecords = new ArrayMap<>();
+ @VisibleForTesting
+ final ArrayMap<Long, ApplicationStartInfo> mInProgRecords = new ArrayMap<>();
AppStartInfoTracker() {
mCallbacks = new SparseArray<>();
@@ -229,7 +230,7 @@
ApplicationStartInfo info = mInProgRecords.get(id);
info.setStartType((int) temperature);
addBaseFieldsFromProcessRecord(info, app);
- addStartInfoLocked(info);
+ mInProgRecords.put(id, addStartInfoLocked(info));
} else {
mInProgRecords.remove(id);
}
@@ -262,6 +263,7 @@
ApplicationStartInfo info = mInProgRecords.get(id);
info.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN);
info.setLaunchMode(launchMode);
+ checkCompletenessAndCallback(info);
}
}
@@ -281,7 +283,7 @@
}
public void handleProcessServiceStart(long startTimeNs, ProcessRecord app,
- ServiceRecord serviceRecord, HostingRecord hostingRecord, boolean cold) {
+ ServiceRecord serviceRecord, boolean cold) {
synchronized (mLock) {
if (!mEnabled) {
return;
@@ -297,7 +299,9 @@
&& serviceRecord.permission.contains("android.permission.BIND_JOB_SERVICE")
? ApplicationStartInfo.START_REASON_JOB
: ApplicationStartInfo.START_REASON_SERVICE);
- start.setIntent(serviceRecord.intent.getIntent());
+ if (serviceRecord.intent != null) {
+ start.setIntent(serviceRecord.intent.getIntent());
+ }
addStartInfoLocked(start);
}
}
@@ -378,6 +382,7 @@
start.setPackageUid(app.info.uid);
start.setDefiningUid(definingUid > 0 ? definingUid : app.info.uid);
start.setProcessName(app.processName);
+ start.setPackageName(app.info.packageName);
}
void reportApplicationOnCreateTimeNanos(ProcessRecord app, long timeNs) {
@@ -419,12 +424,12 @@
}
private void addTimestampToStart(ProcessRecord app, long timeNs, int key) {
- addTimestampToStart(app.processName, app.uid, timeNs, key);
+ addTimestampToStart(app.info.packageName, app.uid, timeNs, key);
}
- private void addTimestampToStart(String processName, int uid, long timeNs, int key) {
+ private void addTimestampToStart(String packageName, int uid, long timeNs, int key) {
synchronized (mLock) {
- AppStartInfoContainer container = mData.get(processName, uid);
+ AppStartInfoContainer container = mData.get(packageName, uid);
if (container == null) {
// Record was not created, discard new data.
return;
@@ -443,11 +448,11 @@
final ApplicationStartInfo info = new ApplicationStartInfo(raw);
- AppStartInfoContainer container = mData.get(raw.getProcessName(), raw.getRealUid());
+ AppStartInfoContainer container = mData.get(raw.getPackageName(), raw.getRealUid());
if (container == null) {
container = new AppStartInfoContainer(mAppStartInfoHistoryListSize);
container.mUid = info.getRealUid();
- mData.put(raw.getProcessName(), raw.getRealUid(), container);
+ mData.put(raw.getPackageName(), raw.getRealUid(), container);
}
container.addStartInfoLocked(info);
@@ -486,6 +491,9 @@
if (!mEnabled) {
return;
}
+ if (maxNum == 0) {
+ maxNum = APP_START_INFO_HISTORY_LIST_SIZE;
+ }
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -891,6 +899,7 @@
mProcStartInfoFile.delete();
}
mData.getMap().clear();
+ mInProgRecords.clear();
}
}
@@ -960,6 +969,10 @@
/** Convenience method to obtain timestamp of beginning of start.*/
private static long getStartTimestamp(ApplicationStartInfo startInfo) {
+ if (startInfo.getStartupTimestamps() == null
+ || !startInfo.getStartupTimestamps().containsKey(START_TIMESTAMP_LAUNCH)) {
+ return -1;
+ }
return startInfo.getStartupTimestamps().get(START_TIMESTAMP_LAUNCH);
}
@@ -997,7 +1010,6 @@
if (oldestIndex >= 0) {
mInfos.remove(oldestIndex);
}
- mInfos.remove(0);
}
mInfos.add(info);
Collections.sort(mInfos, (a, b) ->
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 8fa0089..2ed217a 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2882,6 +2882,10 @@
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
+ if (proxyAttributionTag != null
+ && !isAttributionTagDefined(packageName, proxyPackageName, proxyAttributionTag)) {
+ proxyAttributionTag = null;
+ }
synchronized (this) {
final Ops ops = getOpsLocked(uid, packageName, attributionTag,
@@ -3476,6 +3480,10 @@
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
+ if (proxyAttributionTag != null
+ && !isAttributionTagDefined(packageName, proxyPackageName, proxyAttributionTag)) {
+ proxyAttributionTag = null;
+ }
boolean isRestricted = false;
int startType = START_TYPE_FAILED;
@@ -4329,6 +4337,36 @@
return false;
}
+ /**
+ * Checks to see if the attribution tag is defined in either package or proxyPackage.
+ * This method is intended for ProxyAttributionTag validation and returns false
+ * if it does not exist in either one of them.
+ *
+ * @param packageName Name of the package
+ * @param proxyPackageName Name of the proxy package
+ * @param attributionTag attribution tag to be checked
+ *
+ * @return boolean specifying if attribution tag is valid or not
+ */
+ private boolean isAttributionTagDefined(@Nullable String packageName,
+ @Nullable String proxyPackageName,
+ @Nullable String attributionTag) {
+ if (packageName == null) {
+ return false;
+ } else if (attributionTag == null) {
+ return true;
+ }
+ PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
+ if (proxyPackageName != null) {
+ AndroidPackage proxyPkg = pmInt.getPackage(proxyPackageName);
+ if (proxyPkg != null && isAttributionInPackage(proxyPkg, attributionTag)) {
+ return true;
+ }
+ }
+ AndroidPackage pkg = pmInt.getPackage(packageName);
+ return isAttributionInPackage(pkg, attributionTag);
+ }
+
private void logVerifyAndGetBypassFailure(int uid, @NonNull SecurityException e,
@NonNull String methodName) {
if (Process.isIsolated(uid)) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
index 2957935..a763251 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
@@ -77,8 +77,6 @@
return this;
}
- // TODO: The behavior of InputMethodSubtype#overridesImplicitlyEnabledSubtype() should be
- // documented more clearly.
InputMethodListBuilder fillAuxiliaryImes(ArrayList<InputMethodInfo> imis, Context context) {
// If one or more auxiliary input methods are available, OK to stop populating the list.
for (final InputMethodInfo imi : mInputMethodSet) {
diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
index 71a6b5e..ab650af 100644
--- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
+++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
@@ -16,7 +16,8 @@
package com.android.server.notification;
-import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
+import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT;
+import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF;
import android.app.UiModeManager;
import android.app.WallpaperManager;
@@ -128,10 +129,9 @@
private void updateNightModeImmediately(boolean useNightMode) {
Binder.withCleanCallingIdentity(() -> {
- // TODO: b/314285749 - Placeholder; use real APIs when available.
- mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
- mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
- useNightMode);
+ mUiModeManager.setAttentionModeThemeOverlay(
+ useNightMode ? MODE_ATTENTION_THEME_OVERLAY_NIGHT
+ : MODE_ATTENTION_THEME_OVERLAY_OFF);
});
}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index 9ce3cb3..f6e7ef3 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -41,13 +41,14 @@
import android.system.StructStat;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoParseException;
import com.android.internal.annotations.GuardedBy;
import com.android.server.BootReceiver;
import com.android.server.ServiceThread;
import com.android.server.os.TombstoneProtos.Cause;
import com.android.server.os.TombstoneProtos.Tombstone;
-import com.android.server.os.protobuf.CodedInputStream;
import libcore.io.IoUtils;
@@ -129,21 +130,18 @@
return;
}
+ String processName = "UNKNOWN";
final boolean isProtoFile = filename.endsWith(".pb");
- if (!isProtoFile) {
- return;
- }
+ File protoPath = isProtoFile ? path : new File(path.getAbsolutePath() + ".pb");
- Optional<ParsedTombstone> parsedTombstone = handleProtoTombstone(path, true);
+ Optional<TombstoneFile> parsedTombstone = handleProtoTombstone(protoPath, isProtoFile);
if (parsedTombstone.isPresent()) {
- BootReceiver.addTombstoneToDropBox(
- mContext, path, parsedTombstone.get().getTombstone(),
- parsedTombstone.get().getProcessName(), mTmpFileLock);
+ processName = parsedTombstone.get().getProcessName();
}
+ BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName, mTmpFileLock);
}
- private Optional<ParsedTombstone> handleProtoTombstone(
- File path, boolean addToList) {
+ private Optional<TombstoneFile> handleProtoTombstone(File path, boolean addToList) {
final String filename = path.getName();
if (!filename.endsWith(".pb")) {
Slog.w(TAG, "unexpected tombstone name: " + path);
@@ -173,7 +171,7 @@
return Optional.empty();
}
- final Optional<ParsedTombstone> parsedTombstone = TombstoneFile.parse(pfd);
+ final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
if (!parsedTombstone.isPresent()) {
IoUtils.closeQuietly(pfd);
return Optional.empty();
@@ -186,7 +184,7 @@
previous.dispose();
}
- mTombstones.put(number, parsedTombstone.get().getTombstoneFile());
+ mTombstones.put(number, parsedTombstone.get());
}
}
@@ -334,27 +332,6 @@
}
}
- static class ParsedTombstone {
- TombstoneFile mTombstoneFile;
- Tombstone mTombstone;
- ParsedTombstone(TombstoneFile tombstoneFile, Tombstone tombstone) {
- mTombstoneFile = tombstoneFile;
- mTombstone = tombstone;
- }
-
- public String getProcessName() {
- return mTombstoneFile.getProcessName();
- }
-
- public TombstoneFile getTombstoneFile() {
- return mTombstoneFile;
- }
-
- public Tombstone getTombstone() {
- return mTombstone;
- }
- }
-
static class TombstoneFile {
final ParcelFileDescriptor mPfd;
@@ -437,21 +414,67 @@
}
}
- static Optional<ParsedTombstone> parse(ParcelFileDescriptor pfd) {
- Tombstone tombstoneProto;
- try (FileInputStream is = new FileInputStream(pfd.getFileDescriptor())) {
- final byte[] tombstoneBytes = is.readAllBytes();
+ static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
+ final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
+ final ProtoInputStream stream = new ProtoInputStream(is);
- tombstoneProto = Tombstone.parseFrom(
- CodedInputStream.newInstance(tombstoneBytes));
- } catch (IOException ex) {
+ int pid = 0;
+ int uid = 0;
+ String processName = null;
+ String crashReason = "";
+ String selinuxLabel = "";
+
+ try {
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) Tombstone.PID:
+ pid = stream.readInt(Tombstone.PID);
+ break;
+
+ case (int) Tombstone.UID:
+ uid = stream.readInt(Tombstone.UID);
+ break;
+
+ case (int) Tombstone.COMMAND_LINE:
+ if (processName == null) {
+ processName = stream.readString(Tombstone.COMMAND_LINE);
+ }
+ break;
+
+ case (int) Tombstone.CAUSES:
+ if (!crashReason.equals("")) {
+ // Causes appear in decreasing order of likelihood. For now we only
+ // want the most likely crash reason here, so ignore all others.
+ break;
+ }
+ long token = stream.start(Tombstone.CAUSES);
+ cause:
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) Cause.HUMAN_READABLE:
+ crashReason = stream.readString(Cause.HUMAN_READABLE);
+ break cause;
+
+ default:
+ break;
+ }
+ }
+ stream.end(token);
+ break;
+
+ case (int) Tombstone.SELINUX_LABEL:
+ selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
+ break;
+
+ default:
+ break;
+ }
+ }
+ } catch (IOException | ProtoParseException ex) {
Slog.e(TAG, "Failed to parse tombstone", ex);
return Optional.empty();
}
- int pid = tombstoneProto.getPid();
- int uid = tombstoneProto.getUid();
-
if (!UserHandle.isApp(uid)) {
Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
return Optional.empty();
@@ -468,7 +491,6 @@
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
- String selinuxLabel = tombstoneProto.getSelinuxLabel();
if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
return Optional.empty();
@@ -480,30 +502,11 @@
result.mAppId = appId;
result.mPid = pid;
result.mUid = uid;
- result.mProcessName = getCmdLineProcessName(tombstoneProto);
+ result.mProcessName = processName == null ? "" : processName;
result.mTimestampMs = timestampMs;
- result.mCrashReason = getCrashReason(tombstoneProto);
+ result.mCrashReason = crashReason;
- return Optional.of(new ParsedTombstone(result, tombstoneProto));
- }
-
- private static String getCmdLineProcessName(Tombstone tombstoneProto) {
- for (String cmdline : tombstoneProto.getCommandLineList()) {
- if (cmdline != null) {
- return cmdline;
- }
- }
- return "";
- }
-
- private static String getCrashReason(Tombstone tombstoneProto) {
- for (Cause cause : tombstoneProto.getCausesList()) {
- if (cause.getHumanReadable() != null
- && !cause.getHumanReadable().equals("")) {
- return cause.getHumanReadable();
- }
- }
- return "";
+ return Optional.of(result);
}
public IParcelFileDescriptorRetriever getPfdRetriever() {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 9915554..ac826af 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -32,6 +32,8 @@
import static android.content.pm.LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS;
import static android.content.pm.LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS;
+import static com.android.server.pm.PackageArchiver.isArchivingEnabled;
+
import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -56,7 +58,6 @@
import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.Flags;
import android.content.pm.ILauncherApps;
import android.content.pm.IOnAppsChangedListener;
import android.content.pm.IPackageInstallerCallback;
@@ -507,7 +508,8 @@
if (!canAccessProfile(userId, "cannot get shouldHideFromSuggestions")) {
return false;
}
- if (Flags.archiving() && packageName != null && isPackageArchived(packageName, user)) {
+ if (isArchivingEnabled() && packageName != null
+ && isPackageArchived(packageName, user)) {
return true;
}
if (mPackageManagerInternal.filterAppAccess(
@@ -530,7 +532,7 @@
.addCategory(Intent.CATEGORY_LAUNCHER)
.setPackage(packageName),
user);
- if (Flags.archiving()) {
+ if (isArchivingEnabled()) {
launcherActivities =
getActivitiesForArchivedApp(packageName, user, launcherActivities);
}
@@ -701,7 +703,7 @@
callingUid,
user.getIdentifier());
if (activityInfo == null) {
- if (Flags.archiving()) {
+ if (isArchivingEnabled()) {
return getMatchingArchivedAppActivityInfo(component, user);
}
return null;
@@ -984,7 +986,7 @@
long callingFlag =
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
- if (Flags.archiving()) {
+ if (isArchivingEnabled()) {
callingFlag |= PackageManager.MATCH_ARCHIVED_PACKAGES;
}
final PackageInfo info =
@@ -1457,7 +1459,7 @@
if (!canAccessProfile(user.getIdentifier(), "Cannot check component")) {
return false;
}
- if (Flags.archiving() && component != null && component.getPackageName() != null) {
+ if (isArchivingEnabled() && component != null && component.getPackageName() != null) {
List<LauncherActivityInfoInternal> archiveActivities =
generateLauncherActivitiesForArchivedApp(component.getPackageName(), user);
if (!archiveActivities.isEmpty()) {
@@ -1788,7 +1790,7 @@
}
if (!canLaunch
&& includeArchivedApps
- && Flags.archiving()
+ && isArchivingEnabled()
&& getMatchingArchivedAppActivityInfo(component, user) != null) {
launchIntent.setPackage(null);
launchIntent.setComponent(component);
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index b18f2bf..09a91ed 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -53,11 +53,11 @@
import android.content.pm.ArchivedActivityParcel;
import android.content.pm.ArchivedPackageInfo;
import android.content.pm.ArchivedPackageParcel;
+import android.content.pm.Flags;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.DeleteFlags;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.VersionedPackage;
@@ -78,6 +78,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SELinux;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ExceptionUtils;
@@ -173,12 +174,15 @@
return userState.getArchiveState() != null && !userState.isInstalled();
}
+ public static boolean isArchivingEnabled() {
+ return Flags.archiving() || SystemProperties.getBoolean("pm.archiving.enabled", false);
+ }
+
void requestArchive(
@NonNull String packageName,
@NonNull String callerPackageName,
@NonNull IntentSender intentSender,
- @NonNull UserHandle userHandle,
- @DeleteFlags int flags) {
+ @NonNull UserHandle userHandle) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(callerPackageName);
Objects.requireNonNull(intentSender);
@@ -218,7 +222,7 @@
new VersionedPackage(packageName,
PackageManager.VERSION_CODE_HIGHEST),
callerPackageName,
- DELETE_ARCHIVE | DELETE_KEEP_DATA | flags,
+ DELETE_ARCHIVE | DELETE_KEEP_DATA,
intentSender,
userId,
binderUid);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index fdcd28b..7bf9fe7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -30,6 +30,7 @@
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
+import static com.android.server.pm.PackageArchiver.isArchivingEnabled;
import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -826,7 +827,7 @@
}
params.installFlags &= ~PackageManager.INSTALL_UNARCHIVE;
- if (Flags.archiving() && params.appPackageName != null) {
+ if (isArchivingEnabled() && params.appPackageName != null) {
PackageStateInternal ps = mPm.snapshotComputer().getPackageStateInternal(
params.appPackageName, SYSTEM_UID);
if (ps != null
@@ -1034,7 +1035,7 @@
private int getExistingDraftSessionIdInternal(int installerUid,
SessionParams sessionParams, int userId) {
String appPackageName = sessionParams.appPackageName;
- if (!Flags.archiving() || installerUid == INVALID_UID || appPackageName == null) {
+ if (!isArchivingEnabled() || installerUid == INVALID_UID || appPackageName == null) {
return SessionInfo.INVALID_ID;
}
@@ -1407,14 +1408,11 @@
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
statusReceiver, versionedPackage.getPackageName(),
canSilentlyInstallPackage, userId, mPackageArchiver, flags);
- final boolean shouldShowConfirmationDialog =
- (flags & PackageManager.DELETE_SHOW_DIALOG) != 0;
- if (!shouldShowConfirmationDialog
- && mContext.checkCallingOrSelfPermission(Manifest.permission.DELETE_PACKAGES)
- == PackageManager.PERMISSION_GRANTED) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.DELETE_PACKAGES)
+ == PackageManager.PERMISSION_GRANTED) {
// Sweet, call straight through!
mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
- } else if (!shouldShowConfirmationDialog && canSilentlyInstallPackage) {
+ } else if (canSilentlyInstallPackage) {
// Allow the device owner and affiliated profile owner to silently delete packages
// Need to clear the calling identity to get DELETE_PACKAGES permission
final long ident = Binder.clearCallingIdentity();
@@ -1656,10 +1654,8 @@
@NonNull String packageName,
@NonNull String callerPackageName,
@NonNull IntentSender intentSender,
- @NonNull UserHandle userHandle,
- @DeleteFlags int flags) {
- mPackageArchiver.requestArchive(packageName, callerPackageName, intentSender,
- userHandle, flags);
+ @NonNull UserHandle userHandle) {
+ mPackageArchiver.requestArchive(packageName, callerPackageName, intentSender, userHandle);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 5724ee0..ca00c84 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -4645,7 +4645,7 @@
try {
mInterface.getPackageInstaller().requestArchive(packageName,
/* callerPackageName= */ "", receiver.getIntentSender(),
- new UserHandle(translatedUserId), 0);
+ new UserHandle(translatedUserId));
} catch (Exception e) {
pw.println("Failure [" + e.getMessage() + "]");
return 1;
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 3659cb7..70352be 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -392,8 +392,7 @@
// Delete from mSettings
final SparseBooleanArray changedUsers = new SparseBooleanArray();
synchronized (mPm.mLock) {
- mPm.mSettings.removePackageLPw(packageName);
- outInfo.mIsAppIdRemoved = true;
+ outInfo.mIsAppIdRemoved = mPm.mSettings.removePackageAndAppIdLPw(packageName);
if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) {
final SharedUserSetting sus = mPm.mSettings.getSharedUserSettingLPr(deletedPs);
// If we don't have a disabled system package to reinstall, the package is
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b286b12..a97652c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -92,7 +92,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.pm.parsing.pkg.PackageImpl;
import com.android.internal.pm.pkg.component.ParsedComponent;
import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.internal.pm.pkg.component.ParsedPermission;
@@ -1460,22 +1459,28 @@
return false;
}
- int removePackageLPw(String name) {
+
+ /**
+ * Remove package from mPackages and its corresponding AppId.
+ *
+ * @return True if the AppId has been removed.
+ * False if the app doesn't exist, or if the app has a shared UID and there are other apps that
+ * still use the same shared UID even after the target app is removed.
+ */
+ boolean removePackageAndAppIdLPw(String name) {
final PackageSetting p = mPackages.remove(name);
if (p != null) {
removeInstallerPackageStatus(name);
SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p);
if (sharedUserSetting != null) {
sharedUserSetting.removePackage(p);
- if (checkAndPruneSharedUserLPw(sharedUserSetting, false)) {
- return sharedUserSetting.mAppId;
- }
+ return checkAndPruneSharedUserLPw(sharedUserSetting, false);
} else {
removeAppIdLPw(p.getAppId());
- return p.getAppId();
+ return true;
}
}
- return -1;
+ return false;
}
/**
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7b0a69b..5405590 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static android.content.Intent.ACTION_SCREEN_OFF;
+import static android.content.Intent.ACTION_SCREEN_ON;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.UserManager.DEV_CREATE_OVERRIDE_PROPERTY;
@@ -41,6 +43,7 @@
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -74,8 +77,10 @@
import android.content.pm.parsing.FrameworkParsingPackageUtils;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.multiuser.Flags;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -83,6 +88,7 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IProgressListener;
import android.os.IUserManager;
@@ -91,6 +97,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -174,6 +181,8 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -296,6 +305,12 @@
private static final long BOOT_USER_SET_TIMEOUT_MS = 300_000;
+ /**
+ * The time duration (in milliseconds) post device inactivity after which the private space
+ * should be auto-locked if the corresponding settings option is selected by the user.
+ */
+ private static final long PRIVATE_SPACE_AUTO_LOCK_INACTIVITY_TIMEOUT_MS = 5 * 60 * 1000;
+
// Tron counters
private static final String TRON_GUEST_CREATED = "users_guest_created";
private static final String TRON_USER_CREATED = "users_user_created";
@@ -321,6 +336,8 @@
private final Handler mHandler;
+ private final ThreadPoolExecutor mInternalExecutor;
+
private final File mUsersDir;
private final File mUserListFile;
@@ -522,6 +539,36 @@
private final LockPatternUtils mLockPatternUtils;
+ private KeyguardManager.KeyguardLockedStateListener mKeyguardLockedStateListener;
+
+ /** Token to identify and remove already scheduled private space auto-lock messages */
+ private static final Object PRIVATE_SPACE_AUTO_LOCK_MESSAGE_TOKEN = new Object();
+
+ /** Content observer to get callbacks for privte space autolock settings changes */
+ private final SettingsObserver mPrivateSpaceAutoLockSettingsObserver;
+
+ private final class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (isAutoLockForPrivateSpaceEnabled()) {
+ final String path = uri.getLastPathSegment();
+ if (TextUtils.equals(path, Settings.Secure.PRIVATE_SPACE_AUTO_LOCK)) {
+ int autoLockPreference =
+ Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK,
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_NEVER,
+ getMainUserIdUnchecked());
+ Slog.i(LOG_TAG, "Auto-lock settings changed to " + autoLockPreference);
+ setOrUpdateAutoLockPreferenceForPrivateProfile(autoLockPreference);
+ }
+ }
+ }
+ }
+
private final String ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK =
"com.android.server.pm.DISABLE_QUIET_MODE_AFTER_UNLOCK";
@@ -534,12 +581,168 @@
final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT, android.content.IntentSender.class);
final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
final String callingPackage = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
- // Call setQuietModeEnabled on bg thread to avoid ANR
- BackgroundThread.getHandler().post(() ->
- setQuietModeEnabled(userId, false, target, callingPackage));
+ setQuietModeEnabledAsync(userId, false, target, callingPackage);
}
};
+ /** Checks if the device inactivity broadcast receiver is already registered*/
+ private boolean mIsDeviceInactivityBroadcastReceiverRegistered = false;
+
+ private final BroadcastReceiver mDeviceInactivityBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (isAutoLockForPrivateSpaceEnabled()) {
+ if (ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ maybeScheduleMessageToAutoLockPrivateSpace();
+ } else if (ACTION_SCREEN_ON.equals(intent.getAction())) {
+ // Remove any queued messages since the device is interactive again
+ mHandler.removeCallbacksAndMessages(PRIVATE_SPACE_AUTO_LOCK_MESSAGE_TOKEN);
+ }
+ }
+ }
+ };
+
+ @VisibleForTesting
+ void maybeScheduleMessageToAutoLockPrivateSpace() {
+ // No action needed if auto-lock on inactivity not selected
+ int privateSpaceAutoLockPreference =
+ Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK,
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_NEVER,
+ getMainUserIdUnchecked());
+ if (privateSpaceAutoLockPreference
+ != Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY) {
+ return;
+ }
+ int privateProfileUserId = getPrivateProfileUserId();
+ if (privateProfileUserId != UserHandle.USER_NULL) {
+ scheduleMessageToAutoLockPrivateSpace(privateProfileUserId,
+ PRIVATE_SPACE_AUTO_LOCK_MESSAGE_TOKEN,
+ PRIVATE_SPACE_AUTO_LOCK_INACTIVITY_TIMEOUT_MS);
+ }
+ }
+
+ @VisibleForTesting
+ void scheduleMessageToAutoLockPrivateSpace(int userId, Object token,
+ long delayInMillis) {
+ mHandler.postDelayed(() -> {
+ final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+ if (powerManager != null && !powerManager.isInteractive()) {
+ Slog.i(LOG_TAG, "Auto-locking private space with user-id " + userId);
+ setQuietModeEnabledAsync(userId, true,
+ /* target */ null, mContext.getPackageName());
+ } else {
+ Slog.i(LOG_TAG, "Device is interactive, skipping auto-lock");
+ }
+ }, token, delayInMillis);
+ }
+
+ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+ private void initializeAndRegisterKeyguardLockedStateListener() {
+ mKeyguardLockedStateListener = this::tryAutoLockingPrivateSpaceOnKeyguardChanged;
+ // Register with keyguard to send locked state events to the listener initialized above
+ try {
+ final KeyguardManager keyguardManager =
+ mContext.getSystemService(KeyguardManager.class);
+ Slog.i(LOG_TAG, "Adding keyguard locked state listener");
+ keyguardManager.addKeyguardLockedStateListener(new HandlerExecutor(mHandler),
+ mKeyguardLockedStateListener);
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "Error adding keyguard locked listener ", e);
+ }
+ }
+
+ @VisibleForTesting
+ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
+ void setOrUpdateAutoLockPreferenceForPrivateProfile(
+ @Settings.Secure.PrivateSpaceAutoLockOption int autoLockPreference) {
+ int privateProfileUserId = getPrivateProfileUserId();
+ if (privateProfileUserId == UserHandle.USER_NULL) {
+ Slog.e(LOG_TAG, "Auto-lock preference updated but private space user not found");
+ return;
+ }
+
+ if (autoLockPreference == Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY) {
+ // Register inactivity broadcast
+ if (!mIsDeviceInactivityBroadcastReceiverRegistered) {
+ Slog.i(LOG_TAG, "Registering device inactivity broadcast receivers");
+ mContext.registerReceiver(mDeviceInactivityBroadcastReceiver,
+ new IntentFilter(ACTION_SCREEN_OFF),
+ null, mHandler);
+
+ mContext.registerReceiver(mDeviceInactivityBroadcastReceiver,
+ new IntentFilter(ACTION_SCREEN_ON),
+ null, mHandler);
+
+ mIsDeviceInactivityBroadcastReceiverRegistered = true;
+ }
+ } else {
+ // Unregister device inactivity broadcasts
+ if (mIsDeviceInactivityBroadcastReceiverRegistered) {
+ Slog.i(LOG_TAG, "Removing device inactivity broadcast receivers");
+ mHandler.removeCallbacksAndMessages(PRIVATE_SPACE_AUTO_LOCK_MESSAGE_TOKEN);
+ mContext.unregisterReceiver(mDeviceInactivityBroadcastReceiver);
+ mIsDeviceInactivityBroadcastReceiverRegistered = false;
+ }
+ }
+
+ if (autoLockPreference == Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK) {
+ // Initialize and add keyguard state listener
+ initializeAndRegisterKeyguardLockedStateListener();
+ } else {
+ // Remove keyguard state listener
+ try {
+ final KeyguardManager keyguardManager =
+ mContext.getSystemService(KeyguardManager.class);
+ Slog.i(LOG_TAG, "Removing keyguard locked state listener");
+ keyguardManager.removeKeyguardLockedStateListener(mKeyguardLockedStateListener);
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "Error adding keyguard locked state listener ", e);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void tryAutoLockingPrivateSpaceOnKeyguardChanged(boolean isKeyguardLocked) {
+ if (isAutoLockForPrivateSpaceEnabled()) {
+ int autoLockPreference = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK,
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_NEVER,
+ getMainUserIdUnchecked());
+ boolean isAutoLockOnDeviceLockSelected =
+ autoLockPreference == Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK;
+ if (isKeyguardLocked && isAutoLockOnDeviceLockSelected) {
+ int privateProfileUserId = getPrivateProfileUserId();
+ if (privateProfileUserId != UserHandle.USER_NULL) {
+ Slog.i(LOG_TAG, "Auto-locking private space with user-id "
+ + privateProfileUserId);
+ setQuietModeEnabledAsync(privateProfileUserId,
+ /* enableQuietMode */true, /* target */ null,
+ mContext.getPackageName());
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void setQuietModeEnabledAsync(@UserIdInt int userId, boolean enableQuietMode,
+ IntentSender target, @Nullable String callingPackage) {
+ if (android.multiuser.Flags.moveQuietModeOperationsToSeparateThread()) {
+ // Call setQuietModeEnabled on a separate thread. Calling this operation on the main
+ // thread can cause ANRs, posting on a BackgroundThread can result in delays
+ Slog.d(LOG_TAG, "Calling setQuietModeEnabled for user " + userId
+ + " on a separate thread");
+ mInternalExecutor.execute(() -> setQuietModeEnabled(userId, enableQuietMode, target,
+ callingPackage));
+ } else {
+ // Call setQuietModeEnabled on bg thread to avoid ANR
+ BackgroundThread.getHandler().post(
+ () -> setQuietModeEnabled(userId, enableQuietMode, target,
+ callingPackage)
+ );
+ }
+ }
+
/**
* Cache the owner name string, since it could be read repeatedly on a critical code path
* but hit by slow IO. This could be eliminated once we have the cached UserInfo in place.
@@ -766,6 +969,8 @@
mPackagesLock = packagesLock;
mUsers = users != null ? users : new SparseArray<>();
mHandler = new MainHandler();
+ mInternalExecutor = new ThreadPoolExecutor(/* corePoolSize */ 0, /* maximumPoolSize */ 1,
+ /* keepAliveTime */ 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
mUserVisibilityMediator = new UserVisibilityMediator(mHandler);
mUserDataPreparer = userDataPreparer;
mUserTypes = UserTypeFactory.getUserTypes();
@@ -790,9 +995,15 @@
mLockPatternUtils = new LockPatternUtils(mContext);
mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING);
mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null;
+ mPrivateSpaceAutoLockSettingsObserver = new SettingsObserver(mHandler);
emulateSystemUserModeIfNeeded();
}
+ private static boolean isAutoLockForPrivateSpaceEnabled() {
+ return android.os.Flags.allowPrivateProfile()
+ && Flags.supportAutolockForPrivateSpace();
+ }
+
void systemReady() {
mAppOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
@@ -809,6 +1020,20 @@
new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED),
null, mHandler);
+ if (isAutoLockForPrivateSpaceEnabled()) {
+
+ int mainUserId = getMainUserIdUnchecked();
+
+ mContext.getContentResolver().registerContentObserverAsUser(Settings.Secure.getUriFor(
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK), false,
+ mPrivateSpaceAutoLockSettingsObserver, UserHandle.of(mainUserId));
+
+ setOrUpdateAutoLockPreferenceForPrivateProfile(
+ Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK,
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_NEVER, mainUserId));
+ }
+
markEphemeralUsersForRemoval();
}
@@ -973,6 +1198,18 @@
return UserHandle.USER_NULL;
}
+ private @UserIdInt int getPrivateProfileUserId() {
+ synchronized (mUsersLock) {
+ for (int userId : getUserIds()) {
+ UserInfo userInfo = getUserInfoLU(userId);
+ if (userInfo != null && userInfo.isPrivateProfile()) {
+ return userInfo.id;
+ }
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
@Override
public void setBootUser(@UserIdInt int userId) {
checkCreateUsersPermission("Set boot user");
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index 375ef61..a4dbce6 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -65,6 +65,7 @@
import com.android.internal.widget.RebootEscrowListener;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.Watchdog;
import com.android.server.pm.ApexManager;
import com.android.server.recoverysystem.hal.BootControlHIDL;
@@ -112,6 +113,9 @@
private static final int SOCKET_CONNECTION_MAX_RETRY = 30;
+ /** How long to pause the watchdog for when rebooting the device. */
+ private static final int REBOOT_WATCHDOG_PAUSE_DURATION_MS = 20_000;
+
static final String REQUEST_LSKF_TIMESTAMP_PREF_SUFFIX = "_request_lskf_timestamp";
static final String REQUEST_LSKF_COUNT_PREF_SUFFIX = "_request_lskf_count";
@@ -899,7 +903,8 @@
// Clear the metrics prefs after a successful RoR reboot.
mInjector.getMetricsPrefs().deletePrefsFile();
-
+ Watchdog.getInstance().pauseWatchingCurrentThreadFor(
+ REBOOT_WATCHDOG_PAUSE_DURATION_MS, "reboot can be slow");
PowerManager pm = mInjector.getPowerManager();
pm.reboot(reason);
return RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index d903ad4..73fc8e9 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2280,6 +2280,26 @@
}
@Override
+ public void setVideoFrozen(IBinder sessionToken, boolean isFrozen, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "setVideoFrozen");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .setVideoFrozen(isFrozen);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in setVideoFrozen", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void setTvMessageEnabled(IBinder sessionToken, int type, boolean enabled,
int userId) {
final int callingUid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 13f7152..f6d77ea 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -60,6 +60,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.server.pm.PackageArchiver.isArchivingEnabled;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -104,7 +105,6 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
-import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
@@ -1034,7 +1034,7 @@
}
if (err == ActivityManager.START_SUCCESS && aInfo == null) {
- if (Flags.archiving()) {
+ if (isArchivingEnabled()) {
PackageArchiver packageArchiver = mService
.getPackageManagerInternalLocked()
.getPackageArchiver();
diff --git a/services/core/java/com/android/server/wm/SensitiveContentPackages.java b/services/core/java/com/android/server/wm/SensitiveContentPackages.java
index 3862b82..a7d6903b 100644
--- a/services/core/java/com/android/server/wm/SensitiveContentPackages.java
+++ b/services/core/java/com/android/server/wm/SensitiveContentPackages.java
@@ -21,7 +21,6 @@
import java.io.PrintWriter;
import java.util.Objects;
-import java.util.Set;
/**
* Cache of distinct package/uid pairs that require being blocked from screen capture. This class is
@@ -41,10 +40,33 @@
return false;
}
- /** Replaces the set of package/uid pairs to set that should be blocked from screen capture */
- public void setShouldBlockScreenCaptureForApp(@NonNull Set<PackageInfo> packageInfos) {
- mProtectedPackages.clear();
+ /**
+ * Adds the set of package/uid pairs to set that should be blocked from screen capture
+ *
+ * @param packageInfos packages to be blocked
+ * @return {@code true} if packages set is modified, {@code false} otherwise.
+ */
+ public boolean addBlockScreenCaptureForApps(@NonNull ArraySet<PackageInfo> packageInfos) {
+ if (mProtectedPackages.equals(packageInfos)) {
+ // new set is equal to current set of packages, no need to update
+ return false;
+ }
mProtectedPackages.addAll(packageInfos);
+ return true;
+ }
+
+ /**
+ * Clears the set of package/uid pairs that should be blocked from screen capture
+ *
+ * @return {@code true} if packages set is modified, {@code false} otherwise.
+ */
+ public boolean clearBlockedApps() {
+ if (mProtectedPackages.isEmpty()) {
+ // set was already empty
+ return false;
+ }
+ mProtectedPackages.clear();
+ return true;
}
void dump(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 314d720..8f9ed83 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3558,9 +3558,11 @@
&& top.mLetterboxUiController.isSystemOverrideToFullscreenEnabled();
appCompatTaskInfo.isFromLetterboxDoubleTap = top != null
&& top.mLetterboxUiController.isFromDoubleTap();
- if (appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
+ if (top != null) {
appCompatTaskInfo.topActivityLetterboxWidth = top.getBounds().width();
appCompatTaskInfo.topActivityLetterboxHeight = top.getBounds().height();
+ }
+ if (appCompatTaskInfo.isLetterboxDoubleTapEnabled) {
if (appCompatTaskInfo.isTopActivityPillarboxed()) {
// Pillarboxed
appCompatTaskInfo.topActivityLetterboxHorizontalPosition =
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 22b690e..f2a58e5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -32,6 +32,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
+import android.util.ArraySet;
import android.util.Pair;
import android.view.ContentRecordingSession;
import android.view.Display;
@@ -1020,5 +1021,13 @@
*
* @param packageInfos set of {@link PackageInfo} whose windows should be blocked from capture
*/
- public abstract void setShouldBlockScreenCaptureForApp(@NonNull Set<PackageInfo> packageInfos);
+ public abstract void addBlockScreenCaptureForApps(@NonNull ArraySet<PackageInfo> packageInfos);
+
+ /**
+ * Clears apps added to collection of apps in which screen capture should be disabled.
+ *
+ * <p> This clears and resets any existing set or added applications from
+ * * {@link #addBlockScreenCaptureForApps(ArraySet)}
+ */
+ public abstract void clearBlockedApps();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9179acf..6c83356 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -370,7 +370,6 @@
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@@ -8576,10 +8575,23 @@
}
@Override
- public void setShouldBlockScreenCaptureForApp(Set<PackageInfo> packageInfos) {
+ public void addBlockScreenCaptureForApps(ArraySet<PackageInfo> packageInfos) {
synchronized (mGlobalLock) {
- mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(packageInfos);
- WindowManagerService.this.refreshScreenCaptureDisabled();
+ boolean modified =
+ mSensitiveContentPackages.addBlockScreenCaptureForApps(packageInfos);
+ if (modified) {
+ WindowManagerService.this.refreshScreenCaptureDisabled();
+ }
+ }
+ }
+
+ @Override
+ public void clearBlockedApps() {
+ synchronized (mGlobalLock) {
+ boolean modified = mSensitiveContentPackages.clearBlockedApps();
+ if (modified) {
+ WindowManagerService.this.refreshScreenCaptureDisabled();
+ }
}
}
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 775570c..dfa9dce 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -37,7 +37,6 @@
"com_android_server_adb_AdbDebuggingManager.cpp",
"com_android_server_am_BatteryStatsService.cpp",
"com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp",
- "com_android_server_BootReceiver.cpp",
"com_android_server_ConsumerIrService.cpp",
"com_android_server_companion_virtual_InputController.cpp",
"com_android_server_devicepolicy_CryptoTestHelper.cpp",
@@ -95,16 +94,6 @@
header_libs: [
"bionic_libc_platform_headers",
],
-
- static_libs: [
- "libunwindstack",
- ],
-
- whole_static_libs: [
- "libdebuggerd_tombstone_proto_to_text",
- ],
-
- runtime_libs: ["libdexfile"],
}
cc_defaults {
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 15eb7c6..cc08488 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -33,7 +33,3 @@
# Bug component : 158088 = per-file *AnrTimer*
per-file *AnrTimer* = file:/PERFORMANCE_OWNERS
-
-# Bug component : 158088 = per-file com_android_server_utils_AnrTimer*.java
-per-file com_android_server_utils_AnrTimer*.java = file:/PERFORMANCE_OWNERS
-per-file com_android_server_BootReceiver.cpp = file:/STABILITY_OWNERS
diff --git a/services/core/jni/com_android_server_BootReceiver.cpp b/services/core/jni/com_android_server_BootReceiver.cpp
deleted file mode 100644
index 3892d28..0000000
--- a/services/core/jni/com_android_server_BootReceiver.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <libdebuggerd/tombstone.h>
-#include <nativehelper/JNIHelp.h>
-
-#include <sstream>
-
-#include "jni.h"
-#include "tombstone.pb.h"
-
-namespace android {
-
-static void writeToString(std::stringstream& ss, const std::string& line, bool should_log) {
- ss << line << std::endl;
-}
-
-static jstring com_android_server_BootReceiver_getTombstoneText(JNIEnv* env, jobject,
- jbyteArray tombstoneBytes) {
- Tombstone tombstone;
- tombstone.ParseFromArray(env->GetByteArrayElements(tombstoneBytes, 0),
- env->GetArrayLength(tombstoneBytes));
-
- std::stringstream tombstoneString;
-
- tombstone_proto_to_text(tombstone,
- std::bind(&writeToString, std::ref(tombstoneString),
- std::placeholders::_1, std::placeholders::_2));
-
- return env->NewStringUTF(tombstoneString.str().c_str());
-}
-
-static const JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- {"getTombstoneText", "([B)Ljava/lang/String;",
- (jstring*)com_android_server_BootReceiver_getTombstoneText},
-};
-
-int register_com_android_server_BootReceiver(JNIEnv* env) {
- return jniRegisterNativeMethods(env, "com/android/server/BootReceiver", sMethods,
- NELEM(sMethods));
-}
-
-} // namespace android
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 414339d..2b9bb7a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -685,6 +685,8 @@
{ // acquire lock
std::scoped_lock _l(mLock);
+ outConfig->mousePointerSpeed = mLocked.pointerSpeed;
+ outConfig->mousePointerAccelerationEnabled = mLocked.mousePointerAccelerationEnabled;
outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed
* POINTER_SPEED_EXPONENT);
outConfig->pointerVelocityControlParameters.acceleration =
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index a4b1f84..5d1eb49 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -66,7 +66,6 @@
int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env);
int register_android_server_companion_virtual_InputController(JNIEnv* env);
int register_android_server_app_GameManagerService(JNIEnv* env);
-int register_com_android_server_BootReceiver(JNIEnv* env);
int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env);
int register_com_android_server_display_DisplayControl(JNIEnv* env);
int register_com_android_server_SystemClockTime(JNIEnv* env);
@@ -129,7 +128,6 @@
register_android_server_sensor_SensorService(vm, env);
register_android_server_companion_virtual_InputController(env);
register_android_server_app_GameManagerService(env);
- register_com_android_server_BootReceiver(env);
register_com_android_server_wm_TaskFpsCallbackController(env);
register_com_android_server_display_DisplayControl(env);
register_com_android_server_SystemClockTime(env);
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java
index b363fd4..d781433 100644
--- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java
@@ -20,11 +20,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.media.projection.MediaProjectionInfo;
@@ -35,6 +37,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -52,7 +55,6 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.Collections;
import java.util.Set;
@SmallTest
@@ -68,6 +70,8 @@
private static final int NOTIFICATION_UID_1 = 5;
private static final int NOTIFICATION_UID_2 = 6;
+ private static final ArraySet<PackageInfo> EMPTY_SET = new ArraySet<>();
+
@Rule
public final TestableContext mContext =
new TestableContext(getInstrumentation().getTargetContext(), null);
@@ -107,6 +111,9 @@
mSensitiveContentProtectionManagerService.mNotificationListener =
spy(mSensitiveContentProtectionManagerService.mNotificationListener);
+ doCallRealMethod()
+ .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+ .onListenerConnected();
// Setup RankingMap and two possilbe rankings
when(mSensitiveRanking.hasSensitiveContent()).thenReturn(true);
@@ -128,7 +135,7 @@
mSensitiveContentProtectionManagerService.onDestroy();
}
- private Set<PackageInfo> setupSensitiveNotification() {
+ private ArraySet<PackageInfo> setupSensitiveNotification() {
// Setup Notification Values
when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
@@ -149,10 +156,11 @@
when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
.thenReturn(mNonSensitiveRanking);
- return Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1));
+ return new ArraySet<>(
+ Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1)));
}
- private Set<PackageInfo> setupMultipleSensitiveNotificationsFromSamePackageAndUid() {
+ private ArraySet<PackageInfo> setupMultipleSensitiveNotificationsFromSamePackageAndUid() {
// Setup Notification Values
when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
@@ -173,10 +181,11 @@
when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
.thenReturn(mSensitiveRanking);
- return Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1));
+ return new ArraySet<>(
+ Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1)));
}
- private Set<PackageInfo> setupMultipleSensitiveNotificationsFromDifferentPackage() {
+ private ArraySet<PackageInfo> setupMultipleSensitiveNotificationsFromDifferentPackage() {
// Setup Notification Values
when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
@@ -197,11 +206,12 @@
when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
.thenReturn(mSensitiveRanking);
- return Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1),
- new PackageInfo(NOTIFICATION_PKG_2, NOTIFICATION_UID_1));
+ return new ArraySet<>(
+ Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1),
+ new PackageInfo(NOTIFICATION_PKG_2, NOTIFICATION_UID_1)));
}
- private Set<PackageInfo> setupMultipleSensitiveNotificationsFromDifferentUid() {
+ private ArraySet<PackageInfo> setupMultipleSensitiveNotificationsFromDifferentUid() {
// Setup Notification Values
when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
@@ -222,8 +232,9 @@
when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
.thenReturn(mSensitiveRanking);
- return Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1),
- new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_2));
+ return new ArraySet<>(
+ Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1),
+ new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_2)));
}
private void setupNoSensitiveNotifications() {
@@ -251,11 +262,11 @@
@Test
public void mediaProjectionOnStart_onProjectionStart_setWmBlockedPackages() {
- Set<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
+ ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(expectedBlockedPackages);
+ verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
}
@Test
@@ -264,7 +275,7 @@
mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
}
@Test
@@ -273,37 +284,37 @@
mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
}
@Test
public void mediaProjectionOnStart_multipleNotifications_setWmBlockedPackages() {
- Set<PackageInfo> expectedBlockedPackages =
+ ArraySet<PackageInfo> expectedBlockedPackages =
setupMultipleSensitiveNotificationsFromSamePackageAndUid();
mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(expectedBlockedPackages);
+ verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
}
@Test
public void mediaProjectionOnStart_multiplePackages_setWmBlockedPackages() {
- Set<PackageInfo> expectedBlockedPackages =
+ ArraySet<PackageInfo> expectedBlockedPackages =
setupMultipleSensitiveNotificationsFromDifferentPackage();
mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(expectedBlockedPackages);
+ verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
}
@Test
public void mediaProjectionOnStart_multipleUid_setWmBlockedPackages() {
- Set<PackageInfo> expectedBlockedPackages =
+ ArraySet<PackageInfo> expectedBlockedPackages =
setupMultipleSensitiveNotificationsFromDifferentUid();
mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(expectedBlockedPackages);
+ verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
}
@Test
@@ -316,12 +327,12 @@
mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ verify(mWindowManager).clearBlockedApps();
}
@Test
public void mediaProjectionOnStart_afterOnStop_onProjectionStart_setWmBlockedPackages() {
- Set<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
+ ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
MediaProjectionInfo mediaProjectionInfo = mock(MediaProjectionInfo.class);
mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
@@ -330,7 +341,7 @@
mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(expectedBlockedPackages);
+ verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
}
@Test
@@ -341,7 +352,7 @@
mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
}
@Test
@@ -352,7 +363,7 @@
mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
}
@Test
@@ -363,7 +374,7 @@
mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
}
@Test
@@ -376,6 +387,314 @@
mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
- verify(mWindowManager).setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ }
+
+ @Test
+ public void nlsOnListenerConnected_projectionNotStarted_noop() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+
+ mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
+
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void nlsOnListenerConnected_projectionStopped_noop() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStop(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
+
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void nlsOnListenerConnected_projectionStarted_setWmBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
+
+ verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
+ }
+
+ @Test
+ public void nlsOnListenerConnected_noSensitiveNotifications_noBlockedPackages() {
+ setupNoSensitiveNotifications();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
+
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ }
+
+ @Test
+ public void nlsOnListenerConnected_noNotifications_noBlockedPackages() {
+ setupNoNotifications();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
+
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ }
+
+ @Test
+ public void nlsOnListenerConnected_nullRankingMap_noBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+ doReturn(null)
+ .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+ .getCurrentRanking();
+
+ mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
+
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ }
+
+ @Test
+ public void nlsOnListenerConnected_missingRanking_noBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+ when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1))).thenReturn(null);
+ doReturn(mRankingMap)
+ .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+ .getCurrentRanking();
+
+ mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
+
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ }
+
+ @Test
+ public void nlsOnNotificationRankingUpdate_projectionNotStarted_noop() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationRankingUpdate(mRankingMap);
+
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void nlsOnNotificationRankingUpdate_projectionStopped_noop() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStop(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationRankingUpdate(mRankingMap);
+
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void nlsOnNotificationRankingUpdate_projectionStarted_setWmBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationRankingUpdate(mRankingMap);
+
+ verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
+ }
+
+ @Test
+ public void nlsOnNotificationRankingUpdate_noSensitiveNotifications_noBlockedPackages() {
+ setupNoSensitiveNotifications();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationRankingUpdate(mRankingMap);
+
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ }
+
+ @Test
+ public void nlsOnNotificationRankingUpdate_noNotifications_noBlockedPackages() {
+ setupNoNotifications();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationRankingUpdate(mRankingMap);
+
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ }
+
+ @Test
+ public void nlsOnNotificationRankingUpdate_nullRankingMap_noBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationRankingUpdate(null);
+
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ }
+
+ @Test
+ public void nlsOnNotificationRankingUpdate_missingRanking_noBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+ when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1))).thenReturn(null);
+ doReturn(mRankingMap)
+ .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+ .getCurrentRanking();
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationRankingUpdate(mRankingMap);
+
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ }
+
+ @Test
+ public void nlsOnNotificationRankingUpdate_getActiveNotificationsThrows_noBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ doThrow(SecurityException.class)
+ .when(mSensitiveContentProtectionManagerService.mNotificationListener)
+ .getActiveNotifications();
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationRankingUpdate(mRankingMap);
+
+ verify(mWindowManager).addBlockScreenCaptureForApps(EMPTY_SET);
+ }
+
+ @Test
+ public void nlsOnNotificationPosted_projectionNotStarted_noop() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationPosted(mNotification1, mRankingMap);
+
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void nlsOnNotificationPosted_projectionStopped_noop() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ mMediaProjectionCallbackCaptor.getValue().onStop(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationPosted(mNotification1, mRankingMap);
+
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void nlsOnNotificationPosted_projectionStarted_setWmBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationPosted(mNotification1, mRankingMap);
+
+ ArraySet<PackageInfo> expectedBlockedPackages = new ArraySet<>(
+ Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1)));
+ verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
+ }
+
+ @Test
+ public void nlsOnNotificationPosted_noSensitiveNotifications_noBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationPosted(mNotification2, mRankingMap);
+
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void nlsOnNotificationPosted_noNotifications_noBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationPosted(null, mRankingMap);
+
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void nlsOnNotificationPosted_nullRankingMap_noBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationPosted(mNotification1, null);
+
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void nlsOnNotificationPosted_missingRanking_noBlockedPackages() {
+ // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
+ // as non-sensitive
+ setupSensitiveNotification();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mock(MediaProjectionInfo.class));
+ Mockito.reset(mWindowManager);
+ when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1))).thenReturn(null);
+
+ mSensitiveContentProtectionManagerService.mNotificationListener
+ .onNotificationPosted(mNotification1, mRankingMap);
+
+ verifyZeroInteractions(mWindowManager);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
new file mode 100644
index 0000000..7d3a110
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.am.ActivityManagerService.Injector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.app.ApplicationStartInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.platform.test.annotations.Presubmit;
+import android.text.TextUtils;
+
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.appop.AppOpsService;
+import com.android.server.wm.ActivityTaskManagerService;
+
+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.Description;
+import org.junit.runners.model.Statement;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+
+/**
+ * Test class for {@link android.app.ApplicationStartInfo}.
+ *
+ * Build/Install/Run:
+ * atest ApplicationStartInfoTest
+ */
+@Presubmit
+public class ApplicationStartInfoTest {
+
+ private static final String TAG = ApplicationStartInfoTest.class.getSimpleName();
+ private static final ComponentName COMPONENT = new ComponentName("com.android.test", ".Foo");
+
+ @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
+ @Mock private AppOpsService mAppOpsService;
+ @Mock private PackageManagerInternal mPackageManagerInt;
+
+ private Context mContext = getInstrumentation().getTargetContext();
+ private TestInjector mInjector;
+ private ActivityManagerService mAms;
+ private ProcessList mProcessList;
+ private AppStartInfoTracker mAppStartInfoTracker;
+ private Handler mHandler;
+ private HandlerThread mHandlerThread;
+
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mProcessList = spy(new ProcessList());
+ mAppStartInfoTracker = spy(new AppStartInfoTracker());
+ mAppStartInfoTracker.mEnabled = true;
+ setFieldValue(ProcessList.class, mProcessList, "mAppStartInfoTracker",
+ mAppStartInfoTracker);
+ mInjector = new TestInjector(mContext);
+ mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread());
+ mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+ mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
+ mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal());
+ mAms.mPackageManagerInt = mPackageManagerInt;
+ mAppStartInfoTracker.mService = mAms;
+ doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+ doReturn("com.android.test").when(mPackageManagerInt).getNameForUid(anyInt());
+ // Remove stale instance of PackageManagerInternal if there is any
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+ }
+
+ @After
+ public void tearDown() {
+ mHandlerThread.quit();
+ }
+
+ @Test
+ public void testApplicationStartInfo() throws Exception {
+ mAppStartInfoTracker.clearProcessStartInfo(true);
+ mAppStartInfoTracker.mAppStartInfoLoaded.set(true);
+ mAppStartInfoTracker.mAppStartInfoHistoryListSize =
+ mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE;
+ mAppStartInfoTracker.mProcStartStoreDir = new File(mContext.getFilesDir(),
+ AppStartInfoTracker.APP_START_STORE_DIR);
+ assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir));
+ mAppStartInfoTracker.mProcStartInfoFile = new File(mAppStartInfoTracker.mProcStartStoreDir,
+ AppStartInfoTracker.APP_START_INFO_FILE);
+
+ doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean());
+
+ final int app1Uid = 10123;
+ final int app1Pid1 = 12345;
+ final int app1Pid2 = 12346;
+ final int app1DefiningUid = 23456;
+ final int app1UidUser2 = 1010123;
+ final int app1PidUser2 = 12347;
+ final String app1ProcessName = "com.android.test.stub1:process";
+ final String app1PackageName = "com.android.test.stub1";
+ final long appStartTimestampIntentStarted = 1000000;
+ final long appStartTimestampActivityLaunchFinished = 2000000;
+ final long appStartTimestampReportFullyDrawn = 3000000;
+ final long appStartTimestampService = 4000000;
+ final long appStartTimestampBroadcast = 5000000;
+ final long appStartTimestampRContentProvider = 6000000;
+
+ ProcessRecord app = makeProcessRecord(
+ app1Pid1, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ null, // definingUid
+ app1ProcessName, // processName
+ app1PackageName); // packageName
+
+ ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>();
+
+ // Case 1: Activity start intent failed
+ mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
+ appStartTimestampIntentStarted);
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ verifyInProgressRecordsSize(1);
+ assertEquals(list.size(), 0);
+
+ verifyInProgApplicationStartInfo(
+ 0, // index
+ 0, // pid
+ 0, // uid
+ 0, // packageUid
+ null, // definingUid
+ null, // processName
+ ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
+ ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
+ ApplicationStartInfo.START_TYPE_UNSET, // state type
+ ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode
+
+ mAppStartInfoTracker.onIntentFailed(appStartTimestampIntentStarted);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ verifyInProgressRecordsSize(0);
+ assertEquals(list.size(), 0);
+
+ mAppStartInfoTracker.clearProcessStartInfo(true);
+
+ // Case 2: Activity start launch cancelled
+ mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
+ appStartTimestampIntentStarted);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ verifyInProgressRecordsSize(1);
+ assertEquals(list.size(), 0);
+
+ mAppStartInfoTracker.onActivityLaunched(appStartTimestampIntentStarted, COMPONENT,
+ ApplicationStartInfo.START_TYPE_COLD, app);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ verifyInProgressRecordsSize(1);
+ assertEquals(list.size(), 1);
+
+ verifyInProgApplicationStartInfo(
+ 0, // index
+ app1Pid1, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ null, // definingUid
+ app1ProcessName, // processName
+ ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
+ ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
+ ApplicationStartInfo.START_TYPE_COLD, // state type
+ ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode
+
+ mAppStartInfoTracker.onActivityLaunchCancelled(appStartTimestampIntentStarted);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ verifyInProgressRecordsSize(0);
+ assertEquals(list.size(), 1);
+
+ verifyApplicationStartInfo(
+ list.get(0), // info
+ app1Pid1, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ null, // definingUid
+ app1ProcessName, // processName
+ ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
+ ApplicationStartInfo.STARTUP_STATE_ERROR, // startup state
+ ApplicationStartInfo.START_TYPE_COLD, // state type
+ ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode
+
+ mAppStartInfoTracker.clearProcessStartInfo(true);
+
+ // Case 3: Activity start success
+ mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
+ appStartTimestampIntentStarted);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ verifyInProgressRecordsSize(1);
+ assertEquals(list.size(), 0);
+
+ mAppStartInfoTracker.onActivityLaunched(appStartTimestampIntentStarted, COMPONENT,
+ ApplicationStartInfo.START_TYPE_COLD, app);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ verifyInProgressRecordsSize(1);
+ assertEquals(list.size(), 1);
+
+ verifyInProgApplicationStartInfo(
+ 0, // index
+ app1Pid1, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ null, // definingUid
+ app1ProcessName, // processName
+ ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
+ ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
+ ApplicationStartInfo.START_TYPE_COLD, // state type
+ ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode
+
+ verifyApplicationStartInfo(
+ list.get(0), // info
+ app1Pid1, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ null, // definingUid
+ app1ProcessName, // processName
+ ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
+ ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
+ ApplicationStartInfo.START_TYPE_COLD, // state type
+ ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode
+
+ mAppStartInfoTracker.onActivityLaunchFinished(appStartTimestampIntentStarted, COMPONENT,
+ appStartTimestampActivityLaunchFinished, ApplicationStartInfo.LAUNCH_MODE_STANDARD);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ verifyInProgressRecordsSize(1);
+ assertEquals(list.size(), 1);
+
+ verifyInProgApplicationStartInfo(
+ 0, // index
+ app1Pid1, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ null, // definingUid
+ app1ProcessName, // processName
+ ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
+ ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN, // startup state
+ ApplicationStartInfo.START_TYPE_COLD, // state type
+ ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode
+
+ mAppStartInfoTracker.onReportFullyDrawn(appStartTimestampIntentStarted,
+ appStartTimestampReportFullyDrawn);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ verifyInProgressRecordsSize(0);
+ assertEquals(list.size(), 1);
+
+ verifyApplicationStartInfo(
+ list.get(0), // info
+ app1Pid1, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ null, // definingUid
+ app1ProcessName, // processName
+ ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
+ ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN, // startup state
+ ApplicationStartInfo.START_TYPE_COLD, // state type
+ ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode
+
+ // Don't clear records for use in subsequent cases.
+
+ // Case 4: Create an other app1 record with different pid started for a service
+ sleep(1);
+ app = makeProcessRecord(
+ app1Pid2, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ app1DefiningUid, // definingUid
+ app1ProcessName, // processName
+ app1PackageName); // packageName
+ ServiceRecord service = ServiceRecord.newEmptyInstanceForTest(mAms);
+
+ mAppStartInfoTracker.handleProcessServiceStart(appStartTimestampService, app, service,
+ false);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, 0, 0, list);
+ assertEquals(list.size(), 2);
+
+ verifyApplicationStartInfo(
+ list.get(0), // info
+ app1Pid2, // pid
+ app1Uid, // uid
+ app1Uid, // packageUid
+ app1DefiningUid, // definingUid
+ app1ProcessName, // processName
+ ApplicationStartInfo.START_REASON_SERVICE, // reason
+ ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
+ ApplicationStartInfo.START_TYPE_WARM, // state type
+ ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode
+
+ // Case 5: Create an instance of app1 with a different user started for a broadcast
+ sleep(1);
+ app = makeProcessRecord(
+ app1PidUser2, // pid
+ app1UidUser2, // uid
+ app1UidUser2, // packageUid
+ null, // definingUid
+ app1ProcessName, // processName
+ app1PackageName); // packageName
+
+ mAppStartInfoTracker.handleProcessBroadcastStart(appStartTimestampBroadcast, app,
+ null, true /* isColdStart */);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list);
+ assertEquals(list.size(), 1);
+
+ verifyApplicationStartInfo(
+ list.get(0), // info
+ app1PidUser2, // pid
+ app1UidUser2, // uid
+ app1UidUser2, // packageUid
+ null, // definingUid
+ app1ProcessName, // processName
+ ApplicationStartInfo.START_REASON_BROADCAST, // reason
+ ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
+ ApplicationStartInfo.START_TYPE_COLD, // state type
+ ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode
+
+ // Case 6: User 2 gets removed
+ mAppStartInfoTracker.onPackageRemoved(app1PackageName, app1UidUser2, false);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list);
+ assertEquals(list.size(), 0);
+
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1PidUser2, 0, list);
+ assertEquals(list.size(), 2);
+
+
+ // Case 7: Create a process from another package started for a content provider
+ final int app2UidUser2 = 1010234;
+ final int app2PidUser2 = 12348;
+ final String app2ProcessName = "com.android.test.stub2:process";
+ final String app2PackageName = "com.android.test.stub2";
+
+ sleep(1);
+
+ app = makeProcessRecord(
+ app2PidUser2, // pid
+ app2UidUser2, // uid
+ app2UidUser2, // packageUid
+ null, // definingUid
+ app2ProcessName, // processName
+ app2PackageName); // packageName
+
+ mAppStartInfoTracker.handleProcessContentProviderStart(appStartTimestampRContentProvider,
+ app, false);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(app2PackageName, app2UidUser2, app2PidUser2, 0, list);
+ assertEquals(list.size(), 1);
+
+ verifyApplicationStartInfo(
+ list.get(0), // info
+ app2PidUser2, // pid
+ app2UidUser2, // uid
+ app2UidUser2, // packageUid
+ null, // definingUid
+ app2ProcessName, // processName
+ ApplicationStartInfo.START_REASON_CONTENT_PROVIDER, // reason
+ ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
+ ApplicationStartInfo.START_TYPE_WARM, // state type
+ ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode
+
+ // Case 8: Save and load again
+ ArrayList<ApplicationStartInfo> original = new ArrayList<ApplicationStartInfo>();
+ mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, original);
+ assertTrue(original.size() > 0);
+
+ mAppStartInfoTracker.persistProcessStartInfo();
+ assertTrue(mAppStartInfoTracker.mProcStartInfoFile.exists());
+
+ mAppStartInfoTracker.clearProcessStartInfo(false);
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, list);
+ assertEquals(0, list.size());
+
+ mAppStartInfoTracker.loadExistingProcessStartInfo();
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, list);
+ assertEquals(original.size(), list.size());
+
+ for (int i = list.size() - 1; i >= 0; i--) {
+ assertTrue(list.get(i).equals(original.get(i)));
+ }
+ }
+
+ private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
+ try {
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ Field mfield = Field.class.getDeclaredField("accessFlags");
+ mfield.setAccessible(true);
+ mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
+ field.set(obj, val);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ }
+ }
+
+ private void sleep(long ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid,
+ String processName, String packageName) {
+ return makeProcessRecord(pid, uid, packageUid, definingUid, processName, packageName, mAms);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ static ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid,
+ String processName, String packageName, ActivityManagerService ams) {
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = packageName;
+ ProcessRecord app = new ProcessRecord(ams, ai, processName, uid);
+ app.setPid(pid);
+ app.info.uid = packageUid;
+ if (definingUid != null) {
+ app.setHostingRecord(HostingRecord.byAppZygote(COMPONENT, "", definingUid, ""));
+ }
+ return app;
+ }
+
+ private static Intent buildIntent(ComponentName componentName) throws Exception {
+ Intent intent = new Intent();
+ intent.setComponent(componentName);
+ intent.setPackage(componentName.getPackageName());
+ return intent;
+ }
+
+ private void verifyInProgressRecordsSize(int expectedSize) {
+ synchronized (mAppStartInfoTracker.mLock) {
+ assertEquals(mAppStartInfoTracker.mInProgRecords.size(), expectedSize);
+ }
+ }
+
+ private void verifyInProgApplicationStartInfo(int index,
+ Integer pid, Integer uid, Integer packageUid,
+ Integer definingUid, String processName,
+ Integer reason, Integer startupState, Integer startType, Integer launchMode) {
+ synchronized (mAppStartInfoTracker.mLock) {
+ verifyApplicationStartInfo(mAppStartInfoTracker.mInProgRecords.valueAt(index),
+ pid, uid, packageUid, definingUid, processName, reason, startupState,
+ startType, launchMode);
+ }
+ }
+
+ private void verifyApplicationStartInfo(ApplicationStartInfo info,
+ Integer pid, Integer uid, Integer packageUid,
+ Integer definingUid, String processName,
+ Integer reason, Integer startupState, Integer startType, Integer launchMode) {
+ assertNotNull(info);
+
+ if (pid != null) {
+ assertEquals(pid.intValue(), info.getPid());
+ }
+ if (uid != null) {
+ assertEquals(uid.intValue(), info.getRealUid());
+ }
+ if (packageUid != null) {
+ assertEquals(packageUid.intValue(), info.getPackageUid());
+ }
+ if (definingUid != null) {
+ assertEquals(definingUid.intValue(), info.getDefiningUid());
+ }
+ if (processName != null) {
+ assertTrue(TextUtils.equals(processName, info.getProcessName()));
+ }
+ if (reason != null) {
+ assertEquals(reason.intValue(), info.getReason());
+ }
+ if (startupState != null) {
+ assertEquals(startupState.intValue(), info.getStartupState());
+ }
+ if (startType != null) {
+ assertEquals(startType.intValue(), info.getStartType());
+ }
+ if (launchMode != null) {
+ assertEquals(launchMode.intValue(), info.getLaunchMode());
+ }
+ }
+
+ private class TestInjector extends Injector {
+ TestInjector(Context context) {
+ super(context);
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
+ Handler handler) {
+ return mAppOpsService;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandler;
+ }
+
+ @Override
+ public ProcessList getProcessList(ActivityManagerService service) {
+ return mProcessList;
+ }
+ }
+
+ static class ServiceThreadRule implements TestRule {
+
+ private ServiceThread mThread;
+
+ ServiceThread getThread() {
+ return mThread;
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ mThread = new ServiceThread("TestServiceThread",
+ Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
+ mThread.start();
+ try {
+ base.evaluate();
+ } finally {
+ mThread.getThreadHandler().runWithScissors(mThread::quit, 0 /* timeout */);
+ }
+ }
+ };
+ }
+ }
+
+ // TODO: [b/302724778] Remove manual JNI load
+ static {
+ System.loadLibrary("mockingservicestestjni");
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index 0403c64..ec7e359 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -233,7 +233,7 @@
Exception e = assertThrows(
SecurityException.class,
() -> mArchiveManager.requestArchive(PACKAGE, "different", mIntentSender,
- UserHandle.CURRENT, 0));
+ UserHandle.CURRENT));
assertThat(e).hasMessageThat().isEqualTo(
String.format(
"The UID %s of callerPackageName set by the caller doesn't match the "
@@ -250,7 +250,7 @@
Exception e = assertThrows(
ParcelableException.class,
() -> mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
- UserHandle.CURRENT, 0));
+ UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
String.format("Package %s not found.", PACKAGE));
@@ -260,8 +260,7 @@
public void archiveApp_packageNotInstalledForUser() throws IntentSender.SendIntentException {
mPackageSetting.modifyUserState(UserHandle.CURRENT.getIdentifier()).setInstalled(false);
- mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT,
- 0);
+ mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
rule.mocks().getHandler().flush();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -291,7 +290,7 @@
Exception e = assertThrows(
ParcelableException.class,
() -> mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
- UserHandle.CURRENT, 0));
+ UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo("No installer found");
}
@@ -305,7 +304,7 @@
Exception e = assertThrows(
ParcelableException.class,
() -> mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
- UserHandle.CURRENT, 0));
+ UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
"Installer does not support unarchival");
@@ -319,7 +318,7 @@
Exception e = assertThrows(
ParcelableException.class,
() -> mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
- UserHandle.CURRENT, 0));
+ UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
TextUtils.formatSimple("The app %s does not have a main activity.", PACKAGE));
@@ -331,8 +330,7 @@
doThrow(e).when(mArchiveManager).storeIcon(eq(PACKAGE),
any(LauncherActivityInfo.class), eq(mUserId), anyInt(), anyInt());
- mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT,
- 0);
+ mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
rule.mocks().getHandler().flush();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -355,7 +353,7 @@
Exception e = assertThrows(
ParcelableException.class,
() -> mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
- UserHandle.CURRENT, 0));
+ UserHandle.CURRENT));
assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
assertThat(e.getCause()).hasMessageThat().isEqualTo(
TextUtils.formatSimple("The app %s is opted out of archiving.", PACKAGE));
@@ -363,8 +361,7 @@
@Test
public void archiveApp_withNoAdditionalFlags_success() {
- mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT,
- 0);
+ mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
rule.mocks().getHandler().flush();
verify(mInstallerService).uninstall(
@@ -386,14 +383,13 @@
@Test
public void archiveApp_withAdditionalFlags_success() {
- mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT,
- PackageManager.DELETE_SHOW_DIALOG);
+ mArchiveManager.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
rule.mocks().getHandler().flush();
verify(mInstallerService).uninstall(
eq(new VersionedPackage(PACKAGE, PackageManager.VERSION_CODE_HIGHEST)),
eq(CALLER_PACKAGE),
- eq(DELETE_ARCHIVE | DELETE_KEEP_DATA | PackageManager.DELETE_SHOW_DIALOG),
+ eq(DELETE_ARCHIVE | DELETE_KEEP_DATA),
eq(mIntentSender),
eq(UserHandle.CURRENT.getIdentifier()), anyInt());
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index fd6aa0c..e6298ee 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -19,9 +19,11 @@
import static android.os.UserManager.DISALLOW_SMS;
import static android.os.UserManager.DISALLOW_USER_SWITCH;
import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
+import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -30,20 +32,27 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
+import android.app.KeyguardManager;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
+import android.multiuser.Flags;
+import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
@@ -52,6 +61,7 @@
import androidx.test.annotation.UiThreadTest;
+import com.android.dx.mockito.inline.extended.MockedVoidMethod;
import com.android.internal.widget.LockSettingsInternal;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.testing.ExtendedMockitoRule;
@@ -65,6 +75,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -115,8 +126,11 @@
.spyStatic(LocalServices.class)
.spyStatic(SystemProperties.class)
.mockStatic(Settings.Global.class)
+ .mockStatic(Settings.Secure.class)
.build();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private final Object mPackagesLock = new Object();
private final Context mRealContext = androidx.test.InstrumentationRegistry.getInstrumentation()
.getTargetContext();
@@ -133,6 +147,8 @@
private @Mock StorageManager mStorageManager;
private @Mock LockSettingsInternal mLockSettingsInternal;
private @Mock PackageManagerInternal mPackageManagerInternal;
+ private @Mock KeyguardManager mKeyguardManager;
+ private @Mock PowerManager mPowerManager;
/**
* Reference to the {@link UserManagerService} being tested.
@@ -156,6 +172,8 @@
when(mDeviceStorageMonitorInternal.isMemoryLow()).thenReturn(false);
mockGetLocalService(DeviceStorageMonitorInternal.class, mDeviceStorageMonitorInternal);
when(mSpiedContext.getSystemService(StorageManager.class)).thenReturn(mStorageManager);
+ when(mSpiedContext.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
+ when(mSpiedContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
mockGetLocalService(LockSettingsInternal.class, mLockSettingsInternal);
mockGetLocalService(PackageManagerInternal.class, mPackageManagerInternal);
doNothing().when(mSpiedContext).sendBroadcastAsUser(any(), any(), any());
@@ -550,6 +568,143 @@
assertTrue(hasRestrictionsInUserXMLFile(user.id));
}
+ @Test
+ public void testAutoLockOnDeviceLockForPrivateProfile() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
+ UserManagerService mSpiedUms = spy(mUms);
+ UserInfo privateProfileUser =
+ mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
+ USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
+ mockAutoLockForPrivateSpace(Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK);
+ Mockito.doNothing().when(mSpiedUms).setQuietModeEnabledAsync(
+ eq(privateProfileUser.getUserHandle().getIdentifier()), eq(true), any(),
+ any());
+
+ mSpiedUms.tryAutoLockingPrivateSpaceOnKeyguardChanged(true);
+
+ Mockito.verify(mSpiedUms).setQuietModeEnabledAsync(
+ eq(privateProfileUser.getUserHandle().getIdentifier()), eq(true),
+ any(), any());
+ }
+
+ @Test
+ public void testAutoLockOnDeviceLockForPrivateProfile_keyguardUnlocked() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
+ UserManagerService mSpiedUms = spy(mUms);
+ UserInfo privateProfileUser =
+ mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
+ USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
+ mockAutoLockForPrivateSpace(Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK);
+
+ mSpiedUms.tryAutoLockingPrivateSpaceOnKeyguardChanged(false);
+
+ // Verify that no operation to disable quiet mode is not called
+ Mockito.verify(mSpiedUms, never()).setQuietModeEnabledAsync(
+ eq(privateProfileUser.getUserHandle().getIdentifier()), eq(true),
+ any(), any());
+ }
+
+ @Test
+ public void testAutoLockOnDeviceLockForPrivateProfile_flagDisabled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
+ UserManagerService mSpiedUms = spy(mUms);
+ UserInfo privateProfileUser =
+ mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
+ USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
+
+ mSpiedUms.tryAutoLockingPrivateSpaceOnKeyguardChanged(true);
+
+ // Verify that no auto-lock operations take place
+ verify((MockedVoidMethod) () -> Settings.Secure.getInt(any(),
+ eq(Settings.Secure.PRIVATE_SPACE_AUTO_LOCK), anyInt()), never());
+ Mockito.verify(mSpiedUms, never()).setQuietModeEnabledAsync(
+ eq(privateProfileUser.getUserHandle().getIdentifier()), eq(true),
+ any(), any());
+ }
+
+ @Test
+ public void testAutoLockAfterInactityForPrivateProfile() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
+ UserManagerService mSpiedUms = spy(mUms);
+ mockAutoLockForPrivateSpace(Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY);
+ when(mPowerManager.isInteractive()).thenReturn(false);
+
+ UserInfo privateProfileUser =
+ mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
+ USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
+ Mockito.doNothing().when(mSpiedUms).scheduleMessageToAutoLockPrivateSpace(
+ eq(privateProfileUser.getUserHandle().getIdentifier()), any(),
+ anyLong());
+
+
+ mSpiedUms.maybeScheduleMessageToAutoLockPrivateSpace();
+
+ Mockito.verify(mSpiedUms).scheduleMessageToAutoLockPrivateSpace(
+ eq(privateProfileUser.getUserHandle().getIdentifier()), any(), anyLong());
+ }
+
+ @Test
+ public void testSetOrUpdateAutoLockPreference_noPrivateProfile() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
+
+ mUms.setOrUpdateAutoLockPreferenceForPrivateProfile(
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY);
+
+ Mockito.verify(mSpiedContext, never()).registerReceiver(any(), any(), any(), any());
+ Mockito.verify(mSpiedContext, never()).unregisterReceiver(any());
+ Mockito.verify(mKeyguardManager, never()).removeKeyguardLockedStateListener((any()));
+ Mockito.verify(mKeyguardManager, never()).addKeyguardLockedStateListener(any(), any());
+ }
+
+ @Test
+ public void testSetOrUpdateAutoLockPreference() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
+ mUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
+ USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
+
+ // Set the preference to auto lock on device lock
+ mUms.setOrUpdateAutoLockPreferenceForPrivateProfile(
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK);
+
+ // Verify that keyguard state listener was added
+ Mockito.verify(mKeyguardManager).addKeyguardLockedStateListener(any(), any());
+ //Verity that keyguard state listener was not removed
+ Mockito.verify(mKeyguardManager, never()).removeKeyguardLockedStateListener(any());
+ // Broadcasts are already unregistered when UserManagerService starts and the flag
+ // isDeviceInactivityBroadcastReceiverRegistered is false
+ Mockito.verify(mSpiedContext, never()).registerReceiver(any(), any(), any(), any());
+ Mockito.verify(mSpiedContext, never()).unregisterReceiver(any());
+
+ Mockito.clearInvocations(mKeyguardManager);
+ Mockito.clearInvocations(mSpiedContext);
+
+ // Now set the preference to auto-lock on inactivity
+ mUms.setOrUpdateAutoLockPreferenceForPrivateProfile(
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY);
+
+ // Verify that inactivity broadcasts are registered
+ Mockito.verify(mSpiedContext, times(2)).registerReceiver(any(), any(), any(), any());
+ // Verify that keyguard state listener is removed
+ Mockito.verify(mKeyguardManager).removeKeyguardLockedStateListener(any());
+ // Verify that all other operations don't take place
+ Mockito.verify(mSpiedContext, never()).unregisterReceiver(any());
+ Mockito.verify(mKeyguardManager, never()).addKeyguardLockedStateListener(any(), any());
+
+ Mockito.clearInvocations(mKeyguardManager);
+ Mockito.clearInvocations(mSpiedContext);
+
+ // Finally, set the preference to don't auto-lock
+ mUms.setOrUpdateAutoLockPreferenceForPrivateProfile(
+ Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_NEVER);
+
+ // Verify that inactivity broadcasts are unregistered and keyguard listener was removed
+ Mockito.verify(mSpiedContext).unregisterReceiver(any());
+ Mockito.verify(mKeyguardManager).removeKeyguardLockedStateListener(any());
+ // Verify that no broadcasts were registered and no listeners were added
+ Mockito.verify(mSpiedContext, never()).registerReceiver(any(), any(), any(), any());
+ Mockito.verify(mKeyguardManager, never()).addKeyguardLockedStateListener(any(), any());
+ }
+
/**
* Returns true if the user's XML file has Default restrictions
* @param userId Id of the user.
@@ -632,6 +787,12 @@
SystemProperties.getBoolean(eq("fw.show_multiuserui"), anyBoolean()));
}
+ private void mockAutoLockForPrivateSpace(int val) {
+ doReturn(val).when(() ->
+ Settings.Secure.getIntForUser(any(), eq(Settings.Secure.PRIVATE_SPACE_AUTO_LOCK),
+ anyInt(), anyInt()));
+ }
+
private void mockCurrentUser(@UserIdInt int userId) {
mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);
diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
deleted file mode 100644
index 523c5c0..0000000
--- a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.test.AndroidTestCase;
-
-import com.android.server.os.TombstoneProtos;
-import com.android.server.os.TombstoneProtos.Tombstone;
-
-public class BootReceiverTest extends AndroidTestCase {
- private static final String TAG = "BootReceiverTest";
-
- public void testRemoveMemoryFromTombstone() {
- Tombstone tombstoneBase = Tombstone.newBuilder()
- .setBuildFingerprint("build_fingerprint")
- .setRevision("revision")
- .setPid(123)
- .setTid(23)
- .setUid(34)
- .setSelinuxLabel("selinux_label")
- .addCommandLine("cmd1")
- .addCommandLine("cmd2")
- .addCommandLine("cmd3")
- .setProcessUptime(300)
- .setAbortMessage("abort")
- .addCauses(TombstoneProtos.Cause.newBuilder()
- .setHumanReadable("cause1")
- .setMemoryError(TombstoneProtos.MemoryError.newBuilder()
- .setTool(TombstoneProtos.MemoryError.Tool.SCUDO)
- .setType(TombstoneProtos.MemoryError.Type.DOUBLE_FREE)))
- .addLogBuffers(TombstoneProtos.LogBuffer.newBuilder().setName("name").addLogs(
- TombstoneProtos.LogMessage.newBuilder()
- .setTimestamp("123")
- .setMessage("message")))
- .addOpenFds(TombstoneProtos.FD.newBuilder().setFd(1).setPath("path"))
- .build();
-
- Tombstone tombstoneWithoutMemory = tombstoneBase.toBuilder()
- .putThreads(1, TombstoneProtos.Thread.newBuilder()
- .setId(1)
- .setName("thread1")
- .addRegisters(TombstoneProtos.Register.newBuilder().setName("r1").setU64(1))
- .addRegisters(TombstoneProtos.Register.newBuilder().setName("r2").setU64(2))
- .addBacktraceNote("backtracenote1")
- .addUnreadableElfFiles("files1")
- .setTaggedAddrCtrl(1)
- .setPacEnabledKeys(10)
- .build())
- .build();
-
- Tombstone tombstoneWithMemory = tombstoneBase.toBuilder()
- .addMemoryMappings(TombstoneProtos.MemoryMapping.newBuilder()
- .setBeginAddress(1)
- .setEndAddress(100)
- .setOffset(10)
- .setRead(true)
- .setWrite(true)
- .setExecute(false)
- .setMappingName("mapping")
- .setBuildId("build")
- .setLoadBias(70))
- .putThreads(1, TombstoneProtos.Thread.newBuilder()
- .setId(1)
- .setName("thread1")
- .addRegisters(TombstoneProtos.Register.newBuilder().setName("r1").setU64(1))
- .addRegisters(TombstoneProtos.Register.newBuilder().setName("r2").setU64(2))
- .addBacktraceNote("backtracenote1")
- .addUnreadableElfFiles("files1")
- .addMemoryDump(TombstoneProtos.MemoryDump.newBuilder()
- .setRegisterName("register1")
- .setMappingName("mapping")
- .setBeginAddress(10))
- .setTaggedAddrCtrl(1)
- .setPacEnabledKeys(10)
- .build())
- .build();
-
- assertThat(BootReceiver.removeMemoryFromTombstone(tombstoneWithMemory))
- .isEqualTo(tombstoneWithoutMemory);
- }
-}
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 4e1c72a..2f29d10 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -47,6 +47,7 @@
"flag-junit",
"notification_flags_lib",
"platform-test-rules",
+ "SettingsLib",
],
libs: [
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index db46532..839cf7c 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -17,6 +17,9 @@
package com.android.server;
import static android.Manifest.permission.MODIFY_DAY_NIGHT_MODE;
+import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_DAY;
+import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT;
+import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF;
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
@@ -32,6 +35,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.fail;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
@@ -65,6 +69,7 @@
import android.Manifest;
import android.app.Activity;
import android.app.AlarmManager;
+import android.app.Flags;
import android.app.IOnProjectionStateChangedListener;
import android.app.IUiModeManager;
import android.content.BroadcastReceiver;
@@ -84,6 +89,8 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.test.FakePermissionEnforcer;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.test.mock.MockContentResolver;
@@ -98,6 +105,7 @@
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -109,6 +117,7 @@
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.List;
+import java.util.Map;
import java.util.function.Consumer;
@RunWith(AndroidTestingRunner.class)
@@ -159,6 +168,11 @@
private TwilightListener mTwilightListener;
private FakePermissionEnforcer mPermissionEnforcer;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
+ SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+
+
@Before
public void setUp() {
// The AIDL stub will use PermissionEnforcer to check permission from the caller.
@@ -1437,6 +1451,51 @@
verify(mInjector).startDreamWhenDockedIfAppropriate(mContext);
}
+ private void testAttentionModeThemeOverlay(boolean modeNight) throws RemoteException {
+ //setup
+ if (modeNight) {
+ mService.setNightMode(MODE_NIGHT_YES);
+ assertTrue(mUiManagerService.getConfiguration().isNightModeActive());
+ } else {
+ mService.setNightMode(MODE_NIGHT_NO);
+ assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
+ }
+
+ // attention modes with expected night modes
+ Map<Integer, Boolean> modes = Map.of(
+ MODE_ATTENTION_THEME_OVERLAY_OFF, modeNight,
+ MODE_ATTENTION_THEME_OVERLAY_DAY, false,
+ MODE_ATTENTION_THEME_OVERLAY_NIGHT, true
+ );
+
+ // test
+ for (int aMode : modes.keySet()) {
+ try {
+ mService.setAttentionModeThemeOverlay(aMode);
+
+ int appliedAMode = mService.getAttentionModeThemeOverlay();
+ boolean nMode = modes.get(aMode);
+
+ assertEquals(aMode, appliedAMode);
+ assertEquals(isNightModeActivated(), nMode);
+ } catch (RemoteException e) {
+ fail("Error communicating with server: " + e.getMessage());
+ }
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void testAttentionModeThemeOverlay_nightModeDisabled() throws RemoteException {
+ testAttentionModeThemeOverlay(false);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void testAttentionModeThemeOverlay_nightModeEnabled() throws RemoteException {
+ testAttentionModeThemeOverlay(true);
+ }
+
private void triggerDockIntent() {
final Intent dockedIntent =
new Intent(Intent.ACTION_DOCK_EVENT)
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 30843d2..3797dbb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -16,7 +16,8 @@
package com.android.server.notification;
-import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
+import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT;
+import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF;
import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP;
import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER;
@@ -121,28 +122,49 @@
verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(true));
verify(mColorDisplayManager).setSaturationLevel(eq(0));
verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f));
- verify(mUiModeManager).setNightModeActivatedForCustomMode(
- eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true));
+ verify(mUiModeManager).setAttentionModeThemeOverlay(eq(MODE_ATTENTION_THEME_OVERLAY_NIGHT));
}
@Test
- public void apply_removesPreviouslyAppliedEffects() {
+ public void apply_removesEffects() {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
ZenDeviceEffects previousEffects = new ZenDeviceEffects.Builder()
.setShouldSuppressAmbientDisplay(true)
.setShouldDimWallpaper(true)
+ .setShouldDisplayGrayscale(true)
+ .setShouldUseNightMode(true)
.build();
mApplier.apply(previousEffects, UPDATE_ORIGIN_USER);
verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(true));
+ verify(mColorDisplayManager).setSaturationLevel(eq(0));
verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f));
+ verify(mUiModeManager).setAttentionModeThemeOverlay(eq(MODE_ATTENTION_THEME_OVERLAY_NIGHT));
ZenDeviceEffects noEffects = new ZenDeviceEffects.Builder().build();
mApplier.apply(noEffects, UPDATE_ORIGIN_USER);
verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(false));
+ verify(mColorDisplayManager).setSaturationLevel(eq(100));
verify(mWallpaperManager).setWallpaperDimAmount(eq(0.0f));
- verifyZeroInteractions(mColorDisplayManager, mUiModeManager);
+ verify(mUiModeManager).setAttentionModeThemeOverlay(eq(MODE_ATTENTION_THEME_OVERLAY_OFF));
+ }
+
+ @Test
+ public void apply_removesOnlyPreviouslyAppliedEffects() {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+ ZenDeviceEffects previousEffects = new ZenDeviceEffects.Builder()
+ .setShouldSuppressAmbientDisplay(true)
+ .build();
+ mApplier.apply(previousEffects, UPDATE_ORIGIN_USER);
+ verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(true));
+
+ ZenDeviceEffects noEffects = new ZenDeviceEffects.Builder().build();
+ mApplier.apply(noEffects, UPDATE_ORIGIN_USER);
+
+ verify(mPowerManager).suppressAmbientDisplay(anyString(), eq(false));
+ verifyZeroInteractions(mColorDisplayManager, mWallpaperManager, mUiModeManager);
}
@Test
@@ -150,6 +172,7 @@
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mContext.addMockSystemService(ColorDisplayManager.class, null);
mContext.addMockSystemService(WallpaperManager.class, null);
+ mApplier = new DefaultDeviceEffectsApplier(mContext);
ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
.setShouldSuppressAmbientDisplay(true)
@@ -177,7 +200,7 @@
verify(mWallpaperManager).setWallpaperDimAmount(eq(0.6f));
verify(mPowerManager, never()).suppressAmbientDisplay(anyString(), anyBoolean());
- verify(mUiModeManager, never()).setNightModeActivatedForCustomMode(anyInt(), anyBoolean());
+ verify(mUiModeManager, never()).setAttentionModeThemeOverlay(anyInt());
}
@Test
@@ -223,8 +246,7 @@
screenOffReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
// So the effect is applied, and we stopped listening for this event.
- verify(mUiModeManager).setNightModeActivatedForCustomMode(
- eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true));
+ verify(mUiModeManager).setAttentionModeThemeOverlay(eq(MODE_ATTENTION_THEME_OVERLAY_NIGHT));
verify(mContext).unregisterReceiver(eq(screenOffReceiver));
}
@@ -239,8 +261,7 @@
origin.value());
// Effect was applied, and no broadcast receiver was registered.
- verify(mUiModeManager).setNightModeActivatedForCustomMode(
- eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true));
+ verify(mUiModeManager).setAttentionModeThemeOverlay(eq(MODE_ATTENTION_THEME_OVERLAY_NIGHT));
verify(mContext, never()).registerReceiver(any(), any(), anyInt());
}
@@ -256,8 +277,7 @@
origin.value());
// Effect was applied, and no broadcast receiver was registered.
- verify(mUiModeManager).setNightModeActivatedForCustomMode(
- eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true));
+ verify(mUiModeManager).setAttentionModeThemeOverlay(eq(MODE_ATTENTION_THEME_OVERLAY_NIGHT));
verify(mContext, never()).registerReceiver(any(), any(), anyInt());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java b/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
index 71dbc57..2986372 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -28,7 +29,6 @@
import org.junit.After;
import org.junit.Test;
-import java.util.Collections;
import java.util.Set;
/**
@@ -52,18 +52,21 @@
@After
public void tearDown() {
- mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ mSensitiveContentPackages.clearBlockedApps();
}
@Test
- public void setShouldBlockScreenCaptureForApp() {
- Set<PackageInfo> blockedApps =
+ public void addBlockScreenCaptureForApps() {
+ ArraySet<PackageInfo> blockedApps = new ArraySet(
Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
new PackageInfo(APP_PKG_1, APP_UID_2),
new PackageInfo(APP_PKG_2, APP_UID_1),
- new PackageInfo(APP_PKG_2, APP_UID_2));
+ new PackageInfo(APP_PKG_2, APP_UID_2)
+ ));
- mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(blockedApps);
+ boolean modified = mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
+
+ assertTrue(modified);
assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
@@ -79,15 +82,93 @@
}
@Test
- public void setShouldBlockScreenCaptureForApp_empty() {
- Set<PackageInfo> blockedApps =
+ public void addBlockScreenCaptureForApps_addedTwice() {
+ ArraySet<PackageInfo> blockedApps = new ArraySet(
Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
new PackageInfo(APP_PKG_1, APP_UID_2),
new PackageInfo(APP_PKG_2, APP_UID_1),
- new PackageInfo(APP_PKG_2, APP_UID_2));
+ new PackageInfo(APP_PKG_2, APP_UID_2)
+ ));
- mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(blockedApps);
- mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
+ boolean modified = mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
+
+ assertFalse(modified);
+
+ assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
+ assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
+
+ assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
+ assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
+
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ }
+
+ @Test
+ public void addBlockScreenCaptureForApps_withPartialPreviousPackages() {
+ ArraySet<PackageInfo> blockedApps = new ArraySet(
+ Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
+ new PackageInfo(APP_PKG_1, APP_UID_2),
+ new PackageInfo(APP_PKG_2, APP_UID_1),
+ new PackageInfo(APP_PKG_2, APP_UID_2)
+ ));
+
+ mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
+ boolean modified = mSensitiveContentPackages
+ .addBlockScreenCaptureForApps(
+ new ArraySet(Set.of(new PackageInfo(APP_PKG_3, APP_UID_1))));
+
+ assertTrue(modified);
+
+ assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
+ assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
+
+ assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
+ assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
+
+ assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ }
+
+ @Test
+ public void clearBlockedApps() {
+ ArraySet<PackageInfo> blockedApps = new ArraySet(
+ Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
+ new PackageInfo(APP_PKG_1, APP_UID_2),
+ new PackageInfo(APP_PKG_2, APP_UID_1),
+ new PackageInfo(APP_PKG_2, APP_UID_2)
+ ));
+
+ mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
+ boolean modified = mSensitiveContentPackages.clearBlockedApps();
+
+ assertTrue(modified);
+
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
+
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
+
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
+ assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ }
+
+ @Test
+ public void clearBlockedApps_alreadyEmpty() {
+ boolean modified = mSensitiveContentPackages.clearBlockedApps();
+
+ assertFalse(modified);
assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index a1cc8d5..fe9d837 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -115,7 +115,6 @@
import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
-import java.util.Collections;
/**
* Build/Install/Run:
@@ -139,7 +138,7 @@
@After
public void tearDown() {
- mWm.mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ mWm.mSensitiveContentPackages.clearBlockedApps();
}
@Test
@@ -824,7 +823,7 @@
}
@Test
- public void setShouldBlockScreenCaptureForApp() {
+ public void addBlockScreenCaptureForApps() {
String testPackage = "test";
int ownerId1 = 20;
int ownerId2 = 21;
@@ -833,7 +832,7 @@
blockedPackages.add(blockedPackage);
WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
- wmInternal.setShouldBlockScreenCaptureForApp(blockedPackages);
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages);
assertTrue(mWm.mSensitiveContentPackages
.shouldBlockScreenCaptureForApp(testPackage, ownerId1));
@@ -843,7 +842,47 @@
}
@Test
- public void setShouldBlockScreenCaptureForApp_emptySet_clearsCache() {
+ public void addBlockScreenCaptureForApps_duplicate_verifyNoRefresh() {
+ String testPackage = "test";
+ int ownerId1 = 20;
+ int ownerId2 = 21;
+ PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId1);
+ ArraySet<PackageInfo> blockedPackages = new ArraySet();
+ blockedPackages.add(blockedPackage);
+
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages);
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages);
+
+ verify(mWm, times(1)).refreshScreenCaptureDisabled();
+ }
+
+ @Test
+ public void addBlockScreenCaptureForApps_notDuplicate_verifyRefresh() {
+ String testPackage = "test";
+ int ownerId1 = 20;
+ int ownerId2 = 21;
+ PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId1);
+ PackageInfo blockedPackage2 = new PackageInfo(testPackage, ownerId2);
+ ArraySet<PackageInfo> blockedPackages = new ArraySet();
+ blockedPackages.add(blockedPackage);
+ ArraySet<PackageInfo> blockedPackages2 = new ArraySet();
+ blockedPackages2.add(blockedPackage);
+ blockedPackages2.add(blockedPackage2);
+
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages);
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages2);
+
+ assertTrue(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
+ assertTrue(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId2));
+ verify(mWm, times(2)).refreshScreenCaptureDisabled();
+ }
+
+ @Test
+ public void clearBlockedApps_clearsCache() {
String testPackage = "test";
int ownerId1 = 20;
PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId1);
@@ -851,8 +890,8 @@
blockedPackages.add(blockedPackage);
WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
- wmInternal.setShouldBlockScreenCaptureForApp(blockedPackages);
- wmInternal.setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages);
+ wmInternal.clearBlockedApps();
assertFalse(mWm.mSensitiveContentPackages
.shouldBlockScreenCaptureForApp(testPackage, ownerId1));
@@ -860,6 +899,14 @@
}
@Test
+ public void clearBlockedApps_alreadyEmpty_verifyNoRefresh() {
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ wmInternal.clearBlockedApps();
+
+ verify(mWm, never()).refreshScreenCaptureDisabled();
+ }
+
+ @Test
public void testisLetterboxBackgroundMultiColored() {
assertThat(setupLetterboxConfigurationWithBackgroundType(
LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING)).isTrue();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index fb4edfa..a0562aa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -136,7 +136,7 @@
@After
public void tearDown() {
- mWm.mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(Collections.emptySet());
+ mWm.mSensitiveContentPackages.clearBlockedApps();
}
@Test
@@ -1398,7 +1398,7 @@
PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId1);
ArraySet<PackageInfo> blockedPackages = new ArraySet();
blockedPackages.add(blockedPackage);
- mWm.mSensitiveContentPackages.setShouldBlockScreenCaptureForApp(blockedPackages);
+ mWm.mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedPackages);
assertTrue(window1.isSecureLocked());
assertFalse(window2.isSecureLocked());
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 527159a..70a9540 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -120,12 +120,13 @@
path: "src",
}
-// Make the current.txt available for use by the cts/tests/signature tests.
+// Make the current.txt available for use by the cts/tests/signature and /vendor tests.
// ========================================================================
filegroup {
name: "android-test-base-current.txt",
visibility: [
"//cts/tests/signature/api",
+ "//vendor:__subpackages__",
],
srcs: [
"api/current.txt",
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 2ff7413..f37d2d1 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -77,12 +77,13 @@
auto_gen_config: true,
}
-// Make the current.txt available for use by the cts/tests/signature tests.
+// Make the current.txt available for use by the cts/tests/signature and /vendor tests.
// ========================================================================
filegroup {
name: "android-test-mock-current.txt",
visibility: [
"//cts/tests/signature/api",
+ "//vendor:__subpackages__",
],
srcs: [
"api/current.txt",
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index 13a5dac..21e09d3 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -79,12 +79,13 @@
],
}
-// Make the current.txt available for use by the cts/tests/signature tests.
+// Make the current.txt available for use by the cts/tests/signature and /vendor tests.
// ========================================================================
filegroup {
name: "android-test-runner-current.txt",
visibility: [
"//cts/tests/signature/api",
+ "//vendor:__subpackages__",
],
srcs: [
"api/current.txt",
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index f056110..1a82021 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -381,9 +381,9 @@
return true;
}
- bool FlattenTypeSpec(const ResourceTableTypeView& type,
- const std::vector<ResourceTableEntryView>& sorted_entries,
- BigBuffer* buffer) {
+ ResTable_typeSpec* FlattenTypeSpec(const ResourceTableTypeView& type,
+ const std::vector<ResourceTableEntryView>& sorted_entries,
+ BigBuffer* buffer) {
ChunkWriter type_spec_writer(buffer);
ResTable_typeSpec* spec_header =
type_spec_writer.StartChunk<ResTable_typeSpec>(RES_TABLE_TYPE_SPEC_TYPE);
@@ -391,7 +391,7 @@
if (sorted_entries.empty()) {
type_spec_writer.Finish();
- return true;
+ return spec_header;
}
// We can't just take the size of the vector. There may be holes in the
@@ -427,7 +427,7 @@
}
}
type_spec_writer.Finish();
- return true;
+ return spec_header;
}
bool FlattenTypes(BigBuffer* buffer) {
@@ -450,7 +450,8 @@
expected_type_id++;
type_pool_.MakeRef(type.named_type.to_string());
- if (!FlattenTypeSpec(type, type.entries, buffer)) {
+ const auto type_spec_header = FlattenTypeSpec(type, type.entries, buffer);
+ if (!type_spec_header) {
return false;
}
@@ -511,6 +512,10 @@
return false;
}
}
+
+ // And now we can update the type entries count in the typeSpec header.
+ type_spec_header->typesCount = android::util::HostToDevice16(uint16_t(std::min<uint32_t>(
+ config_to_entry_list_map.size(), std::numeric_limits<uint16_t>::max())));
}
return true;
}