Merge "Rethrow RemoteException from dispatchMediaKeyEvent call" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
new file mode 100644
index 0000000..16907b3
--- /dev/null
+++ b/AconfigFlags.bp
@@ -0,0 +1,43 @@
+// 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.
+
+// Aconfig declarations and libraries for the core framework
+java_defaults {
+ name: "framework-minus-apex-aconfig-libraries",
+
+ // Add java_aconfig_libraries to here to add them to the core framework
+ srcs: [
+ ":com.android.hardware.camera2-aconfig-java{.generated_srcjars}",
+ ],
+}
+
+// Default flags for java_aconfig_libraries that go into framework-minus-apex
+// These libraries will not work standalone
+java_defaults {
+ name: "framework-minus-apex-aconfig-java-defaults",
+ sdk_version: "core_platform",
+ libs: ["fake_device_config"],
+}
+
+aconfig_declarations {
+ name: "com.android.hardware.camera2-aconfig",
+ package: "com.android.hardware.camera2",
+ srcs: ["core/java/android/hardware/camera2/camera_platform.aconfig"],
+}
+
+java_aconfig_library {
+ name: "com.android.hardware.camera2-aconfig-java",
+ aconfig_declarations: "com.android.hardware.camera2-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index be589b2..53554bc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -267,6 +267,7 @@
defaults: [
"framework-aidl-export-defaults",
"latest_android_hardware_soundtrigger3_java_static",
+ "framework-minus-apex-aconfig-libraries",
],
srcs: [
":framework-non-updatable-sources",
@@ -701,6 +702,7 @@
}
build = [
+ "AconfigFlags.bp",
"ProtoLibraries.bp",
"TestProtoLibraries.bp",
]
diff --git a/OWNERS b/OWNERS
index 8ee488d..6c25324 100644
--- a/OWNERS
+++ b/OWNERS
@@ -16,8 +16,6 @@
ogunwale@google.com #{LAST_RESORT_SUGGESTION}
roosa@google.com #{LAST_RESORT_SUGGESTION}
smoreland@google.com #{LAST_RESORT_SUGGESTION}
-svetoslavganov@android.com #{LAST_RESORT_SUGGESTION}
-svetoslavganov@google.com #{LAST_RESORT_SUGGESTION}
yamasani@google.com #{LAST_RESORT_SUGGESTION}
# API changes are already covered by API-Review+1 (http://mdb/android-api-council)
@@ -30,7 +28,7 @@
# Support bulk translation updates
per-file */res*/values*/*.xml = byi@google.com, delphij@google.com
-per-file **.bp,**.mk = hansson@google.com
+per-file **.bp,**.mk = hansson@google.com, joeo@google.com
per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 3a05323..5dc994e 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -371,7 +371,7 @@
/**
* Allows this job to run despite doze restrictions as long as the app is in the foreground
- * or on the temporary whitelist
+ * or on the temporary allowlist
* @hide
*/
public static final int FLAG_IMPORTANT_WHILE_FOREGROUND = 1 << 1;
@@ -2044,13 +2044,13 @@
/**
* Setting this to true indicates that this job is important while the scheduling app
- * is in the foreground or on the temporary whitelist for background restrictions.
+ * is in the foreground or on the temporary allowlist for background restrictions.
* This means that the system will relax doze restrictions on this job during this time.
*
* Apps should use this flag only for short jobs that are essential for the app to function
* properly in the foreground.
*
- * Note that once the scheduling app is no longer whitelisted from background restrictions
+ * Note that once the scheduling app is no longer allowlisted from background restrictions
* and in the background, or the job failed due to unsatisfied constraints,
* this job should be expected to behave like other jobs without this flag.
*
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index 4ce31e9..20da171 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -29,10 +29,10 @@
import java.util.List;
/**
- * Interface to access and modify the permanent and temporary power save whitelist. The two lists
- * are kept separately. Apps placed on the permanent whitelist are only removed via an explicit
- * removeFromWhitelist call. Apps whitelisted by default by the system cannot be removed. Apps
- * placed on the temporary whitelist are removed from that whitelist after a predetermined amount of
+ * Interface to access and modify the permanent and temporary power save allowlist. The two lists
+ * are kept separately. Apps placed on the permanent allowlist are only removed via an explicit
+ * removeFromAllowlist call. Apps whitelisted by default by the system cannot be removed. Apps
+ * placed on the temporary allowlist are removed from that allowlist after a predetermined amount of
* time.
*
* @deprecated Use {@link PowerExemptionManager} instead
@@ -50,18 +50,18 @@
private final PowerExemptionManager mPowerExemptionManager;
/**
- * Indicates that an unforeseen event has occurred and the app should be whitelisted to handle
+ * Indicates that an unforeseen event has occurred and the app should be allowlisted to handle
* it.
*/
public static final int EVENT_UNSPECIFIED = PowerExemptionManager.EVENT_UNSPECIFIED;
/**
- * Indicates that an SMS event has occurred and the app should be whitelisted to handle it.
+ * Indicates that an SMS event has occurred and the app should be allowlisted to handle it.
*/
public static final int EVENT_SMS = PowerExemptionManager.EVENT_SMS;
/**
- * Indicates that an MMS event has occurred and the app should be whitelisted to handle it.
+ * Indicates that an MMS event has occurred and the app should be allowlisted to handle it.
*/
public static final int EVENT_MMS = PowerExemptionManager.EVENT_MMS;
@@ -381,7 +381,7 @@
}
/**
- * Add the specified package to the permanent power save whitelist.
+ * Add the specified package to the permanent power save allowlist.
*
* @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(String)} instead
*/
@@ -392,7 +392,7 @@
}
/**
- * Add the specified packages to the permanent power save whitelist.
+ * Add the specified packages to the permanent power save allowlist.
*
* @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(List)} instead
*/
@@ -403,10 +403,10 @@
}
/**
- * Get a list of app IDs of app that are whitelisted. This does not include temporarily
- * whitelisted apps.
+ * Get a list of app IDs of app that are allowlisted. This does not include temporarily
+ * allowlisted apps.
*
- * @param includingIdle Set to true if the app should be whitelisted from device idle as well
+ * @param includingIdle Set to true if the app should be allowlisted from device idle as well
* as other power save restrictions
* @deprecated Use {@link PowerExemptionManager#getAllowListedAppIds(boolean)} instead
* @hide
@@ -418,10 +418,10 @@
}
/**
- * Returns true if the app is whitelisted from power save restrictions. This does not include
- * temporarily whitelisted apps.
+ * Returns true if the app is allowlisted from power save restrictions. This does not include
+ * temporarily allowlisted apps.
*
- * @param includingIdle Set to true if the app should be whitelisted from device
+ * @param includingIdle Set to true if the app should be allowlisted from device
* idle as well as other power save restrictions
* @deprecated Use {@link PowerExemptionManager#isAllowListed(String, boolean)} instead
* @hide
@@ -432,11 +432,11 @@
}
/**
- * Remove an app from the permanent power save whitelist. Only apps that were added via
+ * Remove an app from the permanent power save allowlist. Only apps that were added via
* {@link #addToWhitelist(String)} or {@link #addToWhitelist(List)} will be removed. Apps
- * whitelisted by default by the system cannot be removed.
+ * allowlisted by default by the system cannot be removed.
*
- * @param packageName The app to remove from the whitelist
+ * @param packageName The app to remove from the allowlist
* @deprecated Use {@link PowerExemptionManager#removeFromPermanentAllowList(String)} instead
*/
@Deprecated
@@ -446,10 +446,10 @@
}
/**
- * Add an app to the temporary whitelist for a short amount of time.
+ * Add an app to the temporary allowlist for a short amount of time.
*
- * @param packageName The package to add to the temp whitelist
- * @param durationMs How long to keep the app on the temp whitelist for (in milliseconds)
+ * @param packageName The package to add to the temp allowlist
+ * @param durationMs How long to keep the app on the temp allowlist for (in milliseconds)
* @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
* @param reason a optional human readable reason string, could be null or empty string.
* @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
@@ -463,10 +463,10 @@
}
/**
- * Add an app to the temporary whitelist for a short amount of time.
+ * Add an app to the temporary allowlist for a short amount of time.
*
- * @param packageName The package to add to the temp whitelist
- * @param durationMs How long to keep the app on the temp whitelist for (in milliseconds)
+ * @param packageName The package to add to the temp allowlist
+ * @param durationMs How long to keep the app on the temp allowlist for (in milliseconds)
* @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
* String, int, String, long)} instead
*/
@@ -478,15 +478,15 @@
}
/**
- * Add an app to the temporary whitelist for a short amount of time for a specific reason. The
- * temporary whitelist is kept separately from the permanent whitelist and apps are
- * automatically removed from the temporary whitelist after a predetermined amount of time.
+ * Add an app to the temporary allowlist for a short amount of time for a specific reason. The
+ * temporary allowlist is kept separately from the permanent allowlist and apps are
+ * automatically removed from the temporary allowlist after a predetermined amount of time.
*
- * @param packageName The package to add to the temp whitelist
- * @param event The reason to add the app to the temp whitelist
- * @param reason A human-readable reason explaining why the app is temp whitelisted. Only
+ * @param packageName The package to add to the temp allowlist
+ * @param event The reason to add the app to the temp allowlist
+ * @param reason A human-readable reason explaining why the app is temp allowlisted. Only
* used for logging purposes. Could be null or empty string.
- * @return The duration (in milliseconds) that the app is whitelisted for
+ * @return The duration (in milliseconds) that the app is allowlisted for
* @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
* String, int, String, int)} instead
*/
@@ -499,16 +499,16 @@
}
/**
- * Add an app to the temporary whitelist for a short amount of time for a specific reason. The
- * temporary whitelist is kept separately from the permanent whitelist and apps are
- * automatically removed from the temporary whitelist after a predetermined amount of time.
+ * Add an app to the temporary allowlist for a short amount of time for a specific reason. The
+ * temporary allowlist is kept separately from the permanent allowlist and apps are
+ * automatically removed from the temporary allowlist after a predetermined amount of time.
*
- * @param packageName The package to add to the temp whitelist
- * @param event The reason to add the app to the temp whitelist
+ * @param packageName The package to add to the temp allowlist
+ * @param event The reason to add the app to the temp allowlist
* @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
- * @param reason A human-readable reason explaining why the app is temp whitelisted. Only
+ * @param reason A human-readable reason explaining why the app is temp allowlisted. Only
* used for logging purposes. Could be null or empty string.
- * @return The duration (in milliseconds) that the app is whitelisted for
+ * @return The duration (in milliseconds) that the app is allowlisted for
* @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
* String, int, String, int)} instead
*/
diff --git a/api/Android.bp b/api/Android.bp
index c16bce5..e9cc405 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -83,6 +83,7 @@
"framework-configinfrastructure",
"framework-connectivity",
"framework-connectivity-t",
+ "framework-crashrecovery",
"framework-devicelock",
"framework-graphics",
"framework-healthfitness",
@@ -104,6 +105,7 @@
system_server_classpath: [
"service-art",
"service-configinfrastructure",
+ "service-crashrecovery",
"service-healthfitness",
"service-media-s",
"service-permission",
diff --git a/boot/Android.bp b/boot/Android.bp
index 83a46c5..93d425e 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -84,6 +84,10 @@
module: "com.android.conscrypt-bootclasspath-fragment",
},
{
+ apex: "com.android.crashrecovery",
+ module: "com.android.crashrecovery-bootclasspath-fragment",
+ },
+ {
apex: "com.android.devicelock",
module: "com.android.devicelock-bootclasspath-fragment",
},
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 10ce3b8..774ba74 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -1565,6 +1565,7 @@
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
+ glBindTexture(GL_TEXTURE_2D, 0);
// Handle animation package
if (part.animation != nullptr) {
@@ -1641,8 +1642,10 @@
if (r > 0) {
glBindTexture(GL_TEXTURE_2D, frame.tid);
} else {
- glGenTextures(1, &frame.tid);
- glBindTexture(GL_TEXTURE_2D, frame.tid);
+ if (part.count != 1) {
+ glGenTextures(1, &frame.tid);
+ glBindTexture(GL_TEXTURE_2D, frame.tid);
+ }
int w, h;
// Set decoding option to alpha unpremultiplied so that the R, G, B channels
// of transparent pixels are preserved.
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
index 766ca56..1d78460 100644
--- a/cmds/idmap2/libidmap2/XmlParser.cpp
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -109,7 +109,7 @@
switch (value.dataType) {
case Res_value::TYPE_STRING: {
if (auto str = parser.getStrings().string8ObjectAt(value.data); str.ok()) {
- return std::string(str->string());
+ return std::string(str->c_str());
}
break;
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 9ba84c9..3cb7468 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -15651,6 +15651,7 @@
public final class Gainmap implements android.os.Parcelable {
ctor public Gainmap(@NonNull android.graphics.Bitmap);
+ ctor public Gainmap(@NonNull android.graphics.Gainmap, @NonNull android.graphics.Bitmap);
method public int describeContents();
method @NonNull public float getDisplayRatioForFullHdr();
method @NonNull public float[] getEpsilonHdr();
@@ -44882,6 +44883,7 @@
field public static final int OUT_OF_NETWORK = 11; // 0xb
field public static final int OUT_OF_SERVICE = 18; // 0x12
field public static final int POWER_OFF = 17; // 0x11
+ field public static final int SATELLITE_ENABLED = 82; // 0x52
field public static final int SERVER_ERROR = 12; // 0xc
field public static final int SERVER_UNREACHABLE = 9; // 0x9
field public static final int TIMED_OUT = 13; // 0xd
@@ -46006,6 +46008,7 @@
field public static final int ERI_FLASH = 2; // 0x2
field public static final int ERI_OFF = 1; // 0x1
field public static final int ERI_ON = 0; // 0x0
+ field public static final String EVENT_DISPLAY_SOS_MESSAGE = "android.telephony.event.DISPLAY_SOS_MESSAGE";
field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
field public static final String EXTRA_APN_PROTOCOL = "android.telephony.extra.APN_PROTOCOL";
field public static final String EXTRA_APN_TYPE = "android.telephony.extra.APN_TYPE";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e61c39f..773d720 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -837,6 +837,23 @@
package android.companion {
+ public static final class AssociationInfo.Builder {
+ ctor public AssociationInfo.Builder(int, int, @NonNull String);
+ ctor public AssociationInfo.Builder(@NonNull android.companion.AssociationInfo);
+ method @NonNull public android.companion.AssociationInfo build();
+ method @NonNull public android.companion.AssociationInfo.Builder setAssociatedDevice(@Nullable android.companion.AssociatedDevice);
+ method @NonNull public android.companion.AssociationInfo.Builder setDeviceMacAddress(@Nullable android.net.MacAddress);
+ method @NonNull public android.companion.AssociationInfo.Builder setDeviceProfile(@Nullable String);
+ method @NonNull public android.companion.AssociationInfo.Builder setDisplayName(@Nullable CharSequence);
+ method @NonNull public android.companion.AssociationInfo.Builder setLastTimeConnected(long);
+ method @NonNull public android.companion.AssociationInfo.Builder setNotifyOnDeviceNearby(boolean);
+ method @NonNull public android.companion.AssociationInfo.Builder setRevoked(boolean);
+ method @NonNull public android.companion.AssociationInfo.Builder setSelfManaged(boolean);
+ method @NonNull public android.companion.AssociationInfo.Builder setSystemDataSyncFlags(int);
+ method @NonNull public android.companion.AssociationInfo.Builder setTag(@Nullable String);
+ method @NonNull public android.companion.AssociationInfo.Builder setTimeApproved(long);
+ }
+
public final class CompanionDeviceManager {
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void enableSecureTransport(boolean);
field public static final int MESSAGE_REQUEST_PING = 1669362552; // 0x63807378
@@ -1877,6 +1894,8 @@
public class AudioManager {
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String);
+ method @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean enterAudioFocusFreezeForTest(@NonNull java.util.List<java.lang.Integer>);
+ method @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public boolean exitAudioFocusFreezeForTest();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void forceComputeCsdOnAllDevices(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void forceUseFrameworkMel(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat);
@@ -1884,6 +1903,9 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public float getCsd();
method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
+ method @NonNull @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public java.util.List<java.lang.Integer> getFocusDuckedUidsForTest();
+ method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusFadeOutDurationForTest();
+ method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusUnmuteDelayAfterFadeOutForTest();
method @Nullable public static android.media.AudioHalVersionInfo getHalVersion();
method public static final int[] getPublicStreamTypes();
method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 4851279..d0d76a4 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -454,12 +454,11 @@
*/
protected void onStart() {
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
- if (mContext != null
+ if (allowsRegisterDefaultOnBackInvokedCallback() && mContext != null
&& WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
// Add onBackPressed as default back behavior.
mDefaultBackCallback = this::onBackPressed;
getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback);
- mDefaultBackCallback = null;
}
}
@@ -470,9 +469,18 @@
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
if (mDefaultBackCallback != null) {
getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mDefaultBackCallback);
+ mDefaultBackCallback = null;
}
}
+ /**
+ * Whether this dialog allows to register the default onBackInvokedCallback.
+ * @hide
+ */
+ protected boolean allowsRegisterDefaultOnBackInvokedCallback() {
+ return true;
+ }
+
private static final String DIALOG_SHOWING_TAG = "android:dialogShowing";
private static final String DIALOG_HIERARCHY_TAG = "android:dialogHierarchy";
@@ -697,7 +705,8 @@
if (event.isTracking() && !event.isCanceled()) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
- if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
+ if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)
+ || !allowsRegisterDefaultOnBackInvokedCallback()) {
onBackPressed();
return true;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 892b45e..bf5b428 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1628,6 +1628,14 @@
*/
public static final int GROUP_ALERT_CHILDREN = 2;
+ /**
+ * Constant for the {@link Builder#setGroup(String) group key} that is added to notifications
+ * that are not already grouped when {@link Builder#setSilent()} is used.
+ *
+ * @hide
+ */
+ public static final String GROUP_KEY_SILENT = "silent";
+
private int mGroupAlertBehavior = GROUP_ALERT_ALL;
/**
@@ -2892,11 +2900,6 @@
}
}
- final Person person = extras.getParcelable(EXTRA_MESSAGING_PERSON, Person.class);
- if (person != null) {
- person.visitUris(visitor);
- }
-
final RemoteInputHistoryItem[] history = extras.getParcelableArray(
Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS,
RemoteInputHistoryItem.class);
@@ -2908,9 +2911,14 @@
}
}
}
- }
- if (isStyle(MessagingStyle.class) && extras != null) {
+ // Extras for MessagingStyle. We visit them even if not isStyle(MessagingStyle), since
+ // Notification Listeners might use directly (without the isStyle check).
+ final Person person = extras.getParcelable(EXTRA_MESSAGING_PERSON, Person.class);
+ if (person != null) {
+ person.visitUris(visitor);
+ }
+
final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES,
Parcelable.class);
if (!ArrayUtils.isEmpty(messages)) {
@@ -2930,9 +2938,8 @@
}
visitIconUri(visitor, extras.getParcelable(EXTRA_CONVERSATION_ICON, Icon.class));
- }
- if (isStyle(CallStyle.class) & extras != null) {
+ // Extras for CallStyle (same reason for visiting without checking isStyle).
Person callPerson = extras.getParcelable(EXTRA_CALL_PERSON, Person.class);
if (callPerson != null) {
callPerson.visitUris(visitor);
@@ -4290,6 +4297,35 @@
}
/**
+ * If {@code true}, silences this instance of the notification, regardless of the sounds or
+ * vibrations set on the notification or notification channel. If {@code false}, then the
+ * normal sound and vibration logic applies.
+ *
+ * @hide
+ */
+ public @NonNull Builder setSilent(boolean silent) {
+ if (!silent) {
+ return this;
+ }
+ if (mN.isGroupSummary()) {
+ setGroupAlertBehavior(GROUP_ALERT_CHILDREN);
+ } else {
+ setGroupAlertBehavior(GROUP_ALERT_SUMMARY);
+ }
+
+ setVibrate(null);
+ setSound(null);
+ mN.defaults &= ~DEFAULT_SOUND;
+ mN.defaults &= ~DEFAULT_VIBRATE;
+ setDefaults(mN.defaults);
+
+ if (TextUtils.isEmpty(mN.mGroupKey)) {
+ setGroup(GROUP_KEY_SILENT);
+ }
+ return this;
+ }
+
+ /**
* Set the first line of text in the platform notification template.
*/
@NonNull
@@ -12819,7 +12855,6 @@
} else {
mBackgroundColor = rawColor;
}
- mProtectionColor = COLOR_INVALID; // filled in at the end
mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
ContrastColorUtil.resolvePrimaryColor(ctx, mBackgroundColor, nightMode),
mBackgroundColor, 4.5);
@@ -12836,7 +12871,6 @@
} else {
int[] attrs = {
R.attr.colorSurface,
- R.attr.colorBackgroundFloating,
R.attr.textColorPrimary,
R.attr.textColorSecondary,
R.attr.colorAccent,
@@ -12848,15 +12882,14 @@
};
try (TypedArray ta = obtainDayNightAttributes(ctx, attrs)) {
mBackgroundColor = getColor(ta, 0, nightMode ? Color.BLACK : Color.WHITE);
- mProtectionColor = getColor(ta, 1, COLOR_INVALID);
- mPrimaryTextColor = getColor(ta, 2, COLOR_INVALID);
- mSecondaryTextColor = getColor(ta, 3, COLOR_INVALID);
- mPrimaryAccentColor = getColor(ta, 4, COLOR_INVALID);
- mSecondaryAccentColor = getColor(ta, 5, COLOR_INVALID);
- mTertiaryAccentColor = getColor(ta, 6, COLOR_INVALID);
- mOnAccentTextColor = getColor(ta, 7, COLOR_INVALID);
- mErrorColor = getColor(ta, 8, COLOR_INVALID);
- mRippleAlpha = Color.alpha(getColor(ta, 9, 0x33ffffff));
+ mPrimaryTextColor = getColor(ta, 1, COLOR_INVALID);
+ mSecondaryTextColor = getColor(ta, 2, COLOR_INVALID);
+ mPrimaryAccentColor = getColor(ta, 3, COLOR_INVALID);
+ mSecondaryAccentColor = getColor(ta, 4, COLOR_INVALID);
+ mTertiaryAccentColor = getColor(ta, 5, COLOR_INVALID);
+ mOnAccentTextColor = getColor(ta, 6, COLOR_INVALID);
+ mErrorColor = getColor(ta, 7, COLOR_INVALID);
+ mRippleAlpha = Color.alpha(getColor(ta, 8, 0x33ffffff));
}
mContrastColor = calculateContrastColor(ctx, rawColor, mPrimaryAccentColor,
mBackgroundColor, nightMode);
@@ -12889,9 +12922,7 @@
}
}
// make sure every color has a valid value
- if (mProtectionColor == COLOR_INVALID) {
- mProtectionColor = ColorUtils.blendARGB(mPrimaryTextColor, mBackgroundColor, 0.8f);
- }
+ mProtectionColor = ColorUtils.blendARGB(mPrimaryTextColor, mBackgroundColor, 0.9f);
}
/** calculates the contrast color for the non-colorized notifications */
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 33b8b03..715edc5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16111,11 +16111,6 @@
* Called by a profile owner of an organization-owned managed profile to suspend personal
* apps on the device. When personal apps are suspended the device can only be used for calls.
*
- * <p>When personal apps are suspended, an ongoing notification about that is shown to the user.
- * When the user taps the notification, system invokes {@link #ACTION_CHECK_POLICY_COMPLIANCE}
- * in the profile owner package. Profile owner implementation that uses personal apps suspension
- * must handle this intent.
- *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with
* @param suspended Whether personal apps should be suspended.
* @throws IllegalStateException if the profile owner doesn't have an activity that handles
diff --git a/core/java/android/app/search/SearchSession.java b/core/java/android/app/search/SearchSession.java
index 0dbd81e..99b64a0 100644
--- a/core/java/android/app/search/SearchSession.java
+++ b/core/java/android/app/search/SearchSession.java
@@ -104,7 +104,7 @@
mInterface = android.app.search.ISearchUiManager.Stub.asInterface(b);
mSessionId = new SearchSessionId(
context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId());
- // b/175527717 whitelist possible clients of this API
+ // b/175527717 allowlist possible clients of this API
searchContext.setPackageName(context.getPackageName());
try {
mInterface.createSearchSession(searchContext, mSessionId, mToken);
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 7d62c79..083fa00 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -17,7 +17,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.net.MacAddress;
import android.os.Parcel;
@@ -41,24 +43,26 @@
private static final String LAST_TIME_CONNECTED_NONE = "None";
/**
* A unique ID of this Association record.
- * Disclosed to the clients (ie. companion applications) for referring to this record (eg. in
+ * Disclosed to the clients (i.e. companion applications) for referring to this record (e.g. in
* {@code disassociate()} API call).
*/
private final int mId;
-
- private final @UserIdInt int mUserId;
- private final @NonNull String mPackageName;
-
- private final @Nullable MacAddress mDeviceMacAddress;
- private final @Nullable CharSequence mDisplayName;
- private final @Nullable String mDeviceProfile;
- private final @Nullable AssociatedDevice mAssociatedDevice;
-
+ @UserIdInt
+ private final int mUserId;
+ @NonNull
+ private final String mPackageName;
+ @Nullable
+ private final String mTag;
+ @Nullable
+ private final MacAddress mDeviceMacAddress;
+ @Nullable
+ private final CharSequence mDisplayName;
+ @Nullable
+ private final String mDeviceProfile;
+ @Nullable
+ private final AssociatedDevice mAssociatedDevice;
private final boolean mSelfManaged;
private final boolean mNotifyOnDeviceNearby;
- private final int mSystemDataSyncFlags;
- private final String mTag;
-
/**
* Indicates that the association has been revoked (removed), but we keep the association
* record for final clean up (e.g. removing the app from the list of the role holders).
@@ -72,6 +76,7 @@
* Default value is Long.MAX_VALUE.
*/
private final long mLastTimeConnectedMs;
+ private final int mSystemDataSyncFlags;
/**
* Creates a new Association.
@@ -93,16 +98,13 @@
}
mId = id;
-
mUserId = userId;
mPackageName = packageName;
-
mDeviceMacAddress = macAddress;
mDisplayName = displayName;
mTag = tag;
mDeviceProfile = deviceProfile;
mAssociatedDevice = associatedDevice;
-
mSelfManaged = selfManaged;
mNotifyOnDeviceNearby = notifyOnDeviceNearby;
mRevoked = revoked;
@@ -119,18 +121,11 @@
}
/**
- * @return the tag of this association.
- * @see CompanionDeviceManager#setAssociationTag(int, String)
- */
- public @Nullable String getTag() {
- return mTag;
- }
-
- /**
* @return the ID of the user who "owns" this association.
* @hide
*/
- public @UserIdInt int getUserId() {
+ @UserIdInt
+ public int getUserId() {
return mUserId;
}
@@ -139,19 +134,31 @@
* @hide
*/
@SystemApi
- public @NonNull String getPackageName() {
+ @NonNull
+ public String getPackageName() {
return mPackageName;
}
/**
+ * @return the tag of this association.
+ * @see CompanionDeviceManager#setAssociationTag(int, String)
+ */
+ @Nullable
+ public String getTag() {
+ return mTag;
+ }
+
+ /**
* @return the MAC address of the device.
*/
- public @Nullable MacAddress getDeviceMacAddress() {
+ @Nullable
+ public MacAddress getDeviceMacAddress() {
return mDeviceMacAddress;
}
/** @hide */
- public @Nullable String getDeviceMacAddressAsString() {
+ @Nullable
+ public String getDeviceMacAddressAsString() {
return mDeviceMacAddress != null ? mDeviceMacAddress.toString().toUpperCase() : null;
}
@@ -161,7 +168,8 @@
*
* @see AssociationRequest.Builder#setDisplayName(CharSequence)
*/
- public @Nullable CharSequence getDisplayName() {
+ @Nullable
+ public CharSequence getDisplayName() {
return mDisplayName;
}
@@ -170,7 +178,8 @@
* association, or {@code null} if no specific profile was used.
* @see AssociationRequest.Builder#setDeviceProfile(String)
*/
- public @Nullable String getDeviceProfile() {
+ @Nullable
+ public String getDeviceProfile() {
return mDeviceProfile;
}
@@ -187,7 +196,8 @@
* @return the companion device that was associated, or {@code null} if the device is
* self-managed or this association info was retrieved from persistent storage.
*/
- public @Nullable AssociatedDevice getAssociatedDevice() {
+ @Nullable
+ public AssociatedDevice getAssociatedDevice() {
return mAssociatedDevice;
}
@@ -228,14 +238,14 @@
* @return the last time self reported disconnected for selfManaged only.
* @hide
*/
- public Long getLastTimeConnectedMs() {
+ public long getLastTimeConnectedMs() {
return mLastTimeConnectedMs;
}
/**
* @return Enabled system data sync flags set via
- * {@link CompanionDeviceManager#enableSystemDataSync(int, int)} and
- * {@link CompanionDeviceManager#disableSystemDataSync(int, int)}.
+ * {@link CompanionDeviceManager#enableSystemDataSyncForTypes(int, int)} (int, int)} and
+ * {@link CompanionDeviceManager#disableSystemDataSyncForTypes(int, int)} (int, int)}.
* Or by default all flags are 1 (enabled).
*/
public int getSystemDataSyncFlags() {
@@ -279,7 +289,8 @@
}
/** @hide */
- public @NonNull String toShortString() {
+ @NonNull
+ public String toShortString() {
final StringBuilder sb = new StringBuilder();
sb.append("id=").append(mId);
if (mDeviceMacAddress != null) {
@@ -350,16 +361,13 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mId);
-
dest.writeInt(mUserId);
dest.writeString(mPackageName);
dest.writeString(mTag);
-
dest.writeTypedObject(mDeviceMacAddress, 0);
dest.writeCharSequence(mDisplayName);
dest.writeString(mDeviceProfile);
dest.writeTypedObject(mAssociatedDevice, 0);
-
dest.writeBoolean(mSelfManaged);
dest.writeBoolean(mNotifyOnDeviceNearby);
dest.writeBoolean(mRevoked);
@@ -370,16 +378,13 @@
private AssociationInfo(@NonNull Parcel in) {
mId = in.readInt();
-
mUserId = in.readInt();
mPackageName = in.readString();
mTag = in.readString();
-
mDeviceMacAddress = in.readTypedObject(MacAddress.CREATOR);
mDisplayName = in.readCharSequence();
mDeviceProfile = in.readString();
mAssociatedDevice = in.readTypedObject(AssociatedDevice.CREATOR);
-
mSelfManaged = in.readBoolean();
mNotifyOnDeviceNearby = in.readBoolean();
mRevoked = in.readBoolean();
@@ -403,45 +408,137 @@
};
/**
- * Use this method to obtain a builder that you can use to create a copy of the
- * given {@link AssociationInfo} with modified values of {@code mLastTimeConnected}
- * or {@code mNotifyOnDeviceNearby}.
- * <p>
- * Note that you <b>must</b> call either {@link Builder#setLastTimeConnected(long)
- * setLastTimeConnected} or {@link Builder#setNotifyOnDeviceNearby(boolean)
- * setNotifyOnDeviceNearby} before you will be able to call {@link Builder#build() build}.
- *
- * This is ensured statically at compile time.
+ * Builder for {@link AssociationInfo}
*
* @hide
*/
- @NonNull
- public static NonActionableBuilder builder(@NonNull AssociationInfo info) {
- return new Builder(info);
- }
-
- /** @hide */
- public static final class Builder implements NonActionableBuilder {
- @NonNull
- private final AssociationInfo mOriginalInfo;
+ @TestApi
+ public static final class Builder {
+ private final int mId;
+ private final int mUserId;
+ private final String mPackageName;
+ private String mTag;
+ private MacAddress mDeviceMacAddress;
+ private CharSequence mDisplayName;
+ private String mDeviceProfile;
+ private AssociatedDevice mAssociatedDevice;
+ private boolean mSelfManaged;
private boolean mNotifyOnDeviceNearby;
private boolean mRevoked;
+ private long mTimeApprovedMs;
private long mLastTimeConnectedMs;
private int mSystemDataSyncFlags;
- private String mTag;
- private Builder(@NonNull AssociationInfo info) {
- mOriginalInfo = info;
+ /** @hide */
+ @TestApi
+ public Builder(int id, int userId, @NonNull String packageName) {
+ mId = id;
+ mUserId = userId;
+ mPackageName = packageName;
+ }
+
+ /** @hide */
+ @TestApi
+ public Builder(@NonNull AssociationInfo info) {
+ mId = info.mId;
+ mUserId = info.mUserId;
+ mPackageName = info.mPackageName;
mTag = info.mTag;
+ mDeviceMacAddress = info.mDeviceMacAddress;
+ mDisplayName = info.mDisplayName;
+ mDeviceProfile = info.mDeviceProfile;
+ mAssociatedDevice = info.mAssociatedDevice;
+ mSelfManaged = info.mSelfManaged;
mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
mRevoked = info.mRevoked;
+ mTimeApprovedMs = info.mTimeApprovedMs;
mLastTimeConnectedMs = info.mLastTimeConnectedMs;
mSystemDataSyncFlags = info.mSystemDataSyncFlags;
}
/** @hide */
- @Override
+ @TestApi
@NonNull
+ public Builder setTag(@Nullable String tag) {
+ mTag = tag;
+ return this;
+ }
+
+ /** @hide */
+ @TestApi
+ @NonNull
+ public Builder setDeviceMacAddress(@Nullable MacAddress deviceMacAddress) {
+ mDeviceMacAddress = deviceMacAddress;
+ return this;
+ }
+
+ /** @hide */
+ @TestApi
+ @NonNull
+ public Builder setDisplayName(@Nullable CharSequence displayName) {
+ mDisplayName = displayName;
+ return this;
+ }
+
+ /** @hide */
+ @TestApi
+ @NonNull
+ public Builder setDeviceProfile(@Nullable String deviceProfile) {
+ mDeviceProfile = deviceProfile;
+ return this;
+ }
+
+ /** @hide */
+ @TestApi
+ @NonNull
+ public Builder setAssociatedDevice(@Nullable AssociatedDevice associatedDevice) {
+ mAssociatedDevice = associatedDevice;
+ return this;
+ }
+
+ /** @hide */
+ @TestApi
+ @NonNull
+ public Builder setSelfManaged(boolean selfManaged) {
+ mSelfManaged = selfManaged;
+ return this;
+ }
+
+ /** @hide */
+ @TestApi
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
+ mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+ return this;
+ }
+
+ /** @hide */
+ @TestApi
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setRevoked(boolean revoked) {
+ mRevoked = revoked;
+ return this;
+ }
+
+ /** @hide */
+ @TestApi
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setTimeApproved(long timeApprovedMs) {
+ if (timeApprovedMs < 0) {
+ throw new IllegalArgumentException("timeApprovedMs must be positive. Was given ("
+ + timeApprovedMs + ")");
+ }
+ mTimeApprovedMs = timeApprovedMs;
+ return this;
+ }
+
+ /** @hide */
+ @TestApi
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
public Builder setLastTimeConnected(long lastTimeConnectedMs) {
if (lastTimeConnectedMs < 0) {
throw new IllegalArgumentException(
@@ -453,23 +550,7 @@
}
/** @hide */
- @Override
- @NonNull
- public Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
- mNotifyOnDeviceNearby = notifyOnDeviceNearby;
- return this;
- }
-
- /** @hide */
- @Override
- @NonNull
- public Builder setRevoked(boolean revoked) {
- mRevoked = revoked;
- return this;
- }
-
- /** @hide */
- @Override
+ @TestApi
@NonNull
public Builder setSystemDataSyncFlags(int flags) {
mSystemDataSyncFlags = flags;
@@ -477,65 +558,32 @@
}
/** @hide */
- @Override
- @NonNull
- public Builder setTag(String tag) {
- mTag = tag;
- return this;
- }
-
- /** @hide */
+ @TestApi
@NonNull
public AssociationInfo build() {
+ if (mId <= 0) {
+ throw new IllegalArgumentException("Association ID should be greater than 0");
+ }
+ if (mDeviceMacAddress == null && mDisplayName == null) {
+ throw new IllegalArgumentException("MAC address and the display name must NOT be "
+ + "null at the same time");
+ }
return new AssociationInfo(
- mOriginalInfo.mId,
- mOriginalInfo.mUserId,
- mOriginalInfo.mPackageName,
+ mId,
+ mUserId,
+ mPackageName,
mTag,
- mOriginalInfo.mDeviceMacAddress,
- mOriginalInfo.mDisplayName,
- mOriginalInfo.mDeviceProfile,
- mOriginalInfo.mAssociatedDevice,
- mOriginalInfo.mSelfManaged,
+ mDeviceMacAddress,
+ mDisplayName,
+ mDeviceProfile,
+ mAssociatedDevice,
+ mSelfManaged,
mNotifyOnDeviceNearby,
mRevoked,
- mOriginalInfo.mTimeApprovedMs,
+ mTimeApprovedMs,
mLastTimeConnectedMs,
mSystemDataSyncFlags
);
}
}
-
- /**
- * This interface is returned from the
- * {@link AssociationInfo#builder(android.companion.AssociationInfo) builder} entry point
- * to indicate that this builder is not yet in a state that can produce a meaningful
- * {@link AssociationInfo} object that is different from the one originally passed in.
- *
- * <p>
- * Only by calling one of the setter methods is this builder turned into one where calling
- * {@link Builder#build() build()} makes sense.
- *
- * @hide
- */
- public interface NonActionableBuilder {
- /** @hide */
- @NonNull
- Builder setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby);
-
- /** @hide */
- @NonNull
- Builder setLastTimeConnected(long lastTimeConnectedMs);
-
- /** @hide */
- @NonNull
- Builder setRevoked(boolean revoked);
-
- /** @hide */
- @NonNull
- Builder setSystemDataSyncFlags(int flags);
-
- /** @hide */
- Builder setTag(String tag);
- }
}
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index ee7836f..ed8484f 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -21,6 +21,7 @@
import android.companion.virtual.IVirtualDeviceSoundEffectListener;
import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceParams;
+import android.content.AttributionSource;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
@@ -46,7 +47,7 @@
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
IVirtualDevice createVirtualDevice(
- in IBinder token, String packageName, int associationId,
+ in IBinder token, in AttributionSource attributionSource, int associationId,
in VirtualDeviceParams params, in IVirtualDeviceActivityListener activityListener,
in IVirtualDeviceSoundEffectListener soundEffectListener);
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index f68cfff..d13bfd4 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -145,7 +145,7 @@
mContext = context.getApplicationContext();
mVirtualDevice = service.createVirtualDevice(
new Binder(),
- mContext.getPackageName(),
+ mContext.getAttributionSource(),
associationId,
params,
mActivityListenerBinder,
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 45d6dc6..b6d8375 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -43,6 +43,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
+import java.io.PrintWriter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -501,6 +502,26 @@
+ ")";
}
+ /**
+ * Dumps debugging information about the VirtualDeviceParams
+ * @hide
+ */
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "mName=" + mName);
+ pw.println(prefix + "mLockState=" + mLockState);
+ pw.println(prefix + "mUsersWithMatchingAccounts=" + mUsersWithMatchingAccounts);
+ pw.println(prefix + "mAllowedCrossTaskNavigations=" + mAllowedCrossTaskNavigations);
+ pw.println(prefix + "mBlockedCrossTaskNavigations=" + mBlockedCrossTaskNavigations);
+ pw.println(prefix + "mAllowedActivities=" + mAllowedActivities);
+ pw.println(prefix + "mBlockedActivities=" + mBlockedActivities);
+ pw.println(prefix + "mDevicePolicies=" + mDevicePolicies);
+ pw.println(prefix + "mDefaultNavigationPolicy=" + mDefaultNavigationPolicy);
+ pw.println(prefix + "mDefaultActivityPolicy=" + mDefaultActivityPolicy);
+ pw.println(prefix + "mVirtualSensorConfigs=" + mVirtualSensorConfigs);
+ pw.println(prefix + "mAudioPlaybackSessionId=" + mAudioPlaybackSessionId);
+ pw.println(prefix + "mAudioRecordingSessionId=" + mAudioRecordingSessionId);
+ }
+
@NonNull
public static final Parcelable.Creator<VirtualDeviceParams> CREATOR =
new Parcelable.Creator<VirtualDeviceParams>() {
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index 3bdf9aa..0dbe411 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -104,6 +104,11 @@
parcel.writeInt(mFlags);
}
+ @Override
+ public String toString() {
+ return "VirtualSensorConfig{" + "mType=" + mType + ", mName='" + mName + '\'' + '}';
+ }
+
/**
* Returns the type of the sensor.
*
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index c3df17d..529363f 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -344,15 +344,22 @@
// If there is a label for the launcher intent, then use that as it is typically shorter.
// Otherwise, just use the top-level application name.
Intent launchIntent = pm.getLaunchIntentForPackage(mContext.getPackageName());
+ if (launchIntent == null) {
+ return getDefaultCallingApplicationLabel();
+ }
List<ResolveInfo> infos =
pm.queryIntentActivities(
launchIntent, PackageManager.ResolveInfoFlags.of(MATCH_DEFAULT_ONLY));
if (infos.size() > 0) {
return infos.get(0).loadLabel(pm);
}
+ return getDefaultCallingApplicationLabel();
+ }
+
+ private CharSequence getDefaultCallingApplicationLabel() {
return mContext.getApplicationInfo()
.loadSafeLabel(
- pm,
+ mContext.getPackageManager(),
/* ellipsizeDip= */ 0,
TextUtils.SAFE_STRING_FLAG_SINGLE_LINE
| TextUtils.SAFE_STRING_FLAG_TRIM);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c5585af..ea0f5ff 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -304,6 +304,8 @@
boolean isPackageSuspendedForUser(String packageName, int userId);
+ boolean isPackageQuarantinedForUser(String packageName, int userId);
+
Bundle getSuspendedPackageAppExtras(String packageName, int userId);
/**
diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java
index e931fe8..1ef3d27 100644
--- a/core/java/android/content/res/Element.java
+++ b/core/java/android/content/res/Element.java
@@ -42,7 +42,7 @@
public static final int MAX_ATTR_LEN_PATH = 4000;
public static final int MAX_ATTR_LEN_DATA_VALUE = 4000;
- private static final String BAD_COMPONENT_NAME_CHARS = ";,[](){}:?-%^*|/\\";
+ private static final String BAD_COMPONENT_NAME_CHARS = ";,[](){}:?%^*|/\\";
private static final String TAG = "PackageParsing";
protected static final String TAG_ACTION = "action";
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index c2a0062..eedb25b 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -123,7 +123,7 @@
* credential, display a picker when multiple credentials exist, etc.
* Callers (e.g. browsers) may optionally set origin in {@link GetCredentialRequest} for an
* app different from their own, to be able to get credentials on behalf of that app. They would
- * need additional permission {@link CREDENTIAL_MANAGER_SET_ORIGIN}
+ * need additional permission {@code CREDENTIAL_MANAGER_SET_ORIGIN}
* to use this functionality
*
* @param context the context used to launch any UI needed; use an activity context to make sure
@@ -209,9 +209,9 @@
*
* <p>This API doesn't invoke any UI. It only performs the preparation work so that you can
* later launch the remaining get-credential operation (involves UIs) through the {@link
- * #getCredential(PrepareGetCredentialResponse.PendingGetCredentialHandle, Context,
+ * #getCredential(Context, PrepareGetCredentialResponse.PendingGetCredentialHandle,
* CancellationSignal, Executor, OutcomeReceiver)} API which incurs less latency compared to
- * the {@link #getCredential(GetCredentialRequest, Context, CancellationSignal, Executor,
+ * the {@link #getCredential(Context, GetCredentialRequest, CancellationSignal, Executor,
* OutcomeReceiver)} API that executes the whole operation in one call.
*
* @param request the request specifying type(s) of credentials to get from the user
@@ -261,7 +261,7 @@
* storing the new credential, etc.
* Callers (e.g. browsers) may optionally set origin in {@link CreateCredentialRequest} for an
* app different from their own, to be able to get credentials on behalf of that app. They would
- * need additional permission {@link CREDENTIAL_MANAGER_SET_ORIGIN}
+ * need additional permission {@code CREDENTIAL_MANAGER_SET_ORIGIN}
* to use this functionality
*
* @param context the context used to launch any UI needed; use an activity context to make sure
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 912e8df..af448f0 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -466,6 +466,19 @@
// LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/PromptInfo.java)
/**
+ * Set if emergency call button should show, for example if biometrics are
+ * required to access the dialer app
+ * @param showEmergencyCallButton if true, shows emergency call button
+ * @return This builder.
+ * @hide
+ */
+ @NonNull
+ public Builder setShowEmergencyCallButton(boolean showEmergencyCallButton) {
+ mPromptInfo.setShowEmergencyCallButton(showEmergencyCallButton);
+ return this;
+ }
+
+ /**
* Creates a {@link BiometricPrompt}.
*
* @return An instance of {@link BiometricPrompt}.
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index e275078..24cfd164 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -48,6 +48,7 @@
private boolean mAllowBackgroundAuthentication;
private boolean mIgnoreEnrollmentState;
private boolean mIsForLegacyFingerprintManager = false;
+ private boolean mShowEmergencyCallButton = false;
public PromptInfo() {
@@ -72,6 +73,7 @@
mAllowBackgroundAuthentication = in.readBoolean();
mIgnoreEnrollmentState = in.readBoolean();
mIsForLegacyFingerprintManager = in.readBoolean();
+ mShowEmergencyCallButton = in.readBoolean();
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -111,6 +113,7 @@
dest.writeBoolean(mAllowBackgroundAuthentication);
dest.writeBoolean(mIgnoreEnrollmentState);
dest.writeBoolean(mIsForLegacyFingerprintManager);
+ dest.writeBoolean(mShowEmergencyCallButton);
}
// LINT.IfChange
@@ -228,6 +231,10 @@
mAllowedSensorIds.add(sensorId);
}
+ public void setShowEmergencyCallButton(boolean showEmergencyCallButton) {
+ mShowEmergencyCallButton = showEmergencyCallButton;
+ }
+
// Getters
public CharSequence getTitle() {
@@ -309,4 +316,8 @@
public boolean isForLegacyFingerprintManager() {
return mIsForLegacyFingerprintManager;
}
+
+ public boolean isShowEmergencyCallButton() {
+ return mShowEmergencyCallButton;
+ }
}
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 6baf91d7..ea951a5 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -236,9 +236,10 @@
private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER =
new CameraExtensionManagerGlobal();
private final Object mLock = new Object();
- private final int PROXY_SERVICE_DELAY_MS = 1000;
+ private final int PROXY_SERVICE_DELAY_MS = 2000;
private InitializerFuture mInitFuture = null;
private ServiceConnection mConnection = null;
+ private int mConnectionCount = 0;
private ICameraExtensionsProxyService mProxy = null;
private boolean mSupportsAdvancedExtensions = false;
@@ -249,6 +250,15 @@
return GLOBAL_CAMERA_MANAGER;
}
+ private void releaseProxyConnectionLocked(Context ctx) {
+ if (mConnection != null ) {
+ ctx.unbindService(mConnection);
+ mConnection = null;
+ mProxy = null;
+ mConnectionCount = 0;
+ }
+ }
+
private void connectToProxyLocked(Context ctx) {
if (mConnection == null) {
Intent intent = new Intent();
@@ -270,7 +280,6 @@
mConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName component) {
- mInitFuture.setStatus(false);
mConnection = null;
mProxy = null;
}
@@ -348,23 +357,32 @@
public boolean registerClient(Context ctx, IBinder token) {
synchronized (mLock) {
+ boolean ret = false;
connectToProxyLocked(ctx);
if (mProxy == null) {
return false;
}
+ mConnectionCount++;
try {
- return mProxy.registerClient(token);
+ ret = mProxy.registerClient(token);
} catch (RemoteException e) {
Log.e(TAG, "Failed to initialize extension! Extension service does "
+ " not respond!");
}
+ if (!ret) {
+ mConnectionCount--;
+ }
- return false;
+ if (mConnectionCount <= 0) {
+ releaseProxyConnectionLocked(ctx);
+ }
+
+ return ret;
}
}
- public void unregisterClient(IBinder token) {
+ public void unregisterClient(Context ctx, IBinder token) {
synchronized (mLock) {
if (mProxy != null) {
try {
@@ -372,6 +390,11 @@
} catch (RemoteException e) {
Log.e(TAG, "Failed to de-initialize extension! Extension service does"
+ " not respond!");
+ } finally {
+ mConnectionCount--;
+ if (mConnectionCount <= 0) {
+ releaseProxyConnectionLocked(ctx);
+ }
}
}
}
@@ -446,8 +469,8 @@
/**
* @hide
*/
- public static void unregisterClient(IBinder token) {
- CameraExtensionManagerGlobal.get().unregisterClient(token);
+ public static void unregisterClient(Context ctx, IBinder token) {
+ CameraExtensionManagerGlobal.get().unregisterClient(ctx, token);
}
/**
@@ -578,7 +601,7 @@
}
}
} finally {
- unregisterClient(token);
+ unregisterClient(mContext, token);
}
return Collections.unmodifiableList(ret);
@@ -626,7 +649,7 @@
Log.e(TAG, "Failed to query the extension for postview availability! Extension "
+ "service does not respond!");
} finally {
- unregisterClient(token);
+ unregisterClient(mContext, token);
}
return false;
@@ -722,7 +745,7 @@
+ "service does not respond!");
return Collections.emptyList();
} finally {
- unregisterClient(token);
+ unregisterClient(mContext, token);
}
}
@@ -791,7 +814,7 @@
+ " not respond!");
return new ArrayList<>();
} finally {
- unregisterClient(token);
+ unregisterClient(mContext, token);
}
}
@@ -872,7 +895,7 @@
}
}
} finally {
- unregisterClient(token);
+ unregisterClient(mContext, token);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
@@ -957,7 +980,7 @@
Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
+ " not respond!");
} finally {
- unregisterClient(token);
+ unregisterClient(mContext, token);
}
return null;
@@ -998,7 +1021,7 @@
Log.e(TAG, "Failed to query the extension progress callbacks! Extension service does"
+ " not respond!");
} finally {
- unregisterClient(token);
+ unregisterClient(mContext, token);
}
return false;
@@ -1075,7 +1098,7 @@
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture request keys!");
} finally {
- unregisterClient(token);
+ unregisterClient(mContext, token);
}
return Collections.unmodifiableSet(ret);
@@ -1155,7 +1178,7 @@
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture result keys!");
} finally {
- unregisterClient(token);
+ unregisterClient(mContext, token);
}
return Collections.unmodifiableSet(ret);
diff --git a/core/java/android/hardware/camera2/camera_platform.aconfig b/core/java/android/hardware/camera2/camera_platform.aconfig
new file mode 100644
index 0000000..67f6300
--- /dev/null
+++ b/core/java/android/hardware/camera2/camera_platform.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.hardware.camera2"
+
+flag {
+ namespace: "camera_platform"
+ name: "initial_test_flag"
+ description: "Flag infrastructure test flag"
+ bug: "292631208"
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index e06699b..c7e74c0 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -90,7 +90,7 @@
private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>();
private RequestProcessor mRequestProcessor = new RequestProcessor();
private final int mSessionId;
- private final IBinder mToken;
+ private IBinder mToken = null;
private Surface mClientRepeatingRequestSurface;
private Surface mClientCaptureSurface;
@@ -103,6 +103,8 @@
private boolean mInitialized;
private boolean mSessionClosed;
+ private final Context mContext;
+
// Lock to synchronize cross-thread access to device public interface
final Object mInterfaceLock;
@@ -113,14 +115,9 @@
public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession(
@NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice,
@NonNull Map<String, CameraCharacteristics> characteristicsMap,
- @NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, int sessionId)
+ @NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, int sessionId,
+ @NonNull IBinder token)
throws CameraAccessException, RemoteException {
- final IBinder token = new Binder(TAG + " : " + sessionId);
- boolean success = CameraExtensionCharacteristics.registerClient(ctx, token);
- if (!success) {
- throw new UnsupportedOperationException("Unsupported extension!");
- }
-
String cameraId = cameraDevice.getId();
CameraExtensionCharacteristics extensionChars = new CameraExtensionCharacteristics(ctx,
cameraId, characteristicsMap);
@@ -204,8 +201,9 @@
IAdvancedExtenderImpl extender = CameraExtensionCharacteristics.initializeAdvancedExtension(
config.getExtension());
extender.init(cameraId, characteristicsMapNative);
- CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(extender,
- cameraDevice, characteristicsMapNative, repeatingRequestSurface,
+
+ CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(ctx,
+ extender, cameraDevice, characteristicsMapNative, repeatingRequestSurface,
burstCaptureSurface, postviewSurface, config.getStateCallback(),
config.getExecutor(), sessionId, token);
@@ -217,13 +215,16 @@
return ret;
}
- private CameraAdvancedExtensionSessionImpl(@NonNull IAdvancedExtenderImpl extender,
+ private CameraAdvancedExtensionSessionImpl(Context ctx,
+ @NonNull IAdvancedExtenderImpl extender,
@NonNull CameraDeviceImpl cameraDevice,
Map<String, CameraMetadataNative> characteristicsMap,
@Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface,
@Nullable Surface postviewSurface,
@NonNull StateCallback callback, @NonNull Executor executor,
- int sessionId, @NonNull IBinder token) {
+ int sessionId,
+ @NonNull IBinder token) {
+ mContext = ctx;
mAdvancedExtender = extender;
mCameraDevice = cameraDevice;
mCharacteristicsMap = characteristicsMap;
@@ -578,12 +579,16 @@
mSessionProcessor = null;
}
- CameraExtensionCharacteristics.unregisterClient(mToken);
- if (mInitialized || (mCaptureSession != null)) {
- notifyClose = true;
- CameraExtensionCharacteristics.releaseSession();
+
+ if (mToken != null) {
+ if (mInitialized || (mCaptureSession != null)) {
+ notifyClose = true;
+ CameraExtensionCharacteristics.releaseSession();
+ }
+ CameraExtensionCharacteristics.unregisterClient(mContext, mToken);
}
mInitialized = false;
+ mToken = null;
for (ImageReader reader : mReaderMap.values()) {
reader.close();
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index d3bde4b..181ab2c 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -2550,19 +2550,32 @@
HashMap<String, CameraCharacteristics> characteristicsMap = new HashMap<>(
mPhysicalIdsToChars);
characteristicsMap.put(mCameraId, mCharacteristics);
+ boolean initializationFailed = true;
+ IBinder token = new Binder(TAG + " : " + mNextSessionId++);
try {
+ boolean ret = CameraExtensionCharacteristics.registerClient(mContext, token);
+ if (!ret) {
+ token = null;
+ throw new UnsupportedOperationException("Unsupported extension!");
+ }
+
if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) {
mCurrentAdvancedExtensionSession =
CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession(
this, characteristicsMap, mContext, extensionConfiguration,
- mNextSessionId++);
+ mNextSessionId, token);
} else {
mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(
this, characteristicsMap, mContext, extensionConfiguration,
- mNextSessionId++);
+ mNextSessionId, token);
}
+ initializationFailed = false;
} catch (RemoteException e) {
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
+ } finally {
+ if (initializationFailed && (token != null)) {
+ CameraExtensionCharacteristics.unregisterClient(mContext, token);
+ }
}
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 5d25681..bf77681 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -91,7 +91,7 @@
private final Set<CaptureRequest.Key> mSupportedRequestKeys;
private final Set<CaptureResult.Key> mSupportedResultKeys;
private final ExtensionSessionStatsAggregator mStatsAggregator;
- private final IBinder mToken;
+ private IBinder mToken = null;
private boolean mCaptureResultsSupported;
private CameraCaptureSession mCaptureSession = null;
@@ -119,6 +119,8 @@
// will do so internally.
private boolean mInternalRepeatingRequestEnabled = true;
+ private final Context mContext;
+
// Lock to synchronize cross-thread access to device public interface
final Object mInterfaceLock;
@@ -135,14 +137,9 @@
@NonNull Map<String, CameraCharacteristics> characteristicsMap,
@NonNull Context ctx,
@NonNull ExtensionSessionConfiguration config,
- int sessionId)
+ int sessionId,
+ @NonNull IBinder token)
throws CameraAccessException, RemoteException {
- final IBinder token = new Binder(TAG + " : " + sessionId);
- boolean success = CameraExtensionCharacteristics.registerClient(ctx, token);
- if (!success) {
- throw new UnsupportedOperationException("Unsupported extension!");
- }
-
String cameraId = cameraDevice.getId();
CameraExtensionCharacteristics extensionChars = new CameraExtensionCharacteristics(ctx,
cameraId, characteristicsMap);
@@ -234,6 +231,7 @@
characteristicsMap.get(cameraId).getNativeMetadata());
CameraExtensionSessionImpl session = new CameraExtensionSessionImpl(
+ ctx,
extenders.second,
extenders.first,
supportedPreviewSizes,
@@ -256,7 +254,7 @@
return session;
}
- public CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender,
+ public CameraExtensionSessionImpl(Context ctx, @NonNull IImageCaptureExtenderImpl imageExtender,
@NonNull IPreviewExtenderImpl previewExtender,
@NonNull List<Size> previewSizes,
@NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice,
@@ -269,6 +267,7 @@
@NonNull IBinder token,
@NonNull Set<CaptureRequest.Key> requestKeys,
@Nullable Set<CaptureResult.Key> resultKeys) {
+ mContext = ctx;
mImageExtender = imageExtender;
mPreviewExtender = previewExtender;
mCameraDevice = cameraDevice;
@@ -878,12 +877,15 @@
+ " respond!");
}
- CameraExtensionCharacteristics.unregisterClient(mToken);
- if (mInitialized || (mCaptureSession != null)) {
- notifyClose = true;
- CameraExtensionCharacteristics.releaseSession();
+ if (mToken != null) {
+ if (mInitialized || (mCaptureSession != null)) {
+ notifyClose = true;
+ CameraExtensionCharacteristics.releaseSession();
+ }
+ CameraExtensionCharacteristics.unregisterClient(mContext, mToken);
}
mInitialized = false;
+ mToken = null;
if (mRepeatingRequestImageCallback != null) {
mRepeatingRequestImageCallback.close();
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 21540bf..5e53373 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1193,12 +1193,12 @@
String physicalCameraId = source.readString();
boolean isMultiResolutionOutput = source.readInt() == 1;
int[] sensorPixelModesUsed = source.createIntArray();
- long streamUseCase = source.readLong();
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
long dynamicRangeProfile = source.readLong();
DynamicRangeProfiles.checkProfileValue(dynamicRangeProfile);
int colorSpace = source.readInt();
+ long streamUseCase = source.readLong();
int timestampBase = source.readInt();
int mirrorMode = source.readInt();
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 94bff89..4700720 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -370,8 +370,9 @@
/**
* Returns the default size of the surface associated with the display, or null if the surface
- * is not provided for layer mirroring by SurfaceFlinger.
- * Only used for mirroring started from MediaProjection.
+ * is not provided for layer mirroring by SurfaceFlinger. Size is rotated to reflect the current
+ * display device orientation.
+ * Used for mirroring from MediaProjection, or a physical display based on display flags.
*/
public abstract Point getDisplaySurfaceDefaultSize(int displayId);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 795eb4a..8f653b3 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -576,6 +576,12 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public static final long DISALLOW_INPUT_METHOD_INTERFACE_OVERRIDE = 148086656L;
+ /**
+ * Enable the logic to allow hiding the IME caption bar ("fake" IME navigation bar).
+ * @hide
+ */
+ public static final boolean ENABLE_HIDE_IME_CAPTION_BAR = true;
+
LayoutInflater mInflater;
TypedArray mThemeAttrs;
@UnsupportedAppUsage
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 78388ef..c01664e 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -16,6 +16,8 @@
package android.inputmethodservice;
+import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
+import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import android.animation.ValueAnimator;
@@ -230,6 +232,16 @@
setIconTintInternal(calculateTargetDarkIntensity(mAppearance,
mDrawLegacyNavigationBarBackground));
+
+ if (ENABLE_HIDE_IME_CAPTION_BAR) {
+ mNavigationBarFrame.setOnApplyWindowInsetsListener((view, insets) -> {
+ if (mNavigationBarFrame != null) {
+ boolean visible = insets.isVisible(captionBar());
+ mNavigationBarFrame.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ }
+ return view.onApplyWindowInsets(insets);
+ });
+ }
}
private void uninstallNavigationBarFrameIfNecessary() {
@@ -240,6 +252,9 @@
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(mNavigationBarFrame);
}
+ if (ENABLE_HIDE_IME_CAPTION_BAR) {
+ mNavigationBarFrame.setOnApplyWindowInsetsListener(null);
+ }
mNavigationBarFrame = null;
}
@@ -414,7 +429,9 @@
decor.bringChildToFront(mNavigationBarFrame);
}
}
- mNavigationBarFrame.setVisibility(View.VISIBLE);
+ if (!ENABLE_HIDE_IME_CAPTION_BAR) {
+ mNavigationBarFrame.setVisibility(View.VISIBLE);
+ }
}
}
@@ -435,6 +452,11 @@
mShouldShowImeSwitcherWhenImeIsShown;
mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown;
+ if (ENABLE_HIDE_IME_CAPTION_BAR) {
+ mService.mWindow.getWindow().getDecorView().getWindowInsetsController()
+ .setImeCaptionBarInsetsHeight(getImeCaptionBarHeight());
+ }
+
if (imeDrawsImeNavBar) {
installNavigationBarFrameIfNecessary();
if (mNavigationBarFrame == null) {
@@ -528,6 +550,16 @@
return drawLegacyNavigationBarBackground;
}
+ /**
+ * Returns the height of the IME caption bar if this should be shown, or {@code 0} instead.
+ */
+ private int getImeCaptionBarHeight() {
+ return mImeDrawsImeNavBar
+ ? mService.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_frame_height)
+ : 0;
+ }
+
@Override
public String toDebugString() {
return "{mImeDrawsImeNavBar=" + mImeDrawsImeNavBar
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 5704dac..e4a09a6 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -79,6 +79,13 @@
@WindowState
private int mWindowState = WindowState.TOKEN_PENDING;
+ @Override
+ protected boolean allowsRegisterDefaultOnBackInvokedCallback() {
+ // Do not register OnBackInvokedCallback from Dialog#onStart, InputMethodService will
+ // register CompatOnBackInvokedCallback for input method window.
+ return false;
+ }
+
/**
* Set {@link IBinder} window token to the window.
*
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 42c5626..8482945 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -19,6 +19,7 @@
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -55,6 +56,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.PowerStats;
import com.google.android.collect.Lists;
@@ -1793,75 +1795,55 @@
}
/**
- * Measured energy delta from the previous reading.
+ * An extension to the history item describing a proc state change for a UID.
*/
- public static final class EnergyConsumerDetails {
- /**
- * Description of the energy consumer, such as CPU, DISPLAY etc
- */
- public static final class EnergyConsumer {
- /**
- * See android.hardware.power.stats.EnergyConsumerType
- */
- public int type;
- /**
- * Used when there are multipe energy consumers of the same type, such
- * as CPU clusters, multiple displays on foldable devices etc.
- */
- public int ordinal;
- /**
- * Human-readable name of the energy consumer, e.g. "CPU"
- */
- public String name;
- }
- public EnergyConsumer[] consumers;
- public long[] chargeUC;
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder();
- for (int i = 0; i < consumers.length; i++) {
- if (chargeUC[i] == POWER_DATA_UNAVAILABLE) {
- continue;
- }
- if (sb.length() != 0) {
- sb.append(' ');
- }
- sb.append(consumers[i].name);
- sb.append('=');
- sb.append(chargeUC[i]);
- }
- return sb.toString();
- }
- }
-
- /**
- * CPU usage for a given UID.
- */
- public static final class CpuUsageDetails {
- /**
- * Descriptions of CPU power brackets, see PowerProfile.getCpuPowerBracketDescription
- */
- public String[] cpuBracketDescriptions;
+ public static final class ProcessStateChange {
public int uid;
- /**
- * The delta, in milliseconds, per CPU power bracket, from the previous record for the
- * same UID.
- */
- public long[] cpuUsageMs;
+ public @BatteryConsumer.ProcessState int processState;
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder();
- UserHandle.formatUid(sb, uid);
- sb.append(": ");
- for (int bracket = 0; bracket < cpuUsageMs.length; bracket++) {
- if (bracket != 0) {
- sb.append(", ");
- }
- sb.append(cpuUsageMs[bracket]);
+ private static final int LARGE_UID_FLAG = 0x80000000;
+ private static final int SMALL_UID_MASK = 0x00FFFFFF;
+ private static final int PROC_STATE_MASK = 0x7F000000;
+ private static final int PROC_STATE_SHIFT = Integer.numberOfTrailingZeros(PROC_STATE_MASK);
+
+ /**
+ * Writes this object to the supplied parcel.
+ */
+ public void writeToParcel(Parcel out) {
+ int bits = processState << PROC_STATE_SHIFT;
+ if ((uid & ~SMALL_UID_MASK) == 0) {
+ bits |= uid;
+ out.writeInt(bits);
+ } else {
+ bits |= LARGE_UID_FLAG;
+ out.writeInt(bits);
+ out.writeInt(uid);
}
- return sb.toString();
+ }
+
+ /**
+ * Reads this object from the supplied parcel.
+ */
+ public void readFromParcel(Parcel in) {
+ int bits = in.readInt();
+ processState = (bits & PROC_STATE_MASK) >>> PROC_STATE_SHIFT;
+ if (processState >= BatteryConsumer.PROCESS_STATE_COUNT) {
+ Slog.e(TAG, "Unrecognized proc state in battery history: " + processState);
+ processState = BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
+ }
+ if ((bits & LARGE_UID_FLAG) == 0) {
+ uid = bits & ~PROC_STATE_MASK;
+ } else {
+ uid = in.readInt();
+ }
+ }
+
+ /**
+ * String representation for inclusion in the battery history dump.
+ */
+ public String formatForBatteryHistory() {
+ return UserHandle.formatUid(uid) + ": "
+ + BatteryConsumer.processStateToString(processState);
}
}
@@ -2008,11 +1990,11 @@
// Non-null when there is more detailed information at this step.
public HistoryStepDetails stepDetails;
- // Non-null when there is energy consumer information
- public EnergyConsumerDetails energyConsumerDetails;
+ // Non-null when there are power stats to be written to history
+ public PowerStats powerStats;
- // Non-null when there is CPU usage information
- public CpuUsageDetails cpuUsageDetails;
+ // Non-null when there is procstate change to be written to history
+ public ProcessStateChange processStateChange;
public static final int EVENT_FLAG_START = 0x8000;
public static final int EVENT_FLAG_FINISH = 0x4000;
@@ -2110,6 +2092,7 @@
public final HistoryTag localWakelockTag = new HistoryTag();
public final HistoryTag localWakeReasonTag = new HistoryTag();
public final HistoryTag localEventTag = new HistoryTag();
+ public final ProcessStateChange localProcessStateChange = new ProcessStateChange();
// Includes a tag's first occurrence in the parcel, so the value of the tag is written
// rather than just its index in the history tag pool.
@@ -2222,8 +2205,8 @@
eventCode = EVENT_NONE;
eventTag = null;
tagsFirstOccurrence = false;
- energyConsumerDetails = null;
- cpuUsageDetails = null;
+ powerStats = null;
+ processStateChange = null;
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -2273,8 +2256,8 @@
}
tagsFirstOccurrence = o.tagsFirstOccurrence;
currentTime = o.currentTime;
- energyConsumerDetails = o.energyConsumerDetails;
- cpuUsageDetails = o.cpuUsageDetails;
+ powerStats = o.powerStats;
+ processStateChange = o.processStateChange;
}
public boolean sameNonEvent(HistoryItem o) {
@@ -2434,8 +2417,14 @@
* Returns a BatteryStatsHistoryIterator. Battery history will continue being writable,
* but the iterator will continue iterating over the snapshot taken at the time this method
* is called.
+ *
+ * @param startTimeMs wall-clock time to start iterating from, inclusive
+ * @param endTimeMs wall-clock time to stop iterating, exclusive.
+ * Pass 0 to indicate current time.
*/
- public abstract BatteryStatsHistoryIterator iterateBatteryStatsHistory();
+ public abstract BatteryStatsHistoryIterator iterateBatteryStatsHistory(
+ @CurrentTimeMillisLong long startTimeMs,
+ @CurrentTimeMillisLong long endTimeMs);
/**
* Returns the number of times the device has been started.
@@ -6911,25 +6900,6 @@
private String printNextItem(HistoryItem rec, long baseTime, boolean checkin,
boolean verbose) {
StringBuilder item = new StringBuilder();
-
- if (rec.cpuUsageDetails != null
- && rec.cpuUsageDetails.cpuBracketDescriptions != null
- && checkin) {
- String[] descriptions = rec.cpuUsageDetails.cpuBracketDescriptions;
- for (int bracket = 0; bracket < descriptions.length; bracket++) {
- item.append(BATTERY_STATS_CHECKIN_VERSION);
- item.append(',');
- item.append(HISTORY_DATA);
- item.append(",0,XB,");
- item.append(descriptions.length);
- item.append(',');
- item.append(bracket);
- item.append(',');
- item.append(descriptions[bracket]);
- item.append("\n");
- }
- }
-
if (!checkin) {
item.append(" ");
TimeUtils.formatDuration(
@@ -7165,57 +7135,19 @@
item.append("\"");
}
}
- boolean firstExtension = true;
- if (rec.energyConsumerDetails != null) {
- firstExtension = false;
+ if (rec.powerStats != null && verbose) {
if (!checkin) {
- item.append(" ext=energy:");
- item.append(rec.energyConsumerDetails);
- } else {
- item.append(",XE");
- for (int i = 0; i < rec.energyConsumerDetails.consumers.length; i++) {
- if (rec.energyConsumerDetails.chargeUC[i] != POWER_DATA_UNAVAILABLE) {
- item.append(',');
- item.append(rec.energyConsumerDetails.consumers[i].name);
- item.append('=');
- item.append(rec.energyConsumerDetails.chargeUC[i]);
- }
- }
+ item.append(
+ "\n Stats: ");
+ item.append(rec.powerStats.formatForBatteryHistory(
+ "\n "));
}
}
- if (rec.cpuUsageDetails != null) {
+ if (rec.processStateChange != null && verbose) {
if (!checkin) {
- if (!firstExtension) {
- item.append("\n ");
- }
- String[] descriptions = rec.cpuUsageDetails.cpuBracketDescriptions;
- if (descriptions != null) {
- for (int bracket = 0; bracket < descriptions.length; bracket++) {
- item.append(" ext=cpu-bracket:");
- item.append(bracket);
- item.append(":");
- item.append(descriptions[bracket]);
- item.append("\n ");
- }
- }
- item.append(" ext=cpu:");
- item.append(rec.cpuUsageDetails);
- } else {
- if (!firstExtension) {
- item.append('\n');
- item.append(BATTERY_STATS_CHECKIN_VERSION);
- item.append(',');
- item.append(HISTORY_DATA);
- item.append(",0");
- }
- item.append(",XC,");
- item.append(rec.cpuUsageDetails.uid);
- for (int i = 0; i < rec.cpuUsageDetails.cpuUsageMs.length; i++) {
- item.append(',');
- item.append(rec.cpuUsageDetails.cpuUsageMs[i]);
- }
+ item.append(" procstate: ");
+ item.append(rec.processStateChange.formatForBatteryHistory());
}
- firstExtension = false;
}
item.append("\n");
if (rec.stepDetails != null) {
@@ -7537,7 +7469,7 @@
long baseTime = -1;
boolean printed = false;
HistoryEventTracker tracker = null;
- try (BatteryStatsHistoryIterator iterator = iterateBatteryStatsHistory()) {
+ try (BatteryStatsHistoryIterator iterator = iterateBatteryStatsHistory(0, 0)) {
HistoryItem rec;
while ((rec = iterator.next()) != null) {
try {
@@ -8460,7 +8392,7 @@
long baseTime = -1;
boolean printed = false;
HistoryEventTracker tracker = null;
- try (BatteryStatsHistoryIterator iterator = iterateBatteryStatsHistory()) {
+ try (BatteryStatsHistoryIterator iterator = iterateBatteryStatsHistory(0, 0)) {
HistoryItem rec;
while ((rec = iterator.next()) != null) {
lastTime = rec.time;
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index e2c52ce..7586bf7 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -315,7 +315,7 @@
throw new IllegalStateException(
"Battery history was not requested in the BatteryUsageStatsQuery");
}
- return new BatteryStatsHistoryIterator(mBatteryStatsHistory);
+ return new BatteryStatsHistoryIterator(mBatteryStatsHistory, 0, 0);
}
@Override
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index eb471705..509c3b8 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1560,7 +1560,7 @@
String attestProp = getString(
TextUtils.formatSimple("ro.product.%s_for_attestation", property));
return attestProp.equals(UNKNOWN)
- ? getString(TextUtils.formatSimple("ro.product.vendor.%s", property)) : UNKNOWN;
+ ? getString(TextUtils.formatSimple("ro.product.vendor.%s", property)) : attestProp;
}
private static String[] getStringList(String property, String separator) {
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index 403f55c..cf35460 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -364,7 +364,7 @@
}
/**
- * Checks any blacklists (set in system settings) to see whether a certain
+ * Checks any denylists (set in system settings) to see whether a certain
* tag is allowed. Entries with disabled tags will be dropped immediately,
* so you can save the work of actually constructing and sending the data.
*
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 6ef1dc0..8f7725ec 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -10,6 +10,7 @@
# BatteryStats
per-file *BatteryConsumer* = file:/BATTERY_STATS_OWNERS
per-file BatteryManager* = file:/BATTERY_STATS_OWNERS
+per-file PowerMonitor* = file:/BATTERY_STATS_OWNERS
per-file PowerComponents.java = file:/BATTERY_STATS_OWNERS
per-file *Stats* = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index bf72b1d..2cda787 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -18,26 +18,19 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.hardware.vibrator.IVibrator;
+import android.os.vibrator.VibratorInfoFactory;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.Range;
-import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Executor;
-import java.util.function.Function;
/**
* Vibrator implementation that controls the main system vibrator.
@@ -82,7 +75,7 @@
if (vibratorIds.length == 0) {
// It is known that the device has no vibrator, so cache and return info that
// reflects the lack of support for effects/primitives.
- return mVibratorInfo = new NoVibratorInfo();
+ return mVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO;
}
VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length];
for (int i = 0; i < vibratorIds.length; i++) {
@@ -96,12 +89,7 @@
}
vibratorInfos[i] = vibrator.getInfo();
}
- if (vibratorInfos.length == 1) {
- // Device has a single vibrator info, cache and return successfully loaded info.
- return mVibratorInfo = new VibratorInfo(/* id= */ -1, vibratorInfos[0]);
- }
- // Device has multiple vibrators, generate a single info representing all of them.
- return mVibratorInfo = new MultiVibratorInfo(vibratorInfos);
+ return mVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, vibratorInfos);
}
}
@@ -275,296 +263,6 @@
}
/**
- * Represents a device with no vibrator as a single {@link VibratorInfo}.
- *
- * @hide
- */
- @VisibleForTesting
- public static class NoVibratorInfo extends VibratorInfo {
- public NoVibratorInfo() {
- // Use empty arrays to indicate no support, while null would indicate support unknown.
- super(/* id= */ -1,
- /* capabilities= */ 0,
- /* supportedEffects= */ new SparseBooleanArray(),
- /* supportedBraking= */ new SparseBooleanArray(),
- /* supportedPrimitives= */ new SparseIntArray(),
- /* primitiveDelayMax= */ 0,
- /* compositionSizeMax= */ 0,
- /* pwlePrimitiveDurationMax= */ 0,
- /* pwleSizeMax= */ 0,
- /* qFactor= */ Float.NaN,
- new FrequencyProfile(/* resonantFrequencyHz= */ Float.NaN,
- /* minFrequencyHz= */ Float.NaN,
- /* frequencyResolutionHz= */ Float.NaN,
- /* maxAmplitudes= */ null));
- }
- }
-
- /**
- * Represents multiple vibrator information as a single {@link VibratorInfo}.
- *
- * <p>This uses an intersection of all vibrators to decide the capabilities and effect/primitive
- * support.
- *
- * @hide
- */
- @VisibleForTesting
- public static class MultiVibratorInfo extends VibratorInfo {
- // Epsilon used for float comparison applied in calculations for the merged info.
- private static final float EPSILON = 1e-5f;
-
- public MultiVibratorInfo(VibratorInfo[] vibrators) {
- // Need to use an extra constructor to share the computation in super initialization.
- this(vibrators, frequencyProfileIntersection(vibrators));
- }
-
- private MultiVibratorInfo(VibratorInfo[] vibrators,
- VibratorInfo.FrequencyProfile mergedProfile) {
- super(/* id= */ -1,
- capabilitiesIntersection(vibrators, mergedProfile.isEmpty()),
- supportedEffectsIntersection(vibrators),
- supportedBrakingIntersection(vibrators),
- supportedPrimitivesAndDurationsIntersection(vibrators),
- integerLimitIntersection(vibrators, VibratorInfo::getPrimitiveDelayMax),
- integerLimitIntersection(vibrators, VibratorInfo::getCompositionSizeMax),
- integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax),
- integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax),
- floatPropertyIntersection(vibrators, VibratorInfo::getQFactor),
- mergedProfile);
- }
-
- private static int capabilitiesIntersection(VibratorInfo[] infos,
- boolean frequencyProfileIsEmpty) {
- int intersection = ~0;
- for (VibratorInfo info : infos) {
- intersection &= info.getCapabilities();
- }
- if (frequencyProfileIsEmpty) {
- // Revoke frequency control if the merged frequency profile ended up empty.
- intersection &= ~IVibrator.CAP_FREQUENCY_CONTROL;
- }
- return intersection;
- }
-
- @Nullable
- private static SparseBooleanArray supportedBrakingIntersection(VibratorInfo[] infos) {
- for (VibratorInfo info : infos) {
- if (!info.isBrakingSupportKnown()) {
- // If one vibrator support is unknown, then the intersection is also unknown.
- return null;
- }
- }
-
- SparseBooleanArray intersection = new SparseBooleanArray();
- SparseBooleanArray firstVibratorBraking = infos[0].getSupportedBraking();
-
- brakingIdLoop:
- for (int i = 0; i < firstVibratorBraking.size(); i++) {
- int brakingId = firstVibratorBraking.keyAt(i);
- if (!firstVibratorBraking.valueAt(i)) {
- // The first vibrator already doesn't support this braking, so skip it.
- continue brakingIdLoop;
- }
-
- for (int j = 1; j < infos.length; j++) {
- if (!infos[j].hasBrakingSupport(brakingId)) {
- // One vibrator doesn't support this braking, so the intersection doesn't.
- continue brakingIdLoop;
- }
- }
-
- intersection.put(brakingId, true);
- }
-
- return intersection;
- }
-
- @Nullable
- private static SparseBooleanArray supportedEffectsIntersection(VibratorInfo[] infos) {
- for (VibratorInfo info : infos) {
- if (!info.isEffectSupportKnown()) {
- // If one vibrator support is unknown, then the intersection is also unknown.
- return null;
- }
- }
-
- SparseBooleanArray intersection = new SparseBooleanArray();
- SparseBooleanArray firstVibratorEffects = infos[0].getSupportedEffects();
-
- effectIdLoop:
- for (int i = 0; i < firstVibratorEffects.size(); i++) {
- int effectId = firstVibratorEffects.keyAt(i);
- if (!firstVibratorEffects.valueAt(i)) {
- // The first vibrator already doesn't support this effect, so skip it.
- continue effectIdLoop;
- }
-
- for (int j = 1; j < infos.length; j++) {
- if (infos[j].isEffectSupported(effectId) != VIBRATION_EFFECT_SUPPORT_YES) {
- // One vibrator doesn't support this effect, so the intersection doesn't.
- continue effectIdLoop;
- }
- }
-
- intersection.put(effectId, true);
- }
-
- return intersection;
- }
-
- @NonNull
- private static SparseIntArray supportedPrimitivesAndDurationsIntersection(
- VibratorInfo[] infos) {
- SparseIntArray intersection = new SparseIntArray();
- SparseIntArray firstVibratorPrimitives = infos[0].getSupportedPrimitives();
-
- primitiveIdLoop:
- for (int i = 0; i < firstVibratorPrimitives.size(); i++) {
- int primitiveId = firstVibratorPrimitives.keyAt(i);
- int primitiveDuration = firstVibratorPrimitives.valueAt(i);
- if (primitiveDuration == 0) {
- // The first vibrator already doesn't support this primitive, so skip it.
- continue primitiveIdLoop;
- }
-
- for (int j = 1; j < infos.length; j++) {
- int vibratorPrimitiveDuration = infos[j].getPrimitiveDuration(primitiveId);
- if (vibratorPrimitiveDuration == 0) {
- // One vibrator doesn't support this primitive, so the intersection doesn't.
- continue primitiveIdLoop;
- } else {
- // The primitive vibration duration is the maximum among all vibrators.
- primitiveDuration = Math.max(primitiveDuration, vibratorPrimitiveDuration);
- }
- }
-
- intersection.put(primitiveId, primitiveDuration);
- }
- return intersection;
- }
-
- private static int integerLimitIntersection(VibratorInfo[] infos,
- Function<VibratorInfo, Integer> propertyGetter) {
- int limit = 0; // Limit 0 means unlimited
- for (VibratorInfo info : infos) {
- int vibratorLimit = propertyGetter.apply(info);
- if ((limit == 0) || (vibratorLimit > 0 && vibratorLimit < limit)) {
- // This vibrator is limited and intersection is unlimited or has a larger limit:
- // use smaller limit here for the intersection.
- limit = vibratorLimit;
- }
- }
- return limit;
- }
-
- private static float floatPropertyIntersection(VibratorInfo[] infos,
- Function<VibratorInfo, Float> propertyGetter) {
- float property = propertyGetter.apply(infos[0]);
- if (Float.isNaN(property)) {
- // If one vibrator is undefined then the intersection is undefined.
- return Float.NaN;
- }
- for (int i = 1; i < infos.length; i++) {
- if (Float.compare(property, propertyGetter.apply(infos[i])) != 0) {
- // If one vibrator has a different value then the intersection is undefined.
- return Float.NaN;
- }
- }
- return property;
- }
-
- @NonNull
- private static FrequencyProfile frequencyProfileIntersection(VibratorInfo[] infos) {
- float freqResolution = floatPropertyIntersection(infos,
- info -> info.getFrequencyProfile().getFrequencyResolutionHz());
- float resonantFreq = floatPropertyIntersection(infos,
- VibratorInfo::getResonantFrequencyHz);
- Range<Float> freqRange = frequencyRangeIntersection(infos, freqResolution);
-
- if ((freqRange == null) || Float.isNaN(freqResolution)) {
- return new FrequencyProfile(resonantFreq, Float.NaN, freqResolution, null);
- }
-
- int amplitudeCount =
- Math.round(1 + (freqRange.getUpper() - freqRange.getLower()) / freqResolution);
- float[] maxAmplitudes = new float[amplitudeCount];
-
- // Use MAX_VALUE here to ensure that the FrequencyProfile constructor called with this
- // will fail if the loop below is broken and do not replace filled values with actual
- // vibrator measurements.
- Arrays.fill(maxAmplitudes, Float.MAX_VALUE);
-
- for (VibratorInfo info : infos) {
- Range<Float> vibratorFreqRange = info.getFrequencyProfile().getFrequencyRangeHz();
- float[] vibratorMaxAmplitudes = info.getFrequencyProfile().getMaxAmplitudes();
- int vibratorStartIdx = Math.round(
- (freqRange.getLower() - vibratorFreqRange.getLower()) / freqResolution);
- int vibratorEndIdx = vibratorStartIdx + maxAmplitudes.length - 1;
-
- if ((vibratorStartIdx < 0) || (vibratorEndIdx >= vibratorMaxAmplitudes.length)) {
- Slog.w(TAG, "Error calculating the intersection of vibrator frequency"
- + " profiles: attempted to fetch from vibrator "
- + info.getId() + " max amplitude with bad index " + vibratorStartIdx);
- return new FrequencyProfile(resonantFreq, Float.NaN, Float.NaN, null);
- }
-
- for (int i = 0; i < maxAmplitudes.length; i++) {
- maxAmplitudes[i] = Math.min(maxAmplitudes[i],
- vibratorMaxAmplitudes[vibratorStartIdx + i]);
- }
- }
-
- return new FrequencyProfile(resonantFreq, freqRange.getLower(),
- freqResolution, maxAmplitudes);
- }
-
- @Nullable
- private static Range<Float> frequencyRangeIntersection(VibratorInfo[] infos,
- float frequencyResolution) {
- Range<Float> firstRange = infos[0].getFrequencyProfile().getFrequencyRangeHz();
- if (firstRange == null) {
- // If one vibrator is undefined then the intersection is undefined.
- return null;
- }
- float intersectionLower = firstRange.getLower();
- float intersectionUpper = firstRange.getUpper();
-
- // Generate the intersection of all vibrator supported ranges, making sure that both
- // min supported frequencies are aligned w.r.t. the frequency resolution.
-
- for (int i = 1; i < infos.length; i++) {
- Range<Float> vibratorRange = infos[i].getFrequencyProfile().getFrequencyRangeHz();
- if (vibratorRange == null) {
- // If one vibrator is undefined then the intersection is undefined.
- return null;
- }
-
- if ((vibratorRange.getLower() >= intersectionUpper)
- || (vibratorRange.getUpper() <= intersectionLower)) {
- // If the range and intersection are disjoint then the intersection is undefined
- return null;
- }
-
- float frequencyDelta = Math.abs(intersectionLower - vibratorRange.getLower());
- if ((frequencyDelta % frequencyResolution) > EPSILON) {
- // If the intersection is not aligned with one vibrator then it's undefined
- return null;
- }
-
- intersectionLower = Math.max(intersectionLower, vibratorRange.getLower());
- intersectionUpper = Math.min(intersectionUpper, vibratorRange.getUpper());
- }
-
- if ((intersectionUpper - intersectionLower) < frequencyResolution) {
- // If the intersection is empty then it's undefined.
- return null;
- }
-
- return Range.create(intersectionLower, intersectionUpper);
- }
- }
-
- /**
* Listener for all vibrators state change.
*
* <p>This registers a listener to all vibrators to merge the callbacks into a single state
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index b24b45d..08b32bf 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -525,14 +525,14 @@
public abstract long getDuration();
/**
- * Checks if a given {@link Vibrator} can play this effect as intended.
+ * Checks if a vibrator with a given {@link VibratorInfo} can play this effect as intended.
*
- * <p>See @link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information
- * about what counts as supported by a vibrator, and what counts as not.
+ * <p>See {@link VibratorInfo#areVibrationFeaturesSupported(VibrationEffect)} for more
+ * information about what counts as supported by a vibrator, and what counts as not.
*
* @hide
*/
- public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator);
+ public abstract boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo);
/**
* Returns true if this effect could represent a touch haptic feedback.
@@ -813,9 +813,9 @@
/** @hide */
@Override
- public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+ public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
for (VibrationEffectSegment segment : mSegments) {
- if (!segment.areVibrationFeaturesSupported(vibrator)) {
+ if (!segment.areVibrationFeaturesSupported(vibratorInfo)) {
return false;
}
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 4e852e3..79e0ca8 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -216,9 +216,7 @@
*/
@TestApi
public boolean hasFrequencyControl() {
- // We currently can only control frequency of the vibration using the compose PWLE method.
- return getInfo().hasCapability(
- IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ return getInfo().hasFrequencyControl();
}
/**
@@ -240,7 +238,7 @@
* @hide
*/
public boolean areVibrationFeaturesSupported(@NonNull VibrationEffect effect) {
- return effect.areVibrationFeaturesSupported(this);
+ return getInfo().areVibrationFeaturesSupported(effect);
}
/**
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 02e6856..4f8c24d 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -156,6 +156,16 @@
return false;
}
VibratorInfo that = (VibratorInfo) o;
+ return mId == that.mId && equalContent(that);
+ }
+
+ /**
+ * Returns {@code true} only if the properties and capabilities of the provided info, except for
+ * the ID, equals to this info. Returns {@code false} otherwise.
+ *
+ * @hide
+ */
+ public boolean equalContent(VibratorInfo that) {
int supportedPrimitivesCount = mSupportedPrimitives.size();
if (supportedPrimitivesCount != that.mSupportedPrimitives.size()) {
return false;
@@ -168,7 +178,7 @@
return false;
}
}
- return mId == that.mId && mCapabilities == that.mCapabilities
+ return mCapabilities == that.mCapabilities
&& mPrimitiveDelayMax == that.mPrimitiveDelayMax
&& mCompositionSizeMax == that.mCompositionSizeMax
&& mPwlePrimitiveDurationMax == that.mPwlePrimitiveDurationMax
@@ -242,6 +252,17 @@
}
/**
+ * Check whether the vibrator has frequency control.
+ *
+ * @return True if the hardware can control the frequency of the vibrations, otherwise false.
+ */
+ public boolean hasFrequencyControl() {
+ // We currently can only control frequency of the vibration using the compose PWLE method.
+ return hasCapability(
+ IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ }
+
+ /**
* Returns a default value to be applied to composed PWLE effects for braking.
*
* @return a supported braking value, one of android.hardware.vibrator.Braking.*
@@ -323,6 +344,23 @@
}
/**
+ * Query whether or not the vibrator supports all components of a given {@link VibrationEffect}
+ * (i.e. the vibrator can play the given effect as intended).
+ *
+ * <p>See {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more
+ * information on how the vibrator support is determined.
+ *
+ * @param effect the {@link VibrationEffect} to check if it is supported
+ * @return {@code true} if the vibrator can play the given {@code effect} as intended,
+ * {@code false} otherwise.
+ *
+ * @hide
+ */
+ public boolean areVibrationFeaturesSupported(@NonNull VibrationEffect effect) {
+ return effect.areVibrationFeaturesSupported(this);
+ }
+
+ /**
* Query the estimated duration of given primitive.
*
* @param primitiveId Which primitives to query for.
@@ -417,7 +455,8 @@
return mFrequencyProfile;
}
- protected long getCapabilities() {
+ /** Returns a single int representing all the capabilities of the vibrator. */
+ public long getCapabilities() {
return mCapabilities;
}
diff --git a/core/java/android/os/vibrator/MultiVibratorInfo.java b/core/java/android/os/vibrator/MultiVibratorInfo.java
new file mode 100644
index 0000000..5f32731
--- /dev/null
+++ b/core/java/android/os/vibrator/MultiVibratorInfo.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright 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 android.os.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.vibrator.IVibrator;
+import android.os.Vibrator;
+import android.os.VibratorInfo;
+import android.util.Range;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import java.util.Arrays;
+import java.util.function.Function;
+
+/**
+ * Represents multiple vibrator information as a single {@link VibratorInfo}.
+ *
+ * <p>This uses an intersection of all vibrators to decide the capabilities and effect/primitive
+ * support.
+ *
+ * @hide
+ */
+public final class MultiVibratorInfo extends VibratorInfo {
+ private static final String TAG = "MultiVibratorInfo";
+
+ // Epsilon used for float comparison applied in calculations for the merged info.
+ private static final float EPSILON = 1e-5f;
+
+ public MultiVibratorInfo(int id, VibratorInfo[] vibrators) {
+ this(id, vibrators, frequencyProfileIntersection(vibrators));
+ }
+
+ private MultiVibratorInfo(
+ int id, VibratorInfo[] vibrators, VibratorInfo.FrequencyProfile mergedProfile) {
+ super(id,
+ capabilitiesIntersection(vibrators, mergedProfile.isEmpty()),
+ supportedEffectsIntersection(vibrators),
+ supportedBrakingIntersection(vibrators),
+ supportedPrimitivesAndDurationsIntersection(vibrators),
+ integerLimitIntersection(vibrators, VibratorInfo::getPrimitiveDelayMax),
+ integerLimitIntersection(vibrators, VibratorInfo::getCompositionSizeMax),
+ integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax),
+ integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax),
+ floatPropertyIntersection(vibrators, VibratorInfo::getQFactor),
+ mergedProfile);
+ }
+
+ private static int capabilitiesIntersection(VibratorInfo[] infos,
+ boolean frequencyProfileIsEmpty) {
+ int intersection = ~0;
+ for (VibratorInfo info : infos) {
+ intersection &= info.getCapabilities();
+ }
+ if (frequencyProfileIsEmpty) {
+ // Revoke frequency control if the merged frequency profile ended up empty.
+ intersection &= ~IVibrator.CAP_FREQUENCY_CONTROL;
+ }
+ return intersection;
+ }
+
+ @Nullable
+ private static SparseBooleanArray supportedBrakingIntersection(VibratorInfo[] infos) {
+ for (VibratorInfo info : infos) {
+ if (!info.isBrakingSupportKnown()) {
+ // If one vibrator support is unknown, then the intersection is also unknown.
+ return null;
+ }
+ }
+
+ SparseBooleanArray intersection = new SparseBooleanArray();
+ SparseBooleanArray firstVibratorBraking = infos[0].getSupportedBraking();
+
+ brakingIdLoop:
+ for (int i = 0; i < firstVibratorBraking.size(); i++) {
+ int brakingId = firstVibratorBraking.keyAt(i);
+ if (!firstVibratorBraking.valueAt(i)) {
+ // The first vibrator already doesn't support this braking, so skip it.
+ continue brakingIdLoop;
+ }
+
+ for (int j = 1; j < infos.length; j++) {
+ if (!infos[j].hasBrakingSupport(brakingId)) {
+ // One vibrator doesn't support this braking, so the intersection doesn't.
+ continue brakingIdLoop;
+ }
+ }
+
+ intersection.put(brakingId, true);
+ }
+
+ return intersection;
+ }
+
+ @Nullable
+ private static SparseBooleanArray supportedEffectsIntersection(VibratorInfo[] infos) {
+ for (VibratorInfo info : infos) {
+ if (!info.isEffectSupportKnown()) {
+ // If one vibrator support is unknown, then the intersection is also unknown.
+ return null;
+ }
+ }
+
+ SparseBooleanArray intersection = new SparseBooleanArray();
+ SparseBooleanArray firstVibratorEffects = infos[0].getSupportedEffects();
+
+ effectIdLoop:
+ for (int i = 0; i < firstVibratorEffects.size(); i++) {
+ int effectId = firstVibratorEffects.keyAt(i);
+ if (!firstVibratorEffects.valueAt(i)) {
+ // The first vibrator already doesn't support this effect, so skip it.
+ continue effectIdLoop;
+ }
+
+ for (int j = 1; j < infos.length; j++) {
+ if (infos[j].isEffectSupported(effectId) != Vibrator.VIBRATION_EFFECT_SUPPORT_YES) {
+ // One vibrator doesn't support this effect, so the intersection doesn't.
+ continue effectIdLoop;
+ }
+ }
+
+ intersection.put(effectId, true);
+ }
+
+ return intersection;
+ }
+
+ @NonNull
+ private static SparseIntArray supportedPrimitivesAndDurationsIntersection(
+ VibratorInfo[] infos) {
+ SparseIntArray intersection = new SparseIntArray();
+ SparseIntArray firstVibratorPrimitives = infos[0].getSupportedPrimitives();
+
+ primitiveIdLoop:
+ for (int i = 0; i < firstVibratorPrimitives.size(); i++) {
+ int primitiveId = firstVibratorPrimitives.keyAt(i);
+ int primitiveDuration = firstVibratorPrimitives.valueAt(i);
+ if (primitiveDuration == 0) {
+ // The first vibrator already doesn't support this primitive, so skip it.
+ continue primitiveIdLoop;
+ }
+
+ for (int j = 1; j < infos.length; j++) {
+ int vibratorPrimitiveDuration = infos[j].getPrimitiveDuration(primitiveId);
+ if (vibratorPrimitiveDuration == 0) {
+ // One vibrator doesn't support this primitive, so the intersection doesn't.
+ continue primitiveIdLoop;
+ } else {
+ // The primitive vibration duration is the maximum among all vibrators.
+ primitiveDuration = Math.max(primitiveDuration, vibratorPrimitiveDuration);
+ }
+ }
+
+ intersection.put(primitiveId, primitiveDuration);
+ }
+ return intersection;
+ }
+
+ private static int integerLimitIntersection(VibratorInfo[] infos,
+ Function<VibratorInfo, Integer> propertyGetter) {
+ int limit = 0; // Limit 0 means unlimited
+ for (VibratorInfo info : infos) {
+ int vibratorLimit = propertyGetter.apply(info);
+ if ((limit == 0) || (vibratorLimit > 0 && vibratorLimit < limit)) {
+ // This vibrator is limited and intersection is unlimited or has a larger limit:
+ // use smaller limit here for the intersection.
+ limit = vibratorLimit;
+ }
+ }
+ return limit;
+ }
+
+ private static float floatPropertyIntersection(VibratorInfo[] infos,
+ Function<VibratorInfo, Float> propertyGetter) {
+ float property = propertyGetter.apply(infos[0]);
+ if (Float.isNaN(property)) {
+ // If one vibrator is undefined then the intersection is undefined.
+ return Float.NaN;
+ }
+ for (int i = 1; i < infos.length; i++) {
+ if (Float.compare(property, propertyGetter.apply(infos[i])) != 0) {
+ // If one vibrator has a different value then the intersection is undefined.
+ return Float.NaN;
+ }
+ }
+ return property;
+ }
+
+ @NonNull
+ private static FrequencyProfile frequencyProfileIntersection(VibratorInfo[] infos) {
+ float freqResolution = floatPropertyIntersection(infos,
+ info -> info.getFrequencyProfile().getFrequencyResolutionHz());
+ float resonantFreq = floatPropertyIntersection(infos,
+ VibratorInfo::getResonantFrequencyHz);
+ Range<Float> freqRange = frequencyRangeIntersection(infos, freqResolution);
+
+ if ((freqRange == null) || Float.isNaN(freqResolution)) {
+ return new FrequencyProfile(resonantFreq, Float.NaN, freqResolution, null);
+ }
+
+ int amplitudeCount =
+ Math.round(1 + (freqRange.getUpper() - freqRange.getLower()) / freqResolution);
+ float[] maxAmplitudes = new float[amplitudeCount];
+
+ // Use MAX_VALUE here to ensure that the FrequencyProfile constructor called with this
+ // will fail if the loop below is broken and do not replace filled values with actual
+ // vibrator measurements.
+ Arrays.fill(maxAmplitudes, Float.MAX_VALUE);
+
+ for (VibratorInfo info : infos) {
+ Range<Float> vibratorFreqRange = info.getFrequencyProfile().getFrequencyRangeHz();
+ float[] vibratorMaxAmplitudes = info.getFrequencyProfile().getMaxAmplitudes();
+ int vibratorStartIdx = Math.round(
+ (freqRange.getLower() - vibratorFreqRange.getLower()) / freqResolution);
+ int vibratorEndIdx = vibratorStartIdx + maxAmplitudes.length - 1;
+
+ if ((vibratorStartIdx < 0) || (vibratorEndIdx >= vibratorMaxAmplitudes.length)) {
+ Slog.w(TAG, "Error calculating the intersection of vibrator frequency"
+ + " profiles: attempted to fetch from vibrator "
+ + info.getId() + " max amplitude with bad index " + vibratorStartIdx);
+ return new FrequencyProfile(resonantFreq, Float.NaN, Float.NaN, null);
+ }
+
+ for (int i = 0; i < maxAmplitudes.length; i++) {
+ maxAmplitudes[i] = Math.min(maxAmplitudes[i],
+ vibratorMaxAmplitudes[vibratorStartIdx + i]);
+ }
+ }
+
+ return new FrequencyProfile(resonantFreq, freqRange.getLower(),
+ freqResolution, maxAmplitudes);
+ }
+
+ @Nullable
+ private static Range<Float> frequencyRangeIntersection(VibratorInfo[] infos,
+ float frequencyResolution) {
+ Range<Float> firstRange = infos[0].getFrequencyProfile().getFrequencyRangeHz();
+ if (firstRange == null) {
+ // If one vibrator is undefined then the intersection is undefined.
+ return null;
+ }
+ float intersectionLower = firstRange.getLower();
+ float intersectionUpper = firstRange.getUpper();
+
+ // Generate the intersection of all vibrator supported ranges, making sure that both
+ // min supported frequencies are aligned w.r.t. the frequency resolution.
+
+ for (int i = 1; i < infos.length; i++) {
+ Range<Float> vibratorRange = infos[i].getFrequencyProfile().getFrequencyRangeHz();
+ if (vibratorRange == null) {
+ // If one vibrator is undefined then the intersection is undefined.
+ return null;
+ }
+
+ if ((vibratorRange.getLower() >= intersectionUpper)
+ || (vibratorRange.getUpper() <= intersectionLower)) {
+ // If the range and intersection are disjoint then the intersection is undefined
+ return null;
+ }
+
+ float frequencyDelta = Math.abs(intersectionLower - vibratorRange.getLower());
+ if ((frequencyDelta % frequencyResolution) > EPSILON) {
+ // If the intersection is not aligned with one vibrator then it's undefined
+ return null;
+ }
+
+ intersectionLower = Math.max(intersectionLower, vibratorRange.getLower());
+ intersectionUpper = Math.min(intersectionUpper, vibratorRange.getUpper());
+ }
+
+ if ((intersectionUpper - intersectionLower) < frequencyResolution) {
+ // If the intersection is empty then it's undefined.
+ return null;
+ }
+
+ return Range.create(intersectionLower, intersectionUpper);
+ }
+}
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index 42b6c2da..a035092 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -23,6 +23,7 @@
import android.os.Parcelable;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.os.VibratorInfo;
import java.util.Objects;
@@ -77,8 +78,8 @@
/** @hide */
@Override
- public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
- if (vibrator.areAllEffectsSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) {
+ public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
+ if (vibratorInfo.isEffectSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) {
return true;
}
if (!mFallback) {
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index c52a09c..95d97bf 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -22,7 +22,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
import com.android.internal.util.Preconditions;
@@ -77,8 +77,8 @@
/** @hide */
@Override
- public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
- return vibrator.areAllPrimitivesSupported(mPrimitiveId);
+ public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
+ return vibratorInfo.isPrimitiveSupported(mPrimitiveId);
}
/** @hide */
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index e997bcd..5f9d102 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -20,7 +20,7 @@
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
import com.android.internal.util.Preconditions;
@@ -96,7 +96,7 @@
/** @hide */
@Override
- public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+ public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
boolean areFeaturesSupported = true;
// If the start/end frequencies are not the same, require frequency control since we need to
// ramp up/down the frequency.
@@ -104,7 +104,7 @@
// If there is no frequency ramping, make sure that the one frequency used does not
// require frequency control.
|| frequencyRequiresFrequencyControl(mStartFrequencyHz)) {
- areFeaturesSupported &= vibrator.hasFrequencyControl();
+ areFeaturesSupported &= vibratorInfo.hasFrequencyControl();
}
// If the start/end amplitudes are not the same, require amplitude control since we need to
// ramp up/down the amplitude.
@@ -112,7 +112,7 @@
// If there is no amplitude ramping, make sure that the amplitude used does not
// require amplitude control.
|| amplitudeRequiresAmplitudeControl(mStartAmplitude)) {
- areFeaturesSupported &= vibrator.hasAmplitudeControl();
+ areFeaturesSupported &= vibratorInfo.hasAmplitudeControl();
}
return areFeaturesSupported;
}
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index a585aa8..9576a5b 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -21,7 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
import com.android.internal.util.Preconditions;
@@ -82,13 +82,13 @@
/** @hide */
@Override
- public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
+ public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
boolean areFeaturesSupported = true;
if (frequencyRequiresFrequencyControl(mFrequencyHz)) {
- areFeaturesSupported &= vibrator.hasFrequencyControl();
+ areFeaturesSupported &= vibratorInfo.hasFrequencyControl();
}
if (amplitudeRequiresAmplitudeControl(mAmplitude)) {
- areFeaturesSupported &= vibrator.hasAmplitudeControl();
+ areFeaturesSupported &= vibratorInfo.hasAmplitudeControl();
}
return areFeaturesSupported;
}
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index 3b286a7..17ac36f 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -21,7 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
/**
* Representation of a single segment of a {@link VibrationEffect}.
@@ -65,7 +65,7 @@
*
* @hide
*/
- public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator);
+ public abstract boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo);
/**
* Returns true if this segment could be a haptic feedback effect candidate.
diff --git a/core/java/android/os/vibrator/VibratorInfoFactory.java b/core/java/android/os/vibrator/VibratorInfoFactory.java
new file mode 100644
index 0000000..d10d7ec
--- /dev/null
+++ b/core/java/android/os/vibrator/VibratorInfoFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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 android.os.vibrator;
+
+import android.annotation.NonNull;
+import android.os.VibratorInfo;
+
+/**
+ * Factory for creating {@link VibratorInfo}s.
+ *
+ * @hide
+ */
+public final class VibratorInfoFactory {
+ /**
+ * Creates a single {@link VibratorInfo} that is an intersection of a given collection of
+ * {@link VibratorInfo}s. That is, the capabilities of the returned info will be an
+ * intersection of that of the provided infos.
+ *
+ * @param id the ID for the new {@link VibratorInfo}.
+ * @param vibratorInfos the {@link VibratorInfo}s from which to create a single
+ * {@link VibratorInfo}.
+ * @return a {@link VibratorInfo} that represents the intersection of {@code vibratorInfos}.
+ */
+ @NonNull
+ public static VibratorInfo create(int id, @NonNull VibratorInfo[] vibratorInfos) {
+ if (vibratorInfos.length == 0) {
+ return new VibratorInfo.Builder(id).build();
+ }
+ if (vibratorInfos.length == 1) {
+ // Create an equivalent info with the requested ID.
+ return new VibratorInfo(id, vibratorInfos[0]);
+ }
+ // Create a MultiVibratorInfo that intersects all the given infos and has the requested ID.
+ return new MultiVibratorInfo(id, vibratorInfos);
+ }
+
+ private VibratorInfoFactory() {}
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 50ebab7..2791c41 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1450,7 +1450,7 @@
/**
* Activity Action: Ask the user to allow an app to ignore battery optimizations (that is,
- * put them on the whitelist of apps shown by
+ * put them on the allowlist of apps shown by
* {@link #ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS}). For an app to use this, it also
* must hold the {@link android.Manifest.permission#REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}
* permission.
@@ -3524,7 +3524,6 @@
public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix,
List<String> names) {
String namespace = prefix.substring(0, prefix.length() - 1);
- Config.enforceReadPermission(namespace);
ArrayMap<String, String> keyValues = new ArrayMap<>();
int currentGeneration = -1;
boolean needsGenerationTracker = false;
@@ -10657,6 +10656,14 @@
"assist_long_press_home_enabled";
/**
+ * Whether press and hold on nav handle can trigger search.
+ *
+ * @hide
+ */
+ public static final String SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED =
+ "search_press_hold_nav_handle_enabled";
+
+ /**
* Control whether Trust Agents are in active unlock or extend unlock mode.
* @hide
*/
@@ -12370,7 +12377,7 @@
public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
/**
- * The package name for the custom bugreport handler app. This app must be whitelisted.
+ * The package name for the custom bugreport handler app. This app must be allowlisted.
* This is currently used only by Power Menu short press.
* @deprecated Use {@link android.provider.Settings.Secure#CUSTOM_BUGREPORT_HANDLER_APP}
* instead
@@ -12786,7 +12793,7 @@
"location_background_throttle_proximity_alert_interval_ms";
/**
- * Packages that are whitelisted for background throttling (throttling will not be applied).
+ * Packages that are allowlisted for background throttling (throttling will not be applied).
* @hide
*/
@Readable
@@ -12794,7 +12801,7 @@
"location_background_throttle_package_whitelist";
/**
- * Packages that are whitelisted for ignoring location settings (may retrieve location even
+ * Packages that are allowlisted for ignoring location settings (may retrieve location even
* when user location settings are off), for emergency purposes.
* @deprecated No longer used from Android 12+
* @hide
@@ -13378,7 +13385,7 @@
/**
* List of certificate (hex string representation of the application's certificate - SHA-1
- * or SHA-256) and carrier app package pairs which are whitelisted to prompt the user for
+ * or SHA-256) and carrier app package pairs which are allowlisted to prompt the user for
* install when a sim card with matching UICC carrier privilege rules is inserted. The
* certificate is used as a key, so the certificate encoding here must be the same as the
* certificate encoding used on the SIM.
@@ -16779,7 +16786,7 @@
"enable_adb_incremental_install_default";
/**
- * The packages whitelisted to be run in autofill compatibility mode. The list
+ * The packages allowlisted to be run in autofill compatibility mode. The list
* of packages is {@code ":"} colon delimited, and each entry has the name of the
* package and an optional list of url bar resource ids (the list is delimited by
* brackets&mdash{@code [} and {@code ]}&mdash and is also comma delimited).
@@ -16839,7 +16846,7 @@
public static final String STYLUS_EVER_USED = "stylus_ever_used";
/**
- * Exemptions to the hidden API blacklist.
+ * Exemptions to the hidden API denylist.
*
* @hide
*/
@@ -19640,21 +19647,6 @@
.getApplicationContext().checkCallingOrSelfPermission(permission);
}
- /**
- * Enforces READ_DEVICE_CONFIG permission if namespace is not one of public namespaces.
- * @hide
- */
- public static void enforceReadPermission(String namespace) {
- if (ActivityThread.currentApplication().getApplicationContext()
- .checkCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG)
- != PackageManager.PERMISSION_GRANTED) {
- if (!DeviceConfig.getPublicNamespaces().contains(namespace)) {
- throw new SecurityException("Permission denial: reading from settings requires:"
- + Manifest.permission.READ_DEVICE_CONFIG);
- }
- }
- }
-
private static void setMonitorCallbackAsUser(
@NonNull @CallbackExecutor Executor executor,
@NonNull ContentResolver resolver, @UserIdInt int userHandle,
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 75640bd..f3b4c6d 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -92,6 +92,7 @@
mapParcel.recycle();
if (buffer != null) {
mRankingMapFd.unmap(buffer);
+ mRankingMapFd.close();
}
}
} else {
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 910fc44..c027981 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -64,6 +64,7 @@
private IBinder mToken;
private Icon mIcon;
private CharSequence mLabel;
+ private CharSequence mDefaultLabel;
private CharSequence mSubtitle;
private CharSequence mContentDescription;
private CharSequence mStateDescription;
@@ -142,10 +143,25 @@
* Gets the current label for the tile.
*/
public CharSequence getLabel() {
+ return mLabel != null ? mLabel : mDefaultLabel;
+ }
+
+ /**
+ * @hide
+ * @return
+ */
+ public CharSequence getCustomLabel() {
return mLabel;
}
/**
+ * @hide
+ */
+ public void setDefaultLabel(CharSequence defaultLabel) {
+ mDefaultLabel = defaultLabel;
+ }
+
+ /**
* Sets the current label for the tile.
*
* Does not take effect until {@link #updateTile()} is called.
@@ -267,6 +283,7 @@
}
dest.writeInt(mState);
TextUtils.writeToParcel(mLabel, dest, flags);
+ TextUtils.writeToParcel(mDefaultLabel, dest, flags);
TextUtils.writeToParcel(mSubtitle, dest, flags);
TextUtils.writeToParcel(mContentDescription, dest, flags);
TextUtils.writeToParcel(mStateDescription, dest, flags);
@@ -285,6 +302,7 @@
}
mState = source.readInt();
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ mDefaultLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mStateDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 21f676e..94d8516 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -67,6 +67,7 @@
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractionSoundTriggerSession;
+import com.android.internal.infra.AndroidFuture;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -1710,6 +1711,11 @@
Slog.i(TAG, "onProcessRestarted");
mHandler.sendEmptyMessage(MSG_PROCESS_RESTARTED);
}
+
+ @Override
+ public void onOpenFile(String filename, AndroidFuture future) throws RemoteException {
+ throw new UnsupportedOperationException("Hotword cannot access files from the disk.");
+ }
}
void onDetectorRemoteException() {
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index d9ee859..ccf8b67 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -227,6 +227,12 @@
public void stopDetection() {
HotwordDetectionService.this.onStopDetection();
}
+
+ @Override
+ public void registerRemoteStorageService(IDetectorSessionStorageService
+ detectorSessionStorageService) {
+ throw new UnsupportedOperationException("Hotword cannot access files from the disk.");
+ }
};
@Override
diff --git a/core/java/android/service/voice/IDetectorSessionStorageService.aidl b/core/java/android/service/voice/IDetectorSessionStorageService.aidl
new file mode 100644
index 0000000..592373e
--- /dev/null
+++ b/core/java/android/service/voice/IDetectorSessionStorageService.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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 android.service.voice;
+
+import com.android.internal.infra.AndroidFuture;
+
+/**
+ * @hide
+ */
+oneway interface IDetectorSessionStorageService {
+ /**
+ * Called when a file open request is sent. Only files with the given names under the internal
+ * app storage, i.e., {@link Context#getFilesDir()} can be opened.
+ */
+ void openFile(in String filename, in AndroidFuture future);
+}
diff --git a/core/java/android/service/voice/ISandboxedDetectionService.aidl b/core/java/android/service/voice/ISandboxedDetectionService.aidl
index 098536d..c76ac28 100644
--- a/core/java/android/service/voice/ISandboxedDetectionService.aidl
+++ b/core/java/android/service/voice/ISandboxedDetectionService.aidl
@@ -24,6 +24,7 @@
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.SharedMemory;
+import android.service.voice.IDetectorSessionStorageService;
import android.service.voice.IDetectorSessionVisualQueryDetectionCallback;
import android.service.voice.IDspHotwordDetectionCallback;
import android.view.contentcapture.IContentCaptureManager;
@@ -71,4 +72,10 @@
void ping(in IRemoteCallback callback);
void stopDetection();
+
+ /**
+ * Registers the interface stub to talk to the voice interaction service for initialization/
+ * detection unrelated functionalities.
+ */
+ void registerRemoteStorageService(in IDetectorSessionStorageService detectorSessionStorageService);
}
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index 128bc0d..f1bc792 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -36,6 +36,7 @@
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.infra.AndroidFuture;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -302,6 +303,11 @@
Binder.withCleanCallingIdentity(() -> mExecutor.execute(
() -> mCallback.onHotwordDetectionServiceRestarted()));
}
+
+ @Override
+ public void onOpenFile(String filename, AndroidFuture future) throws RemoteException {
+ throw new UnsupportedOperationException("Hotword cannot access files from the disk.");
+ }
}
/** @hide */
diff --git a/core/java/android/service/voice/VisualQueryDetectionService.java b/core/java/android/service/voice/VisualQueryDetectionService.java
index cbe7666..d184b1e 100644
--- a/core/java/android/service/voice/VisualQueryDetectionService.java
+++ b/core/java/android/service/voice/VisualQueryDetectionService.java
@@ -40,7 +40,12 @@
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.IContentCaptureManager;
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.util.Objects;
+import java.util.concurrent.ExecutionException;
import java.util.function.IntConsumer;
/**
@@ -86,6 +91,8 @@
private ContentCaptureManager mContentCaptureManager;
@Nullable
private IRecognitionServiceManager mIRecognitionServiceManager;
+ @Nullable
+ private IDetectorSessionStorageService mDetectorSessionStorageService;
private final ISandboxedDetectionService mInterface = new ISandboxedDetectionService.Stub() {
@@ -154,6 +161,12 @@
public void updateRecognitionServiceManager(IRecognitionServiceManager manager) {
mIRecognitionServiceManager = manager;
}
+
+ @Override
+ public void registerRemoteStorageService(IDetectorSessionStorageService
+ detectorSessionStorageService) {
+ mDetectorSessionStorageService = detectorSessionStorageService;
+ }
};
@Override
@@ -323,4 +336,23 @@
}
}
+ /**
+ * Overrides {@link Context#openFileInput} to read files with the given file names under the
+ * internal app storage of the {@link VoiceInteractionService}, i.e., only files stored in
+ * {@link Context#getFilesDir()} can be opened.
+ */
+ @Override
+ public @Nullable FileInputStream openFileInput(@NonNull String filename) throws
+ FileNotFoundException {
+ try {
+ AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+ mDetectorSessionStorageService.openFile(filename, future);
+ ParcelFileDescriptor pfd = future.get();
+ return new FileInputStream(pfd.getFileDescriptor());
+ } catch (RemoteException | ExecutionException | InterruptedException e) {
+ Log.w(TAG, "Cannot open file due to remote service failure");
+ throw new FileNotFoundException(e.getMessage());
+ }
+ }
+
}
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index 9e0eb4b..b5448d4 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -25,6 +25,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.content.Context;
import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioFormat;
import android.os.Binder;
@@ -37,7 +38,10 @@
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.infra.AndroidFuture;
+import java.io.File;
+import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -58,18 +62,20 @@
private final Callback mCallback;
private final Executor mExecutor;
+ private final Context mContext;
private final IVoiceInteractionManagerService mManagerService;
private final VisualQueryDetectorInitializationDelegate mInitializationDelegate;
private final String mAttributionTag;
VisualQueryDetector(
IVoiceInteractionManagerService managerService,
- @NonNull @CallbackExecutor Executor executor,
- Callback callback, @Nullable String attributionTag) {
+ @NonNull @CallbackExecutor Executor executor, Callback callback, Context context,
+ @Nullable String attributionTag) {
mManagerService = managerService;
mCallback = callback;
mExecutor = executor;
mInitializationDelegate = new VisualQueryDetectorInitializationDelegate();
+ mContext = context;
mAttributionTag = attributionTag;
}
@@ -247,7 +253,7 @@
@Override
void initialize(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory) {
initAndVerifyDetector(options, sharedMemory,
- new InitializationStateListener(mExecutor, mCallback),
+ new InitializationStateListener(mExecutor, mCallback, mContext),
DETECTOR_TYPE_VISUAL_QUERY_DETECTOR, mAttributionTag);
}
@@ -332,9 +338,12 @@
private final Executor mExecutor;
private final Callback mCallback;
- InitializationStateListener(Executor executor, Callback callback) {
+ private final Context mContext;
+
+ InitializationStateListener(Executor executor, Callback callback, Context context) {
this.mExecutor = executor;
this.mCallback = callback;
+ this.mContext = context;
}
@Override
@@ -428,5 +437,22 @@
!TextUtils.isEmpty(errorMessage) ? errorMessage : "Error data is null");
}));
}
+ @Override
+ public void onOpenFile(String filename, AndroidFuture future) throws RemoteException {
+ Slog.v(TAG, "BinderCallback#onOpenFile " + filename);
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
+ Slog.v(TAG, "onOpenFile: " + filename);
+ File f = new File(mContext.getFilesDir(), filename);
+ ParcelFileDescriptor pfd = null;
+ try {
+ Slog.d(TAG, "opened a file with ParcelFileDescriptor.");
+ pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "Cannot open file. No ParcelFileDescriptor returned.");
+ } finally {
+ future.complete(pfd);
+ }
+ }));
+ }
}
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 8cec17f..b48b7ec 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -965,7 +965,7 @@
}
VisualQueryDetector visualQueryDetector =
- new VisualQueryDetector(mSystemService, executor, callback,
+ new VisualQueryDetector(mSystemService, executor, callback, this,
getAttributionTag());
HotwordDetector visualQueryDetectorInitializationDelegate =
visualQueryDetector.getInitializationDelegate();
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 9656f36..7f313c1 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -38,6 +38,7 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.pooled.PooledLambda;
import java.lang.ref.WeakReference;
@@ -232,39 +233,68 @@
intent,
attributionSource,
new ModelDownloadListener() {
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private boolean mIsTerminated = false;
+
@Override
public void onProgress(int completedPercent) {
- try {
- listener.onProgress(completedPercent);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (mLock) {
+ if (mIsTerminated) {
+ return;
+ }
+ try {
+ listener.onProgress(completedPercent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
@Override
public void onSuccess() {
- try {
- listener.onSuccess();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (mLock) {
+ if (mIsTerminated) {
+ return;
+ }
+ mIsTerminated = true;
+ try {
+ listener.onSuccess();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
@Override
public void onScheduled() {
- try {
- listener.onScheduled();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (mLock) {
+ if (mIsTerminated) {
+ return;
+ }
+ mIsTerminated = true;
+ try {
+ listener.onScheduled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
@Override
public void onError(int error) {
- try {
- listener.onError(error);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (mLock) {
+ if (mIsTerminated) {
+ return;
+ }
+ mIsTerminated = true;
+ try {
+ listener.onError(error);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
});
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 751cd21..6e73a3c 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -19,7 +19,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
@@ -78,11 +81,17 @@
private int mConnectionCount = 0;
private final InputMethodManager mImm;
+ private final RectF mTempRectF = new RectF();
+
+ private final Region mTempRegion = new Region();
+
+ private final Matrix mTempMatrix = new Matrix();
+
/**
* The handwrite-able View that is currently the target of a hovering stylus pointer. This is
* used to help determine whether the handwriting PointerIcon should be shown in
* {@link #onResolvePointerIcon(Context, MotionEvent)} so that we can reduce the number of calls
- * to {@link #findBestCandidateView(float, float)}.
+ * to {@link #findBestCandidateView(float, float, boolean)}.
*/
@Nullable
private WeakReference<View> mCachedHoverTarget = null;
@@ -184,8 +193,8 @@
final float y = motionEvent.getY(pointerIndex);
if (largerThanTouchSlop(x, y, mState.mStylusDownX, mState.mStylusDownY)) {
mState.mExceedHandwritingSlop = true;
- View candidateView =
- findBestCandidateView(mState.mStylusDownX, mState.mStylusDownY);
+ View candidateView = findBestCandidateView(mState.mStylusDownX,
+ mState.mStylusDownY, /* isHover */ false);
if (candidateView != null) {
if (candidateView == getConnectedView()) {
if (!candidateView.hasFocus()) {
@@ -393,13 +402,14 @@
final View cachedHoverTarget = getCachedHoverTarget();
if (cachedHoverTarget != null) {
final Rect handwritingArea = getViewHandwritingArea(cachedHoverTarget);
- if (isInHandwritingArea(handwritingArea, hoverX, hoverY, cachedHoverTarget)
+ if (isInHandwritingArea(handwritingArea, hoverX, hoverY, cachedHoverTarget,
+ /* isHover */ true)
&& shouldTriggerStylusHandwritingForView(cachedHoverTarget)) {
return cachedHoverTarget;
}
}
- final View candidateView = findBestCandidateView(hoverX, hoverY);
+ final View candidateView = findBestCandidateView(hoverX, hoverY, /* isHover */ true);
if (candidateView != null) {
mCachedHoverTarget = new WeakReference<>(candidateView);
@@ -429,14 +439,14 @@
* @param y the y coordinates of the stylus event, in the coordinates of the window.
*/
@Nullable
- private View findBestCandidateView(float x, float y) {
+ private View findBestCandidateView(float x, float y, boolean isHover) {
// If the connectedView is not null and do not set any handwriting area, it will check
// whether the connectedView's boundary contains the initial stylus position. If true,
// directly return the connectedView.
final View connectedView = getConnectedView();
if (connectedView != null) {
Rect handwritingArea = getViewHandwritingArea(connectedView);
- if (isInHandwritingArea(handwritingArea, x, y, connectedView)
+ if (isInHandwritingArea(handwritingArea, x, y, connectedView, isHover)
&& shouldTriggerStylusHandwritingForView(connectedView)) {
return connectedView;
}
@@ -450,7 +460,7 @@
for (HandwritableViewInfo viewInfo : handwritableViewInfos) {
final View view = viewInfo.getView();
final Rect handwritingArea = viewInfo.getHandwritingArea();
- if (!isInHandwritingArea(handwritingArea, x, y, view)
+ if (!isInHandwritingArea(handwritingArea, x, y, view, isHover)
|| !shouldTriggerStylusHandwritingForView(view)) {
continue;
}
@@ -546,15 +556,48 @@
* Return true if the (x, y) is inside by the given {@link Rect} with the View's
* handwriting bounds with offsets applied.
*/
- private static boolean isInHandwritingArea(@Nullable Rect handwritingArea,
- float x, float y, View view) {
+ private boolean isInHandwritingArea(@Nullable Rect handwritingArea,
+ float x, float y, View view, boolean isHover) {
if (handwritingArea == null) return false;
- return contains(handwritingArea, x, y,
+ if (!contains(handwritingArea, x, y,
view.getHandwritingBoundsOffsetLeft(),
view.getHandwritingBoundsOffsetTop(),
view.getHandwritingBoundsOffsetRight(),
- view.getHandwritingBoundsOffsetBottom());
+ view.getHandwritingBoundsOffsetBottom())) {
+ return false;
+ }
+
+ // The returned handwritingArea computed by ViewParent#getChildVisibleRect didn't consider
+ // the case where a view is stacking on top of the editor. (e.g. DrawerLayout, popup)
+ // We must check the hit region of the editor again, and avoid the case where another
+ // view on top of the editor is handling MotionEvents.
+ ViewParent parent = view.getParent();
+ if (parent == null) {
+ return true;
+ }
+
+ Region region = mTempRegion;
+ mTempRegion.set(0, 0, view.getWidth(), view.getHeight());
+ Matrix matrix = mTempMatrix;
+ matrix.reset();
+ if (!parent.getChildLocalHitRegion(view, region, matrix, isHover)) {
+ return false;
+ }
+
+ // It's not easy to extend the region by the given handwritingBoundsOffset. Instead, we
+ // create a rectangle surrounding the motion event location and check if this rectangle
+ // overlaps with the hit region of the editor.
+ float left = x - view.getHandwritingBoundsOffsetRight();
+ float top = y - view.getHandwritingBoundsOffsetBottom();
+ float right = Math.max(x + view.getHandwritingBoundsOffsetLeft(), left + 1);
+ float bottom = Math.max(y + view.getHandwritingBoundsOffsetTop(), top + 1);
+ RectF rectF = mTempRectF;
+ rectF.set(left, top, right, bottom);
+ matrix.mapRect(rectF);
+
+ return region.op(Math.round(rectF.left), Math.round(rectF.top),
+ Math.round(rectF.right), Math.round(rectF.bottom), Region.Op.INTERSECT);
}
/**
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
index c7fd380..37663d5 100644
--- a/core/java/android/view/IRecentsAnimationRunner.aidl
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -21,6 +21,7 @@
import android.view.RemoteAnimationTarget;
import android.view.IRecentsAnimationController;
import android.window.TaskSnapshot;
+import android.os.Bundle;
/**
* Interface that is used to callback from window manager to the process that runs a recents
@@ -57,7 +58,7 @@
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void onAnimationStart(in IRecentsAnimationController controller,
in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
- in Rect homeContentInsets, in Rect minimizedHomeBounds) = 2;
+ in Rect homeContentInsets, in Rect minimizedHomeBounds, in Bundle extras) = 2;
/**
* Called when the task of an activity that has been started while the recents animation
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 3812d37..2761aae 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Matrix;
+import android.graphics.Rect;
import android.graphics.Region;
import android.gui.TouchOcclusionMode;
import android.os.IBinder;
@@ -103,10 +104,7 @@
public long dispatchingTimeoutMillis;
// Window frame.
- public int frameLeft;
- public int frameTop;
- public int frameRight;
- public int frameBottom;
+ public final Rect frame = new Rect();
public int surfaceInset;
@@ -184,10 +182,7 @@
layoutParamsFlags = other.layoutParamsFlags;
layoutParamsType = other.layoutParamsType;
dispatchingTimeoutMillis = other.dispatchingTimeoutMillis;
- frameLeft = other.frameLeft;
- frameTop = other.frameTop;
- frameRight = other.frameRight;
- frameBottom = other.frameBottom;
+ frame.set(other.frame);
surfaceInset = other.surfaceInset;
scaleFactor = other.scaleFactor;
touchableRegion.set(other.touchableRegion);
@@ -209,8 +204,7 @@
@Override
public String toString() {
return new StringBuilder(name != null ? name : "")
- .append(", frame=[").append(frameLeft).append(",").append(frameTop).append(",")
- .append(frameRight).append(",").append(frameBottom).append("]")
+ .append(", frame=[").append(frame).append("]")
.append(", touchableRegion=").append(touchableRegion)
.append(", scaleFactor=").append(scaleFactor)
.append(", transform=").append(transform)
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 6c5f195..fabfed3 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -28,7 +28,6 @@
import static android.view.InsetsAnimationControlImplProto.PENDING_INSETS;
import static android.view.InsetsAnimationControlImplProto.SHOWN_ON_FINISH;
import static android.view.InsetsAnimationControlImplProto.TMP_MATRIX;
-import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsController.DEBUG;
import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
@@ -285,15 +284,11 @@
return false;
}
final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
- ArrayList<SurfaceParams> params = new ArrayList<>();
- updateLeashesForSide(ISIDE_LEFT, offset.left, mPendingInsets.left, params, outState,
- mPendingAlpha);
- updateLeashesForSide(ISIDE_TOP, offset.top, mPendingInsets.top, params, outState,
- mPendingAlpha);
- updateLeashesForSide(ISIDE_RIGHT, offset.right, mPendingInsets.right, params, outState,
- mPendingAlpha);
- updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params, outState,
- mPendingAlpha);
+ final ArrayList<SurfaceParams> params = new ArrayList<>();
+ updateLeashesForSide(ISIDE_LEFT, offset.left, params, outState, mPendingAlpha);
+ updateLeashesForSide(ISIDE_TOP, offset.top, params, outState, mPendingAlpha);
+ updateLeashesForSide(ISIDE_RIGHT, offset.right, params, outState, mPendingAlpha);
+ updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, params, outState, mPendingAlpha);
mController.applySurfaceParams(params.toArray(new SurfaceParams[params.size()]));
mCurrentInsets = mPendingInsets;
@@ -457,7 +452,7 @@
return alpha >= 1 ? 1 : (alpha <= 0 ? 0 : alpha);
}
- private void updateLeashesForSide(@InternalInsetsSide int side, int offset, int inset,
+ private void updateLeashesForSide(@InternalInsetsSide int side, int offset,
ArrayList<SurfaceParams> surfaceParams, @Nullable InsetsState outState, float alpha) {
final ArraySet<InsetsSourceControl> controls = mSideControlsMap.get(side);
if (controls == null) {
@@ -475,9 +470,9 @@
}
addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame);
- final boolean visible = mHasZeroInsetsIme && side == ISIDE_BOTTOM
- ? (mAnimationType == ANIMATION_TYPE_SHOW || !mFinished)
- : inset != 0;
+ final boolean visible = mPendingFraction == 0 && source != null
+ ? source.isVisible()
+ : !mFinished || mShownOnFinish;
if (outState != null && source != null) {
outState.addSource(new InsetsSource(source)
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 4ecfc40..8ec7d67 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -16,10 +16,12 @@
package android.view;
+import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
import static android.os.Trace.TRACE_TAG_VIEW;
import static android.view.InsetsControllerProto.CONTROL;
import static android.view.InsetsControllerProto.STATE;
import static android.view.InsetsSource.ID_IME;
+import static android.view.InsetsSource.ID_IME_CAPTION_BAR;
import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
import static android.view.WindowInsets.Type.FIRST;
import static android.view.WindowInsets.Type.LAST;
@@ -40,6 +42,7 @@
import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.graphics.Insets;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.CancellationSignal;
import android.os.Handler;
@@ -652,6 +655,7 @@
private int mLastWindowingMode;
private boolean mStartingAnimation;
private int mCaptionInsetsHeight = 0;
+ private int mImeCaptionBarInsetsHeight = 0;
private boolean mAnimationsDisabled;
private boolean mCompatSysUiVisibilityStaled;
@@ -662,9 +666,6 @@
/** Set of inset types for which an animation was started since last resetting this field */
private @InsetsType int mLastStartedAnimTypes;
- /** Set of inset types which cannot be controlled by the user animation */
- private @InsetsType int mDisabledUserAnimationInsetsTypes;
-
/** Set of inset types which are existing */
private @InsetsType int mExistingTypes = 0;
@@ -693,6 +694,9 @@
if (!CAPTION_ON_SHELL && source1.getType() == captionBar()) {
return;
}
+ if (source1.getId() == ID_IME_CAPTION_BAR) {
+ return;
+ }
// Don't change the indexes of the sources while traversing. Remove it later.
mPendingRemoveIndexes.add(index1);
@@ -823,6 +827,9 @@
if (mFrame.equals(frame)) {
return;
}
+ if (mImeCaptionBarInsetsHeight != 0) {
+ setImeCaptionBarInsetsHeight(mImeCaptionBarInsetsHeight);
+ }
mHost.notifyInsetsChanged();
mFrame.set(frame);
}
@@ -877,21 +884,11 @@
mState.set(newState, 0 /* types */);
@InsetsType int existingTypes = 0;
@InsetsType int visibleTypes = 0;
- @InsetsType int disabledUserAnimationTypes = 0;
@InsetsType int[] cancelledUserAnimationTypes = {0};
for (int i = 0, size = newState.sourceSize(); i < size; i++) {
final InsetsSource source = newState.sourceAt(i);
@InsetsType int type = source.getType();
@AnimationType int animationType = getAnimationType(type);
- if (!source.isUserControllable()) {
- // The user animation is not allowed when visible frame is empty.
- disabledUserAnimationTypes |= type;
- if (animationType == ANIMATION_TYPE_USER) {
- // Existing user animation needs to be cancelled.
- animationType = ANIMATION_TYPE_NONE;
- cancelledUserAnimationTypes[0] |= type;
- }
- }
final InsetsSourceConsumer consumer = mSourceConsumers.get(source.getId());
if (consumer != null) {
consumer.updateSource(source, animationType);
@@ -921,28 +918,11 @@
}
InsetsState.traverse(mState, newState, mRemoveGoneSources);
- updateDisabledUserAnimationTypes(disabledUserAnimationTypes);
-
if (cancelledUserAnimationTypes[0] != 0) {
mHandler.post(() -> show(cancelledUserAnimationTypes[0]));
}
}
- private void updateDisabledUserAnimationTypes(@InsetsType int disabledUserAnimationTypes) {
- @InsetsType int diff = mDisabledUserAnimationInsetsTypes ^ disabledUserAnimationTypes;
- if (diff != 0) {
- for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
- InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
- if (consumer.getControl() != null && (consumer.getType() & diff) != 0) {
- mHandler.removeCallbacks(mInvokeControllableInsetsChangedListeners);
- mHandler.post(mInvokeControllableInsetsChangedListeners);
- break;
- }
- }
- mDisabledUserAnimationInsetsTypes = disabledUserAnimationTypes;
- }
- }
-
private boolean captionInsetsUnchanged() {
if (CAPTION_ON_SHELL) {
return false;
@@ -1007,6 +987,12 @@
// Ensure to update all existing source consumers
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+ if (consumer.getId() == ID_IME_CAPTION_BAR) {
+ // The inset control for the IME caption bar will never be dispatched
+ // by the server.
+ continue;
+ }
+
final InsetsSourceControl control = mTmpControlArray.get(consumer.getId());
if (control != null) {
controllableTypes |= control.getType();
@@ -1316,26 +1302,6 @@
+ " while an existing " + Type.toString(mTypesBeingCancelled)
+ " is being cancelled.");
}
- if (animationType == ANIMATION_TYPE_USER) {
- final @InsetsType int disabledTypes = types & mDisabledUserAnimationInsetsTypes;
- if (DEBUG) Log.d(TAG, "user animation disabled types: " + disabledTypes);
- types &= ~mDisabledUserAnimationInsetsTypes;
-
- if ((disabledTypes & ime()) != 0) {
- ImeTracker.forLogging().onFailed(statsToken,
- ImeTracker.PHASE_CLIENT_DISABLED_USER_ANIMATION);
-
- if (fromIme
- && !mState.isSourceOrDefaultVisible(mImeSourceConsumer.getId(), ime())) {
- // We've requested IMM to show IME, but the IME is not controllable. We need to
- // cancel the request.
- setRequestedVisibleTypes(0 /* visibleTypes */, ime());
- if (mImeSourceConsumer.onAnimationStateChanged(false /* running */)) {
- notifyVisibilityChanged();
- }
- }
- }
- }
if (types == 0) {
// nothing to animate.
listener.onCancelled(null);
@@ -1499,7 +1465,8 @@
continue;
}
final InsetsSourceControl control = consumer.getControl();
- if (control != null && control.getLeash() != null) {
+ if (control != null
+ && (control.getLeash() != null || control.getId() == ID_IME_CAPTION_BAR)) {
controls.put(control.getId(), new InsetsSourceControl(control));
typesReady |= consumer.getType();
}
@@ -1885,6 +1852,35 @@
}
@Override
+ public void setImeCaptionBarInsetsHeight(int height) {
+ if (!ENABLE_HIDE_IME_CAPTION_BAR) {
+ return;
+ }
+ Rect newFrame = new Rect(mFrame.left, mFrame.bottom - height, mFrame.right, mFrame.bottom);
+ InsetsSource source = mState.peekSource(ID_IME_CAPTION_BAR);
+ if (mImeCaptionBarInsetsHeight != height
+ || (source != null && !newFrame.equals(source.getFrame()))) {
+ mImeCaptionBarInsetsHeight = height;
+ if (mImeCaptionBarInsetsHeight != 0) {
+ mState.getOrCreateSource(ID_IME_CAPTION_BAR, captionBar())
+ .setFrame(newFrame);
+ getSourceConsumer(ID_IME_CAPTION_BAR, captionBar()).setControl(
+ new InsetsSourceControl(ID_IME_CAPTION_BAR, captionBar(),
+ null /* leash */, false /* initialVisible */,
+ new Point(), Insets.NONE),
+ new int[1], new int[1]);
+ } else {
+ mState.removeSource(ID_IME_CAPTION_BAR);
+ InsetsSourceConsumer sourceConsumer = mSourceConsumers.get(ID_IME_CAPTION_BAR);
+ if (sourceConsumer != null) {
+ sourceConsumer.setControl(null, new int[1], new int[1]);
+ }
+ }
+ mHost.notifyInsetsChanged();
+ }
+ }
+
+ @Override
public void setSystemBarsBehavior(@Behavior int behavior) {
mHost.setSystemBarsBehavior(behavior);
}
@@ -1908,7 +1904,7 @@
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
InsetsSource source = mState.peekSource(consumer.getId());
- if (consumer.getControl() != null && source != null && source.isUserControllable()) {
+ if (consumer.getControl() != null && source != null) {
result |= consumer.getType();
}
}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 6441186..0d5704e 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -20,6 +20,7 @@
import static android.view.InsetsSourceProto.TYPE;
import static android.view.InsetsSourceProto.VISIBLE;
import static android.view.InsetsSourceProto.VISIBLE_FRAME;
+import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.ime;
import android.annotation.IntDef;
@@ -47,6 +48,9 @@
/** The insets source ID of IME */
public static final int ID_IME = createId(null, 0, ime());
+ /** The insets source ID of the IME caption bar ("fake" IME navigation bar). */
+ static final int ID_IME_CAPTION_BAR =
+ InsetsSource.createId(null /* owner */, 1 /* index */, captionBar());
/**
* Controls whether this source suppresses the scrim. If the scrim is ignored, the system won't
@@ -183,11 +187,6 @@
return (mFlags & flags) == flags;
}
- boolean isUserControllable() {
- // If mVisibleFrame is null, it will be the same area as mFrame.
- return mVisibleFrame == null || !mVisibleFrame.isEmpty();
- }
-
/**
* Calculates the insets this source will cause to a client window.
*
@@ -215,8 +214,12 @@
// During drag-move and drag-resizing, the caption insets position may not get updated
// before the app frame get updated. To layout the app content correctly during drag events,
// we always return the insets with the corresponding height covering the top.
+ // However, with the "fake" IME navigation bar treated as a caption bar, we return the
+ // insets with the corresponding height the bottom.
if (getType() == WindowInsets.Type.captionBar()) {
- return Insets.of(0, frame.height(), 0, 0);
+ return getId() == ID_IME_CAPTION_BAR
+ ? Insets.of(0, 0, 0, frame.height())
+ : Insets.of(0, frame.height(), 0, 0);
}
// Checks for whether there is shared edge with insets for 0-width/height window.
final boolean hasIntersection = relativeFrame.isEmpty()
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index e8f62fc..a4cbc52 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -44,6 +44,7 @@
private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
= new ArrayList<>();
private int mCaptionInsetsHeight = 0;
+ private int mImeCaptionBarInsetsHeight = 0;
private WindowInsetsAnimationControlListener mLoggingListener;
private @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();
@@ -91,6 +92,11 @@
}
@Override
+ public void setImeCaptionBarInsetsHeight(int height) {
+ mImeCaptionBarInsetsHeight = height;
+ }
+
+ @Override
public void setSystemBarsBehavior(int behavior) {
if (mReplayedInsetsController != null) {
mReplayedInsetsController.setSystemBarsBehavior(behavior);
@@ -168,6 +174,9 @@
if (mCaptionInsetsHeight != 0) {
controller.setCaptionInsetsHeight(mCaptionInsetsHeight);
}
+ if (mImeCaptionBarInsetsHeight != 0) {
+ controller.setImeCaptionBarInsetsHeight(mImeCaptionBarInsetsHeight);
+ }
if (mAnimationsDisabled) {
controller.setAnimationsDisabled(true);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 92509c9..5e19c67 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9279,8 +9279,8 @@
}
while (parentGroup != null && !parentGroup.isImportantForAutofill()) {
- ignoredParentLeft += parentGroup.mLeft;
- ignoredParentTop += parentGroup.mTop;
+ ignoredParentLeft += parentGroup.mLeft - parentGroup.mScrollX;
+ ignoredParentTop += parentGroup.mTop - parentGroup.mScrollY;
viewParent = parentGroup.getParent();
if (viewParent instanceof View) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1b1098d..7bdff8c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7361,6 +7361,90 @@
}
}
+ /**
+ * @hide
+ */
+ @Override
+ public boolean getChildLocalHitRegion(@NonNull View child, @NonNull Region region,
+ @NonNull Matrix matrix, boolean isHover) {
+ if (!child.hasIdentityMatrix()) {
+ matrix.preConcat(child.getInverseMatrix());
+ }
+
+ final int dx = child.mLeft - mScrollX;
+ final int dy = child.mTop - mScrollY;
+ matrix.preTranslate(-dx, -dy);
+
+ final int width = mRight - mLeft;
+ final int height = mBottom - mTop;
+
+ // Map the bounds of this view into the region's coordinates and clip the region.
+ final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF();
+ rect.set(0, 0, width, height);
+ matrix.mapRect(rect);
+
+ boolean notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
+ Math.round(rect.right), Math.round(rect.bottom), Region.Op.INTERSECT);
+
+ if (isHover) {
+ HoverTarget target = mFirstHoverTarget;
+ boolean childIsHit = false;
+ while (target != null) {
+ final HoverTarget next = target.next;
+ if (target.child == child) {
+ childIsHit = true;
+ break;
+ }
+ target = next;
+ }
+ if (!childIsHit) {
+ target = mFirstHoverTarget;
+ while (notEmpty && target != null) {
+ final HoverTarget next = target.next;
+ final View hoveredView = target.child;
+
+ rect.set(hoveredView.mLeft, hoveredView.mTop, hoveredView.mRight,
+ hoveredView.mBottom);
+ matrix.mapRect(rect);
+ notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
+ Math.round(rect.right), Math.round(rect.bottom), Region.Op.DIFFERENCE);
+ target = next;
+ }
+ }
+ } else {
+ TouchTarget target = mFirstTouchTarget;
+ boolean childIsHit = false;
+ while (target != null) {
+ final TouchTarget next = target.next;
+ if (target.child == child) {
+ childIsHit = true;
+ break;
+ }
+ target = next;
+ }
+ if (!childIsHit) {
+ target = mFirstTouchTarget;
+ while (notEmpty && target != null) {
+ final TouchTarget next = target.next;
+ final View touchedView = target.child;
+
+ rect.set(touchedView.mLeft, touchedView.mTop, touchedView.mRight,
+ touchedView.mBottom);
+ matrix.mapRect(rect);
+ notEmpty = region.op(Math.round(rect.left), Math.round(rect.top),
+ Math.round(rect.right), Math.round(rect.bottom), Region.Op.DIFFERENCE);
+ target = next;
+ }
+ }
+ }
+
+ if (notEmpty && mParent != null) {
+ notEmpty = mParent.getChildLocalHitRegion(this, region, matrix, isHover);
+ }
+ return notEmpty;
+ }
+
+
private static void applyOpToRegionByBounds(Region region, View view, Region.Op op) {
final int[] locationInWindow = new int[2];
view.getLocationInWindow(locationInWindow);
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 1020d2e..54bc348 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
@@ -686,6 +687,36 @@
}
/**
+ * Compute the region where the child can receive the {@link MotionEvent}s from the root view.
+ *
+ * <p> Given region where the child will accept {@link MotionEvent}s.
+ * Modify the region to the unblocked region where the child can receive the
+ * {@link MotionEvent}s from the view root.
+ * </p>
+ *
+ * <p> The given region is always clipped by the bounds of the parent views. When there are
+ * on-going {@link MotionEvent}s, this method also makes use of the event dispatching results to
+ * determine whether a sibling view will also block the child's hit region.
+ * </p>
+ *
+ * @param child a child View, whose hit region we want to compute.
+ * @param region the initial hit region where the child view will handle {@link MotionEvent}s,
+ * defined in the child coordinates. Will be overwritten to the result hit region.
+ * @param matrix the matrix that maps the given child view's coordinates to the region
+ * coordinates. It will be modified to a matrix that maps window coordinates to
+ * the result region's coordinates.
+ * @param isHover if true it will return the hover events' hit region, otherwise it will
+ * return the touch events' hit region.
+ * @return true if the returned region is not empty.
+ * @hide
+ */
+ default boolean getChildLocalHitRegion(@NonNull View child, @NonNull Region region,
+ @NonNull Matrix matrix, boolean isHover) {
+ region.setEmpty();
+ return false;
+ }
+
+ /**
* Unbuffered dispatch has been requested by a child of this view parent.
* This method is called by the View hierarchy to signal ancestors that a View needs to
* request unbuffered dispatch.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3aa610a..ddd3269 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -128,6 +128,7 @@
import android.graphics.PorterDuff;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
@@ -321,13 +322,6 @@
SystemProperties.getBoolean("persist.wm.debug.client_immersive_confirmation", false);
/**
- * Whether the client should compute the window frame on its own.
- * @hide
- */
- public static final boolean LOCAL_LAYOUT =
- SystemProperties.getBoolean("persist.debug.local_layout", true);
-
- /**
* Set this system property to true to force the view hierarchy to render
* at 60 Hz. This can be used to measure the potential framerate.
*/
@@ -1911,8 +1905,8 @@
final float compatScale = frames.compatScale;
final boolean frameChanged = !mWinFrame.equals(frame);
final boolean configChanged = !mLastReportedMergedConfiguration.equals(mergedConfiguration);
- final boolean attachedFrameChanged = LOCAL_LAYOUT
- && !Objects.equals(mTmpFrames.attachedFrame, attachedFrame);
+ final boolean attachedFrameChanged =
+ !Objects.equals(mTmpFrames.attachedFrame, attachedFrame);
final boolean displayChanged = mDisplay.getDisplayId() != displayId;
final boolean compatScaleChanged = mTmpFrames.compatScale != compatScale;
final boolean dragResizingChanged = mPendingDragResizing != dragResizing;
@@ -2397,6 +2391,22 @@
}
@Override
+ public boolean getChildLocalHitRegion(@NonNull View child, @NonNull Region region,
+ @NonNull Matrix matrix, boolean isHover) {
+ if (child != mView) {
+ throw new IllegalArgumentException("child " + child + " is not the root view "
+ + mView + " managed by this ViewRootImpl");
+ }
+
+ RectF rectF = new RectF(0, 0, mWidth, mHeight);
+ matrix.mapRect(rectF);
+ // Note: don't apply scroll offset, because we want to know its
+ // visibility in the virtual canvas being given to the view hierarchy.
+ return region.op(Math.round(rectF.left), Math.round(rectF.top),
+ Math.round(rectF.right), Math.round(rectF.bottom), Region.Op.INTERSECT);
+ }
+
+ @Override
public void bringChildToFront(View child) {
}
@@ -8292,8 +8302,7 @@
final int measuredWidth = mMeasuredWidth;
final int measuredHeight = mMeasuredHeight;
final boolean relayoutAsync;
- if (LOCAL_LAYOUT
- && (mViewFrameInfo.flags & FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED) == 0
+ if ((mViewFrameInfo.flags & FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED) == 0
&& mWindowAttributes.type != TYPE_APPLICATION_STARTING
&& mSyncSeqId <= mLastSyncSeqId
&& winConfigFromAm.diff(winConfigFromWm, false /* compareUndefined */) == 0) {
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index bc0bab7..cc2cd79 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -250,6 +250,16 @@
void setCaptionInsetsHeight(int height);
/**
+ * Sets the insets height for the IME caption bar, which corresponds to the
+ * "fake" IME navigation bar.
+ *
+ * @param height the insets height of the IME caption bar.
+ * @hide
+ */
+ default void setImeCaptionBarInsetsHeight(int height) {
+ }
+
+ /**
* Controls the behavior of system bars.
*
* @param behavior Determines how the bars behave when being hidden by the application.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index afe7559..8899f5c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12884,6 +12884,15 @@
}
/**
+ * @return true if this TextView could be filled by an Autofill service. Note that disabled
+ * fields can still be filled.
+ */
+ @UnsupportedAppUsage
+ boolean isTextAutofillable() {
+ return mText instanceof Editable && onCheckIsTextEditor();
+ }
+
+ /**
* Returns true, only while processing a touch gesture, if the initial
* touch down event caused focus to move to the text view and as a result
* its selection changed. Only valid while processing the touch gesture
@@ -13605,7 +13614,7 @@
@Override
public void autofill(AutofillValue value) {
- if (!isTextEditable()) {
+ if (!isTextAutofillable()) {
Log.w(LOG_TAG, "cannot autofill non-editable TextView: " + this);
return;
}
@@ -13621,7 +13630,7 @@
@Override
public @AutofillType int getAutofillType() {
- return isTextEditable() ? AUTOFILL_TYPE_TEXT : AUTOFILL_TYPE_NONE;
+ return isTextAutofillable() ? AUTOFILL_TYPE_TEXT : AUTOFILL_TYPE_NONE;
}
/**
@@ -13635,7 +13644,7 @@
@Override
@Nullable
public AutofillValue getAutofillValue() {
- if (isTextEditable()) {
+ if (isTextAutofillable()) {
final CharSequence text = TextUtils.trimToParcelableSize(getText());
return AutofillValue.forText(text);
}
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index be88e53..6e9f044 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -161,10 +161,8 @@
private static List<WindowInfo> buildWindowInfos(InputWindowHandle[] windowHandles) {
var windowInfos = new ArrayList<WindowInfo>(windowHandles.length);
for (var handle : windowHandles) {
- var bounds = new Rect(handle.frameLeft, handle.frameTop, handle.frameRight,
- handle.frameBottom);
windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, handle.displayId,
- bounds, handle.inputConfig, handle.transform));
+ handle.frame, handle.inputConfig, handle.transform));
}
return windowInfos;
}
diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
index 3801188..ba87caa 100644
--- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
+++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
@@ -22,6 +22,7 @@
import android.service.voice.HotwordRejectedResult;
import android.service.voice.SoundTriggerFailure;
import android.service.voice.VisualQueryDetectionServiceFailure;
+import com.android.internal.infra.AndroidFuture;
/**
* @hide
@@ -113,4 +114,9 @@
/** Called when the hotword detection process is restarted */
void onProcessRestarted();
+
+ /**
+ * Called when a file open request is sent.
+ */
+ void onOpenFile(in String filename, in AndroidFuture future);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 79152b4..d3103f1 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -18,11 +18,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.BatteryConsumer;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.BatteryStats.BitDescription;
-import android.os.BatteryStats.CpuUsageDetails;
-import android.os.BatteryStats.EnergyConsumerDetails;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.HistoryStepDetails;
import android.os.BatteryStats.HistoryTag;
@@ -42,7 +41,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ParseUtils;
import java.io.File;
import java.io.FileOutputStream;
@@ -83,7 +81,7 @@
private static final int VERSION = 209;
private static final String HISTORY_DIR = "battery-history";
- private static final String FILE_SUFFIX = ".bin";
+ private static final String FILE_SUFFIX = ".bh";
private static final int MIN_FREE_SPACE = 100 * 1024 * 1024;
// Part of initial delta int that specifies the time delta.
@@ -124,10 +122,9 @@
// therefore the tag value is written in the parcel
static final int TAG_FIRST_OCCURRENCE_FLAG = 0x8000;
- static final int EXTENSION_MEASURED_ENERGY_HEADER_FLAG = 0x00000001;
- static final int EXTENSION_MEASURED_ENERGY_FLAG = 0x00000002;
- static final int EXTENSION_CPU_USAGE_HEADER_FLAG = 0x00000004;
- static final int EXTENSION_CPU_USAGE_FLAG = 0x00000008;
+ static final int EXTENSION_POWER_STATS_DESCRIPTOR_FLAG = 0x00000001;
+ static final int EXTENSION_POWER_STATS_FLAG = 0x00000002;
+ static final int EXTENSION_PROCESS_STATE_CHANGE_FLAG = 0x00000004;
// For state1, trace everything except the wakelock bit (which can race with
// suspend) and the running bit (which isn't meaningful in traces).
@@ -149,10 +146,11 @@
* The active history file that the history buffer is backed up into.
*/
private AtomicFile mActiveFile;
+
/**
- * A list of history files with incremental indexes.
+ * A list of history files with increasing timestamps.
*/
- private final List<Integer> mFileNumbers = new ArrayList<>();
+ private final List<BatteryHistoryFile> mHistoryFiles = new ArrayList<>();
/**
* A list of small history parcels, used when BatteryStatsImpl object is created from
@@ -200,14 +198,42 @@
private long mTrackRunningHistoryElapsedRealtimeMs = 0;
private long mTrackRunningHistoryUptimeMs = 0;
private long mHistoryBaseTimeMs;
- private boolean mMeasuredEnergyHeaderWritten = false;
- private boolean mCpuUsageHeaderWritten = false;
- private final VarintParceler mVarintParceler = new VarintParceler();
+ private final ArraySet<PowerStats.Descriptor> mWrittenPowerStatsDescriptors = new ArraySet<>();
private byte mLastHistoryStepLevel = 0;
private boolean mMutable = true;
private final BatteryStatsHistory mWritableHistory;
private boolean mCleanupEnabled = true;
+ private static class BatteryHistoryFile implements Comparable<BatteryHistoryFile> {
+ public final long monotonicTimeMs;
+ public final AtomicFile atomicFile;
+
+ private BatteryHistoryFile(File directory, long monotonicTimeMs) {
+ this.monotonicTimeMs = monotonicTimeMs;
+ atomicFile = new AtomicFile(new File(directory, monotonicTimeMs + FILE_SUFFIX));
+ }
+
+ @Override
+ public int compareTo(BatteryHistoryFile o) {
+ return Long.compare(monotonicTimeMs, o.monotonicTimeMs);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return monotonicTimeMs == ((BatteryHistoryFile) o).monotonicTimeMs;
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(monotonicTimeMs);
+ }
+
+ @Override
+ public String toString() {
+ return atomicFile.getBaseFile().toString();
+ }
+ }
+
/**
* A delegate responsible for computing additional details for a step in battery history.
*/
@@ -317,30 +343,45 @@
Slog.wtf(TAG, "HistoryDir does not exist:" + mHistoryDir.getPath());
}
- final Set<Integer> dedup = new ArraySet<>();
- // scan directory, fill mFileNumbers and mActiveFile.
+ final List<File> toRemove = new ArrayList<>();
+ final Set<BatteryHistoryFile> dedup = new ArraySet<>();
mHistoryDir.listFiles((dir, name) -> {
final int b = name.lastIndexOf(FILE_SUFFIX);
if (b <= 0) {
+ toRemove.add(new File(dir, name));
return false;
}
- final int c = ParseUtils.parseInt(name.substring(0, b), -1);
- if (c != -1) {
- dedup.add(c);
- return true;
- } else {
+ try {
+ long monotonicTime = Long.parseLong(name.substring(0, b));
+ dedup.add(new BatteryHistoryFile(mHistoryDir, monotonicTime));
+ } catch (NumberFormatException e) {
+ toRemove.add(new File(dir, name));
return false;
}
+ return true;
});
if (!dedup.isEmpty()) {
- mFileNumbers.addAll(dedup);
- Collections.sort(mFileNumbers);
- setActiveFile(mFileNumbers.get(mFileNumbers.size() - 1));
- } else {
- // No file found, default to have file 0.
- mFileNumbers.add(0);
- setActiveFile(0);
+ mHistoryFiles.addAll(dedup);
+ Collections.sort(mHistoryFiles);
+ setActiveFile(mHistoryFiles.get(mHistoryFiles.size() - 1));
+ } else if (mMutable) {
+ // No file found, default to have the initial file.
+ BatteryHistoryFile name = makeBatteryHistoryFile();
+ mHistoryFiles.add(name);
+ setActiveFile(name);
}
+ if (!toRemove.isEmpty()) {
+ // Clear out legacy history files, which did not follow the X-Y.bin naming format.
+ BackgroundThread.getHandler().post(() -> {
+ for (File file : toRemove) {
+ file.delete();
+ }
+ });
+ }
+ }
+
+ private BatteryHistoryFile makeBatteryHistoryFile() {
+ return new BatteryHistoryFile(mHistoryDir, mClock.elapsedRealtime() + mHistoryBaseTimeMs);
}
public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize,
@@ -384,8 +425,7 @@
mLastHistoryElapsedRealtimeMs = 0;
mTrackRunningHistoryElapsedRealtimeMs = 0;
mTrackRunningHistoryUptimeMs = 0;
- mMeasuredEnergyHeaderWritten = false;
- mCpuUsageHeaderWritten = false;
+ mWrittenPowerStatsDescriptors.clear();
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
@@ -439,28 +479,15 @@
/**
* Set the active file that mHistoryBuffer is backed up into.
- *
- * @param fileNumber the history file that mHistoryBuffer is backed up into.
*/
- private void setActiveFile(int fileNumber) {
- mActiveFile = getFile(fileNumber);
+ private void setActiveFile(BatteryHistoryFile file) {
+ mActiveFile = file.atomicFile;
if (DEBUG) {
Slog.d(TAG, "activeHistoryFile:" + mActiveFile.getBaseFile().getPath());
}
}
/**
- * Create history AtomicFile from file number.
- *
- * @param num file number.
- * @return AtomicFile object.
- */
- private AtomicFile getFile(int num) {
- return new AtomicFile(
- new File(mHistoryDir, num + FILE_SUFFIX));
- }
-
- /**
* When {@link #mHistoryBuffer} reaches {@link BatteryStatsImpl.Constants#MAX_HISTORY_BUFFER},
* create next history file.
*/
@@ -470,15 +497,19 @@
return;
}
- if (mFileNumbers.isEmpty()) {
+ if (mHistoryFiles.isEmpty()) {
Slog.wtf(TAG, "mFileNumbers should never be empty");
return;
}
- // The last number in mFileNumbers is the highest number. The next file number is highest
- // number plus one.
- final int next = mFileNumbers.get(mFileNumbers.size() - 1) + 1;
- mFileNumbers.add(next);
+ final long start = SystemClock.uptimeMillis();
+ writeHistory();
+ if (DEBUG) {
+ Slog.d(TAG, "writeHistory took ms:" + (SystemClock.uptimeMillis() - start));
+ }
+
+ final BatteryHistoryFile next = makeBatteryHistoryFile();
+ mHistoryFiles.add(next);
setActiveFile(next);
try {
mActiveFile.getBaseFile().createNewFile();
@@ -486,6 +517,21 @@
Slog.e(TAG, "Could not create history file: " + mActiveFile.getBaseFile());
}
+ mHistoryBuffer.setDataSize(0);
+ mHistoryBuffer.setDataPosition(0);
+ mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2);
+ mHistoryBufferLastPos = -1;
+ mHistoryLastWritten.clear();
+ mHistoryLastLastWritten.clear();
+
+ // Mark every entry in the pool with a flag indicating that the tag
+ // has not yet been encountered while writing the current history buffer.
+ for (Map.Entry<HistoryTag, Integer> entry : mHistoryTagPool.entrySet()) {
+ entry.setValue(entry.getValue() | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG);
+ }
+
+ mWrittenPowerStatsDescriptors.clear();
+
synchronized (this) {
cleanupLocked();
}
@@ -507,17 +553,17 @@
// if free disk space is less than 100MB, delete oldest history file.
if (!hasFreeDiskSpace()) {
- int oldest = mFileNumbers.remove(0);
- getFile(oldest).delete();
+ BatteryHistoryFile oldest = mHistoryFiles.remove(0);
+ oldest.atomicFile.delete();
}
// if there are more history files than allowed, delete oldest history files.
// mMaxHistoryFiles comes from Constants.MAX_HISTORY_FILES and can be updated by GService
// config at run time.
- while (mFileNumbers.size() > mMaxHistoryFiles) {
- int oldest = mFileNumbers.get(0);
- getFile(oldest).delete();
- mFileNumbers.remove(0);
+ while (mHistoryFiles.size() > mMaxHistoryFiles) {
+ BatteryHistoryFile oldest = mHistoryFiles.get(0);
+ oldest.atomicFile.delete();
+ mHistoryFiles.remove(0);
}
}
@@ -537,12 +583,14 @@
*/
public void reset() {
if (DEBUG) Slog.i(TAG, "********** CLEARING HISTORY!");
- for (Integer i : mFileNumbers) {
- getFile(i).delete();
+ for (BatteryHistoryFile file : mHistoryFiles) {
+ file.atomicFile.delete();
}
- mFileNumbers.clear();
- mFileNumbers.add(0);
- setActiveFile(0);
+ mHistoryFiles.clear();
+
+ BatteryHistoryFile name = makeBatteryHistoryFile();
+ mHistoryFiles.add(name);
+ setActiveFile(name);
initHistoryBuffer();
}
@@ -550,9 +598,13 @@
/**
* Start iterating history files and history buffer.
*
- * @return always return true.
+ * @param startTimeMs monotonic time (the HistoryItem.time field) to start iterating from,
+ * inclusive
+ * @param endTimeMs monotonic time to stop iterating, exclusive.
+ * Pass 0 to indicate current time.
*/
- public BatteryStatsHistoryIterator iterate() {
+ @NonNull
+ public BatteryStatsHistoryIterator iterate(long startTimeMs, long endTimeMs) {
mCurrentFileIndex = 0;
mCurrentParcel = null;
mCurrentParcelEnd = 0;
@@ -563,7 +615,7 @@
mWritableHistory.setCleanupEnabledLocked(false);
}
}
- return new BatteryStatsHistoryIterator(this);
+ return new BatteryStatsHistoryIterator(this, startTimeMs, endTimeMs);
}
/**
@@ -590,7 +642,7 @@
* buffer
*/
@Nullable
- public Parcel getNextParcel() {
+ public Parcel getNextParcel(long startTimeMs, long endTimeMs) {
// First iterate through all records in current parcel.
if (mCurrentParcel != null) {
if (mCurrentParcel.dataPosition() < mCurrentParcelEnd) {
@@ -606,13 +658,29 @@
}
}
- // Try next available history file.
+ int firstFileIndex = 0;
// skip the last file because its data is in history buffer.
- while (mCurrentFileIndex < mFileNumbers.size() - 1) {
+ int lastFileIndex = mHistoryFiles.size() - 1;
+ for (int i = mHistoryFiles.size() - 1; i >= 0; i--) {
+ BatteryHistoryFile file = mHistoryFiles.get(i);
+ if (file.monotonicTimeMs >= endTimeMs) {
+ lastFileIndex = i;
+ }
+ if (file.monotonicTimeMs <= startTimeMs) {
+ firstFileIndex = i;
+ break;
+ }
+ }
+
+ if (mCurrentFileIndex < firstFileIndex) {
+ mCurrentFileIndex = firstFileIndex;
+ }
+
+ while (mCurrentFileIndex < lastFileIndex) {
mCurrentParcel = null;
mCurrentParcelEnd = 0;
final Parcel p = Parcel.obtain();
- AtomicFile file = getFile(mFileNumbers.get(mCurrentFileIndex++));
+ AtomicFile file = mHistoryFiles.get(mCurrentFileIndex++).atomicFile;
if (readFileToParcel(p, file)) {
int bufSize = p.readInt();
int curPos = p.dataPosition();
@@ -769,9 +837,9 @@
private void writeToParcel(Parcel out, boolean useBlobs) {
final long start = SystemClock.uptimeMillis();
- out.writeInt(mFileNumbers.size() - 1);
- for (int i = 0; i < mFileNumbers.size() - 1; i++) {
- AtomicFile file = getFile(mFileNumbers.get(i));
+ out.writeInt(mHistoryFiles.size() - 1);
+ for (int i = 0; i < mHistoryFiles.size() - 1; i++) {
+ AtomicFile file = mHistoryFiles.get(i).atomicFile;
byte[] raw = new byte[0];
try {
raw = file.readFully();
@@ -872,8 +940,12 @@
}
@VisibleForTesting
- public List<Integer> getFilesNumbers() {
- return mFileNumbers;
+ public List<String> getFilesNames() {
+ List<String> names = new ArrayList<>();
+ for (BatteryHistoryFile historyFile : mHistoryFiles) {
+ names.add(historyFile.atomicFile.getBaseFile().getName());
+ }
+ return names;
}
@VisibleForTesting
@@ -886,8 +958,8 @@
*/
public int getHistoryUsedSize() {
int ret = 0;
- for (int i = 0; i < mFileNumbers.size() - 1; i++) {
- ret += getFile(mFileNumbers.get(i)).getBaseFile().length();
+ for (int i = 0; i < mHistoryFiles.size() - 1; i++) {
+ ret += mHistoryFiles.get(i).atomicFile.getBaseFile().length();
}
ret += mHistoryBuffer.dataSize();
if (mHistoryParcels != null) {
@@ -937,7 +1009,7 @@
* Prepares to continue recording after restoring previous history from persistent storage.
*/
public void continueRecordingHistory() {
- if (mHistoryBuffer.dataPosition() <= 0 && mFileNumbers.size() <= 1) {
+ if (mHistoryBuffer.dataPosition() <= 0 && mHistoryFiles.size() <= 1) {
return;
}
@@ -1050,11 +1122,23 @@
}
/**
- * Records measured energy data.
+ * Records a PowerStats snapshot.
*/
- public void recordEnergyConsumerDetails(long elapsedRealtimeMs, long uptimeMs,
- EnergyConsumerDetails energyConsumerDetails) {
- mHistoryCur.energyConsumerDetails = energyConsumerDetails;
+ public void recordPowerStats(long elapsedRealtimeMs, long uptimeMs,
+ PowerStats powerStats) {
+ mHistoryCur.powerStats = powerStats;
+ mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
+ * Records the change of a UID's proc state.
+ */
+ public void recordProcessStateChange(long elapsedRealtimeMs, long uptimeMs,
+ int uid, @BatteryConsumer.ProcessState int processState) {
+ mHistoryCur.processStateChange = mHistoryCur.localProcessStateChange;
+ mHistoryCur.processStateChange.uid = uid;
+ mHistoryCur.processStateChange.processState = processState;
mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG;
writeHistoryItem(elapsedRealtimeMs, uptimeMs);
}
@@ -1279,17 +1363,6 @@
}
/**
- * Records CPU usage by a specific UID. The recorded data is the delta from
- * the previous record for the same UID.
- */
- public void recordCpuUsage(long elapsedRealtimeMs, long uptimeMs,
- CpuUsageDetails cpuUsageDetails) {
- mHistoryCur.cpuUsageDetails = cpuUsageDetails;
- mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG;
- writeHistoryItem(elapsedRealtimeMs, uptimeMs);
- }
-
- /**
* Writes changes to a HistoryItem state bitmap to Atrace.
*/
private void recordTraceCounters(int oldval, int newval, int mask,
@@ -1355,7 +1428,9 @@
mTraceLastState2 = cur.states2;
}
- if (!mHaveBatteryLevel || !mRecordingHistory) {
+ if ((!mHaveBatteryLevel || !mRecordingHistory)
+ && cur.powerStats == null
+ && cur.processStateChange == null) {
return;
}
@@ -1391,8 +1466,8 @@
&& mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
&& mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
&& mHistoryLastWritten.batteryVoltage == cur.batteryVoltage
- && mHistoryLastWritten.energyConsumerDetails == null
- && mHistoryLastWritten.cpuUsageDetails == null) {
+ && mHistoryLastWritten.powerStats == null
+ && mHistoryLastWritten.processStateChange == null) {
// We can merge this new change in with the last one. Merging is
// allowed as long as only the states have changed, and within those states
// as long as no bit has changed both between now and the last entry, as
@@ -1434,34 +1509,15 @@
mMaxHistoryBufferSize = 1024;
}
- //open a new history file.
- final long start = SystemClock.uptimeMillis();
- writeHistory();
- if (DEBUG) {
- Slog.d(TAG, "addHistoryBufferLocked writeHistory took ms:"
- + (SystemClock.uptimeMillis() - start));
- }
- startNextFile();
- mHistoryBuffer.setDataSize(0);
- mHistoryBuffer.setDataPosition(0);
- mHistoryBuffer.setDataCapacity(mMaxHistoryBufferSize / 2);
- mHistoryBufferLastPos = -1;
- mHistoryLastWritten.clear();
- mHistoryLastLastWritten.clear();
-
- // Mark every entry in the pool with a flag indicating that the tag
- // has not yet been encountered while writing the current history buffer.
- for (Map.Entry<HistoryTag, Integer> entry : mHistoryTagPool.entrySet()) {
- entry.setValue(entry.getValue() | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG);
- }
- mMeasuredEnergyHeaderWritten = false;
- mCpuUsageHeaderWritten = false;
-
// Make a copy of mHistoryCur.
HistoryItem copy = new HistoryItem();
copy.setTo(cur);
+
+ startNextFile();
+
// startRecordingHistory will reset mHistoryCur.
startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
+
// Add the copy into history buffer.
writeHistoryItem(elapsedRealtimeMs, uptimeMs, copy, HistoryItem.CMD_UPDATE);
return;
@@ -1477,8 +1533,8 @@
copy.eventCode = HistoryItem.EVENT_NONE;
copy.eventTag = null;
copy.tagsFirstOccurrence = false;
- copy.energyConsumerDetails = null;
- copy.cpuUsageDetails = null;
+ copy.powerStats = null;
+ copy.processStateChange = null;
writeHistoryItem(elapsedRealtimeMs, uptimeMs, copy, HistoryItem.CMD_RESET);
}
writeHistoryItem(elapsedRealtimeMs, uptimeMs, cur, HistoryItem.CMD_UPDATE);
@@ -1506,8 +1562,8 @@
cur.eventCode = HistoryItem.EVENT_NONE;
cur.eventTag = null;
cur.tagsFirstOccurrence = false;
- cur.energyConsumerDetails = null;
- cur.cpuUsageDetails = null;
+ cur.powerStats = null;
+ cur.processStateChange = null;
if (DEBUG) {
Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
+ " now " + mHistoryBuffer.dataPosition()
@@ -1638,17 +1694,14 @@
if (stateIntChanged) {
firstToken |= BatteryStatsHistory.DELTA_STATE_FLAG;
}
- if (cur.energyConsumerDetails != null) {
- extensionFlags |= BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_FLAG;
- if (!mMeasuredEnergyHeaderWritten) {
- extensionFlags |= BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_HEADER_FLAG;
+ if (cur.powerStats != null) {
+ extensionFlags |= BatteryStatsHistory.EXTENSION_POWER_STATS_FLAG;
+ if (!mWrittenPowerStatsDescriptors.contains(cur.powerStats.descriptor)) {
+ extensionFlags |= BatteryStatsHistory.EXTENSION_POWER_STATS_DESCRIPTOR_FLAG;
}
}
- if (cur.cpuUsageDetails != null) {
- extensionFlags |= EXTENSION_CPU_USAGE_FLAG;
- if (!mCpuUsageHeaderWritten) {
- extensionFlags |= BatteryStatsHistory.EXTENSION_CPU_USAGE_HEADER_FLAG;
- }
+ if (cur.processStateChange != null) {
+ extensionFlags |= BatteryStatsHistory.EXTENSION_PROCESS_STATE_CHANGE_FLAG;
}
if (extensionFlags != 0) {
cur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG;
@@ -1773,37 +1826,16 @@
dest.writeDouble(cur.wifiRailChargeMah);
if (extensionFlags != 0) {
dest.writeInt(extensionFlags);
- if (cur.energyConsumerDetails != null) {
- if (DEBUG) {
- Slog.i(TAG, "WRITE DELTA: measuredEnergyDetails=" + cur.energyConsumerDetails);
+ if (cur.powerStats != null) {
+ if ((extensionFlags & BatteryStatsHistory.EXTENSION_POWER_STATS_DESCRIPTOR_FLAG)
+ != 0) {
+ cur.powerStats.descriptor.writeSummaryToParcel(dest);
+ mWrittenPowerStatsDescriptors.add(cur.powerStats.descriptor);
}
- if (!mMeasuredEnergyHeaderWritten) {
- EnergyConsumerDetails.EnergyConsumer[] consumers =
- cur.energyConsumerDetails.consumers;
- dest.writeInt(consumers.length);
- for (EnergyConsumerDetails.EnergyConsumer consumer : consumers) {
- dest.writeInt(consumer.type);
- dest.writeInt(consumer.ordinal);
- dest.writeString(consumer.name);
- }
- mMeasuredEnergyHeaderWritten = true;
- }
- mVarintParceler.writeLongArray(dest, cur.energyConsumerDetails.chargeUC);
+ cur.powerStats.writeToParcel(dest);
}
-
- if (cur.cpuUsageDetails != null) {
- if (DEBUG) {
- Slog.i(TAG, "WRITE DELTA: cpuUsageDetails=" + cur.cpuUsageDetails);
- }
- if (!mCpuUsageHeaderWritten) {
- dest.writeInt(cur.cpuUsageDetails.cpuBracketDescriptions.length);
- for (String desc: cur.cpuUsageDetails.cpuBracketDescriptions) {
- dest.writeString(desc);
- }
- mCpuUsageHeaderWritten = true;
- }
- dest.writeInt(cur.cpuUsageDetails.uid);
- mVarintParceler.writeLongArray(dest, cur.cpuUsageDetails.cpuUsageMs);
+ if (cur.processStateChange != null) {
+ cur.processStateChange.writeToParcel(dest);
}
}
}
@@ -2054,13 +2086,15 @@
* fewer bytes. It is a bit more expensive than just writing the long into the parcel,
* but at scale saves a lot of storage and allows recording of longer battery history.
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public static final class VarintParceler {
/**
* Writes an array of longs into Parcel using the varint format, see
* https://developers.google.com/protocol-buffers/docs/encoding#varints
*/
public void writeLongArray(Parcel parcel, long[] values) {
+ if (values.length == 0) {
+ return;
+ }
int out = 0;
int shift = 0;
for (long value : values) {
@@ -2092,6 +2126,9 @@
* Reads a long written with {@link #writeLongArray}
*/
public void readLongArray(Parcel parcel, long[] values) {
+ if (values.length == 0) {
+ return;
+ }
int in = parcel.readInt();
int available = 4;
for (int i = 0; i < values.length; i++) {
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index 4c2b2854..a5d2d0f 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -16,6 +16,7 @@
package com.android.internal.os;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -33,32 +34,32 @@
private static final boolean DEBUG = false;
private static final String TAG = "BatteryStatsHistoryItr";
private final BatteryStatsHistory mBatteryStatsHistory;
+ private final @CurrentTimeMillisLong long mStartTimeMs;
+ private final @CurrentTimeMillisLong long mEndTimeMs;
private final BatteryStats.HistoryStepDetails mReadHistoryStepDetails =
new BatteryStats.HistoryStepDetails();
private final SparseArray<BatteryStats.HistoryTag> mHistoryTags = new SparseArray<>();
- private BatteryStats.EnergyConsumerDetails mEnergyConsumerDetails;
- private BatteryStats.CpuUsageDetails mCpuUsageDetails;
- private final BatteryStatsHistory.VarintParceler mVarintParceler =
- new BatteryStatsHistory.VarintParceler();
+ private final PowerStats.DescriptorRegistry mDescriptorRegistry =
+ new PowerStats.DescriptorRegistry();
+ private BatteryStats.HistoryItem mHistoryItem = new BatteryStats.HistoryItem();
+ private boolean mNextItemReady;
- private final BatteryStats.HistoryItem mHistoryItem = new BatteryStats.HistoryItem();
-
- private static final int MAX_ENERGY_CONSUMER_COUNT = 100;
- private static final int MAX_CPU_BRACKET_COUNT = 100;
-
- public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history) {
+ public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history,
+ @CurrentTimeMillisLong long startTimeMs,
+ @CurrentTimeMillisLong long endTimeMs) {
mBatteryStatsHistory = history;
+ mStartTimeMs = startTimeMs;
+ mEndTimeMs = (endTimeMs != 0) ? endTimeMs : Long.MAX_VALUE;
mHistoryItem.clear();
}
@Override
public boolean hasNext() {
- Parcel p = mBatteryStatsHistory.getNextParcel();
- if (p == null) {
- close();
- return false;
+ if (!mNextItemReady) {
+ advance();
}
- return true;
+
+ return mHistoryItem != null;
}
/**
@@ -67,25 +68,45 @@
*/
@Override
public BatteryStats.HistoryItem next() {
- Parcel p = mBatteryStatsHistory.getNextParcel();
- if (p == null) {
- close();
- return null;
+ if (!mNextItemReady) {
+ advance();
+ }
+ mNextItemReady = false;
+ return mHistoryItem;
+ }
+
+ private void advance() {
+ while (true) {
+ Parcel p = mBatteryStatsHistory.getNextParcel(mStartTimeMs, mEndTimeMs);
+ if (p == null) {
+ break;
+ }
+
+ final long lastRealtimeMs = mHistoryItem.time;
+ final long lastWalltimeMs = mHistoryItem.currentTime;
+ try {
+ readHistoryDelta(p, mHistoryItem);
+ } catch (Throwable t) {
+ Slog.wtf(TAG, "Corrupted battery history", t);
+ break;
+ }
+ if (mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME
+ && mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_RESET
+ && lastWalltimeMs != 0) {
+ mHistoryItem.currentTime = lastWalltimeMs + (mHistoryItem.time - lastRealtimeMs);
+ }
+ if (mEndTimeMs != 0 && mHistoryItem.currentTime >= mEndTimeMs) {
+ break;
+ }
+ if (mHistoryItem.currentTime >= mStartTimeMs) {
+ mNextItemReady = true;
+ return;
+ }
}
- final long lastRealtimeMs = mHistoryItem.time;
- final long lastWalltimeMs = mHistoryItem.currentTime;
- try {
- readHistoryDelta(p, mHistoryItem);
- } catch (Throwable t) {
- Slog.wtf(TAG, "Corrupted battery history", t);
- return null;
- }
- if (mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME
- && mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_RESET && lastWalltimeMs != 0) {
- mHistoryItem.currentTime = lastWalltimeMs + (mHistoryItem.time - lastRealtimeMs);
- }
- return mHistoryItem;
+ mHistoryItem = null;
+ mNextItemReady = true;
+ close();
}
private void readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur) {
@@ -229,74 +250,24 @@
cur.wifiRailChargeMah = src.readDouble();
if ((cur.states2 & BatteryStats.HistoryItem.STATE2_EXTENSIONS_FLAG) != 0) {
final int extensionFlags = src.readInt();
- if ((extensionFlags & BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_HEADER_FLAG) != 0) {
- if (mEnergyConsumerDetails == null) {
- mEnergyConsumerDetails = new BatteryStats.EnergyConsumerDetails();
- }
-
- final int consumerCount = src.readInt();
- if (consumerCount > MAX_ENERGY_CONSUMER_COUNT) {
- // Check to avoid a heap explosion in case the parcel is corrupted
- throw new IllegalStateException(
- "EnergyConsumer count too high: " + consumerCount
- + ". Max = " + MAX_ENERGY_CONSUMER_COUNT);
- }
- mEnergyConsumerDetails.consumers =
- new BatteryStats.EnergyConsumerDetails.EnergyConsumer[consumerCount];
- mEnergyConsumerDetails.chargeUC = new long[consumerCount];
- for (int i = 0; i < consumerCount; i++) {
- BatteryStats.EnergyConsumerDetails.EnergyConsumer consumer =
- new BatteryStats.EnergyConsumerDetails.EnergyConsumer();
- consumer.type = src.readInt();
- consumer.ordinal = src.readInt();
- consumer.name = src.readString();
- mEnergyConsumerDetails.consumers[i] = consumer;
- }
+ if ((extensionFlags & BatteryStatsHistory.EXTENSION_POWER_STATS_DESCRIPTOR_FLAG) != 0) {
+ PowerStats.Descriptor descriptor = PowerStats.Descriptor.readSummaryFromParcel(src);
+ mDescriptorRegistry.register(descriptor);
}
-
- if ((extensionFlags & BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_FLAG) != 0) {
- if (mEnergyConsumerDetails == null) {
- throw new IllegalStateException("MeasuredEnergyDetails without a header");
- }
-
- mVarintParceler.readLongArray(src, mEnergyConsumerDetails.chargeUC);
- cur.energyConsumerDetails = mEnergyConsumerDetails;
+ if ((extensionFlags & BatteryStatsHistory.EXTENSION_POWER_STATS_FLAG) != 0) {
+ cur.powerStats = PowerStats.readFromParcel(src, mDescriptorRegistry);
} else {
- cur.energyConsumerDetails = null;
+ cur.powerStats = null;
}
-
- if ((extensionFlags & BatteryStatsHistory.EXTENSION_CPU_USAGE_HEADER_FLAG) != 0) {
- mCpuUsageDetails = new BatteryStats.CpuUsageDetails();
- final int cpuBracketCount = src.readInt();
- if (cpuBracketCount > MAX_CPU_BRACKET_COUNT) {
- // Check to avoid a heap explosion in case the parcel is corrupted
- throw new IllegalStateException("Too many CPU brackets: " + cpuBracketCount
- + ". Max = " + MAX_CPU_BRACKET_COUNT);
- }
- mCpuUsageDetails.cpuBracketDescriptions = new String[cpuBracketCount];
- for (int i = 0; i < cpuBracketCount; i++) {
- mCpuUsageDetails.cpuBracketDescriptions[i] = src.readString();
- }
- mCpuUsageDetails.cpuUsageMs =
- new long[mCpuUsageDetails.cpuBracketDescriptions.length];
- } else if (mCpuUsageDetails != null) {
- mCpuUsageDetails.cpuBracketDescriptions = null;
- }
-
- if ((extensionFlags & BatteryStatsHistory.EXTENSION_CPU_USAGE_FLAG) != 0) {
- if (mCpuUsageDetails == null) {
- throw new IllegalStateException("CpuUsageDetails without a header");
- }
-
- mCpuUsageDetails.uid = src.readInt();
- mVarintParceler.readLongArray(src, mCpuUsageDetails.cpuUsageMs);
- cur.cpuUsageDetails = mCpuUsageDetails;
+ if ((extensionFlags & BatteryStatsHistory.EXTENSION_PROCESS_STATE_CHANGE_FLAG) != 0) {
+ cur.processStateChange = cur.localProcessStateChange;
+ cur.processStateChange.readFromParcel(src);
} else {
- cur.cpuUsageDetails = null;
+ cur.processStateChange = null;
}
} else {
- cur.energyConsumerDetails = null;
- cur.cpuUsageDetails = null;
+ cur.powerStats = null;
+ cur.processStateChange = null;
}
}
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index 664aeee..5ea6ba8 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -195,6 +195,34 @@
* is distributed among the state according to the time the object spent in those states
* since the previous call to updateValues.
*/
+ public void updateValues(long[] values, long timestampMs) {
+ LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
+ if (container == null || container.mLength != values.length) {
+ container = new LongArrayContainer(values.length);
+ }
+ container.setValues(values);
+ updateValues(container, timestampMs);
+ sTmpArrayContainer.set(container);
+ }
+
+ /**
+ * Adds the supplied values to the current accumulated values in the counter.
+ */
+ public void incrementValues(long[] values, long timestampMs) {
+ LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
+ if (container == null || container.mLength != values.length) {
+ container = new LongArrayContainer(values.length);
+ }
+ container.setValues(values);
+ native_incrementValues(mNativeObject, container.mNativeObject, timestampMs);
+ sTmpArrayContainer.set(container);
+ }
+
+ /**
+ * Sets the new values. The delta between the previously set values and these values
+ * is distributed among the state according to the time the object spent in those states
+ * since the previous call to updateValues.
+ */
public void updateValues(LongArrayContainer longArrayContainer, long timestampMs) {
if (longArrayContainer.mLength != mLength) {
throw new IllegalArgumentException(
@@ -293,6 +321,10 @@
long longArrayContainerNativeObject, long timestampMs);
@CriticalNative
+ private static native void native_incrementValues(long nativeObject,
+ long longArrayContainerNativeObject, long timestampMs);
+
+ @CriticalNative
private static native void native_addCounts(long nativeObject,
long longArrayContainerNativeObject);
diff --git a/core/java/com/android/internal/os/MultiStateStats.java b/core/java/com/android/internal/os/MultiStateStats.java
new file mode 100644
index 0000000..f971849
--- /dev/null
+++ b/core/java/com/android/internal/os/MultiStateStats.java
@@ -0,0 +1,303 @@
+/*
+ * 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.internal.os;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Maintains multidimensional multi-state stats. States could be something like on-battery (0,1),
+ * screen-on (0,1), process state etc. Dimensions refer to the metrics themselves, e.g.
+ * CPU residency, Network packet counts etc. All metrics must be represented as <code>long</code>
+ * values;
+ */
+public class MultiStateStats {
+ /**
+ * A set of states, e.g. on-battery, screen-on, procstate. The state values are integers
+ * from 0 to States.mLabels.length
+ */
+ public static class States {
+ final boolean mTracked;
+ final String[] mLabels;
+
+ public States(boolean tracked, String... labels) {
+ this.mTracked = tracked;
+ this.mLabels = labels;
+ }
+
+ public boolean isTracked() {
+ return mTracked;
+ }
+ }
+
+ /**
+ * Factory for MultiStateStats containers. All generated containers retain their connection
+ * to the Factory and the corresponding configuration.
+ */
+ public static class Factory {
+ private static final int INVALID_SERIAL_STATE = -1;
+ final int mDimensionCount;
+ final States[] mStates;
+ /*
+ * The LongArrayMultiStateCounter object that is used for accumulation of per-state
+ * stats thinks of "state" as a simple 0-based index. This Factory object's job is to
+ * map a combination of individual states (e.g. on-battery, process state etc) to
+ * such a simple index.
+ *
+ * This task is performed in two steps:
+ * 1) We define "composite state" as an integer that combines all constituent States
+ * into one integer as bit fields. This gives us a convenient mechanism for updating a
+ * single constituent State at a time. We maintain an array of bit field masks
+ * corresponding to each constituent State.
+ *
+ * 2) We map composite states to "serial states", i.e. simple integer indexes, taking
+ * into account which constituent states are configured as tracked. If a state is not
+ * tracked, there is no need to maintain separate counts for its values, thus
+ * all values of that constituent state can be mapped to the same serial state.
+ */
+ private final int[] mStateBitFieldMasks;
+ private final short[] mStateBitFieldShifts;
+ final int[] mCompositeToSerialState;
+ final int mSerialStateCount;
+
+ public Factory(int dimensionCount, States... states) {
+ mDimensionCount = dimensionCount;
+ mStates = states;
+
+ int serialStateCount = 1;
+ for (States state : mStates) {
+ if (state.mTracked) {
+ serialStateCount *= state.mLabels.length;
+ }
+ }
+ mSerialStateCount = serialStateCount;
+
+ mStateBitFieldMasks = new int[mStates.length];
+ mStateBitFieldShifts = new short[mStates.length];
+
+ int shift = 0;
+ for (int i = 0; i < mStates.length; i++) {
+ mStateBitFieldShifts[i] = (short) shift;
+ if (mStates[i].mLabels.length < 2) {
+ throw new IllegalArgumentException("Invalid state: " + Arrays.toString(
+ mStates[i].mLabels) + ". Should have at least two values.");
+ }
+ int max = mStates[i].mLabels.length - 1;
+ int bitcount = Integer.SIZE - Integer.numberOfLeadingZeros(max);
+ mStateBitFieldMasks[i] = ((1 << bitcount) - 1) << shift;
+ shift = shift + bitcount;
+ }
+
+ if (shift >= Integer.SIZE - 1) {
+ throw new IllegalArgumentException("Too many states: " + shift
+ + " bits are required to represent the composite state, but only "
+ + (Integer.SIZE - 1) + " are available");
+ }
+
+ // Create a mask that filters out all non tracked states
+ int trackedMask = 0xFFFFFFFF;
+ for (int state = 0; state < mStates.length; state++) {
+ if (!mStates[state].mTracked) {
+ trackedMask &= ~mStateBitFieldMasks[state];
+ }
+ }
+
+ mCompositeToSerialState = new int[1 << shift];
+ Arrays.fill(mCompositeToSerialState, INVALID_SERIAL_STATE);
+
+ int nextSerialState = 0;
+ for (int composite = 0; composite < mCompositeToSerialState.length; composite++) {
+ if (!isValidCompositeState(composite)) continue;
+
+ // Values of an untracked State map to different composite states, but must map to
+ // the same serial state. Achieve that by computing a "base composite", which
+ // is equivalent to the current composite, but has 0 for all untracked States.
+ // See if the base composite already has a serial state assigned. If so, just use
+ // the same one for the current composite.
+ int baseComposite = composite & trackedMask;
+ if (mCompositeToSerialState[baseComposite] != INVALID_SERIAL_STATE) {
+ mCompositeToSerialState[composite] = mCompositeToSerialState[baseComposite];
+ } else {
+ mCompositeToSerialState[composite] = nextSerialState++;
+ }
+ }
+ }
+
+ private boolean isValidCompositeState(int composite) {
+ for (int stateIndex = 0; stateIndex < mStates.length; stateIndex++) {
+ int state = extractStateFromComposite(composite, stateIndex);
+ if (state >= mStates[stateIndex].mLabels.length) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private int extractStateFromComposite(int compositeState, int stateIndex) {
+ return (compositeState & mStateBitFieldMasks[stateIndex])
+ >>> mStateBitFieldShifts[stateIndex];
+ }
+
+ private int setStateInComposite(int baseCompositeState, int stateIndex, int value) {
+ return (baseCompositeState & ~mStateBitFieldMasks[stateIndex])
+ | (value << mStateBitFieldShifts[stateIndex]);
+ }
+
+ /**
+ * Allocates a new stats container using this Factory's configuration.
+ */
+ public MultiStateStats create() {
+ return new MultiStateStats(this, mDimensionCount);
+ }
+
+ /**
+ * Returns the total number of composite states handled by this container. For example,
+ * if there are two states: on-battery (0,1) and screen-on (0,1), both tracked, then the
+ * serial state count will be 2 * 2 = 4
+ */
+ @VisibleForTesting
+ public int getSerialStateCount() {
+ return mSerialStateCount;
+ }
+
+ /**
+ * Returns the integer index used by this container to represent the supplied composite
+ * state.
+ */
+ @VisibleForTesting
+ public int getSerialState(int[] states) {
+ Preconditions.checkArgument(states.length == mStates.length);
+ int compositeState = 0;
+ for (int i = 0; i < states.length; i++) {
+ compositeState = setStateInComposite(compositeState, i, states[i]);
+ }
+ int serialState = mCompositeToSerialState[compositeState];
+ if (serialState == INVALID_SERIAL_STATE) {
+ throw new IllegalArgumentException("State values out of bounds: "
+ + Arrays.toString(states));
+ }
+ return serialState;
+ }
+ }
+
+ private final Factory mFactory;
+ private final LongArrayMultiStateCounter mCounter;
+ private int mCompositeState;
+ private boolean mTracking;
+
+ public MultiStateStats(Factory factory, int dimensionCount) {
+ this.mFactory = factory;
+ mCounter = new LongArrayMultiStateCounter(factory.mSerialStateCount, dimensionCount);
+ }
+
+ /**
+ * Updates the current composite state by changing one of the States supplied to the Factory
+ * constructor.
+ *
+ * @param stateIndex Corresponds to the index of the States supplied to the Factory constructor
+ * @param state The new value of the state (e.g. 0 or 1 for "on-battery")
+ * @param timestampMs The time when the state change occurred
+ */
+ public void setState(int stateIndex, int state, long timestampMs) {
+ if (!mTracking) {
+ mCounter.updateValues(new long[mCounter.getArrayLength()], timestampMs);
+ mTracking = true;
+ }
+ mCompositeState = mFactory.setStateInComposite(mCompositeState, stateIndex, state);
+ mCounter.setState(mFactory.mCompositeToSerialState[mCompositeState], timestampMs);
+ }
+
+ /**
+ * Adds the delta to the metrics. The number of values must correspond to the dimension count
+ * supplied to the Factory constructor
+ */
+ public void increment(long[] values, long timestampMs) {
+ mCounter.incrementValues(values, timestampMs);
+ mTracking = true;
+ }
+
+ /**
+ * Returns accumulated stats for the specified composite state.
+ */
+ public void getStats(long[] outValues, int[] states) {
+ mCounter.getCounts(outValues, mFactory.getSerialState(states));
+ }
+
+ /**
+ * Resets the counters.
+ */
+ public void reset() {
+ mCounter.reset();
+ mTracking = false;
+ }
+
+ @Override
+ public String toString() {
+ return mCounter.toString();
+ }
+
+ /**
+ * Prints the accumulated stats, one line of every combination of states that has data.
+ */
+ public void dump(PrintWriter pw) {
+ long[] tmpArray = new long[mCounter.getArrayLength()];
+ dumpAllStates(pw, new int[mFactory.mStates.length], 0, tmpArray);
+ }
+
+ private void dumpAllStates(PrintWriter pw, int[] states, int stateIndex, long[] values) {
+ if (stateIndex < states.length) {
+ if (!mFactory.mStates[stateIndex].mTracked) {
+ dumpAllStates(pw, states, stateIndex + 1, values);
+ return;
+ }
+
+ for (int i = 0; i < mFactory.mStates[stateIndex].mLabels.length; i++) {
+ states[stateIndex] = i;
+ dumpAllStates(pw, states, stateIndex + 1, values);
+ }
+ return;
+ }
+
+ mCounter.getCounts(values, mFactory.getSerialState(states));
+ boolean nonZero = false;
+ for (long value : values) {
+ if (value != 0) {
+ nonZero = true;
+ break;
+ }
+ }
+ if (!nonZero) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < states.length; i++) {
+ if (mFactory.mStates[i].mTracked) {
+ if (sb.length() != 0) {
+ sb.append(" ");
+ }
+ sb.append(mFactory.mStates[i].mLabels[states[i]]);
+ }
+ }
+ sb.append(" ");
+ sb.append(Arrays.toString(values));
+ pw.println(sb);
+ }
+}
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 0b9773e..e35b7f1 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -11,6 +11,7 @@
per-file *ChargeCalculator* = file:/BATTERY_STATS_OWNERS
per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS
per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS
+per-file *PowerStats* = file:/BATTERY_STATS_OWNERS
per-file *Kernel* = file:/BATTERY_STATS_OWNERS
per-file *MultiState* = file:/BATTERY_STATS_OWNERS
per-file *PowerProfile* = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
new file mode 100644
index 0000000..8f66d1f
--- /dev/null
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -0,0 +1,327 @@
+/*
+ * 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.internal.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.BatteryConsumer;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Container for power stats, acquired by various PowerStatsCollector classes. See subclasses for
+ * details.
+ */
+public final class PowerStats {
+ private static final String TAG = "PowerStats";
+
+ private static final BatteryStatsHistory.VarintParceler VARINT_PARCELER =
+ new BatteryStatsHistory.VarintParceler();
+ private static final byte PARCEL_FORMAT_VERSION = 1;
+
+ private static final int PARCEL_FORMAT_VERSION_MASK = 0x000000FF;
+ private static final int PARCEL_FORMAT_VERSION_SHIFT =
+ Integer.numberOfTrailingZeros(PARCEL_FORMAT_VERSION_MASK);
+ private static final int STATS_ARRAY_LENGTH_MASK = 0x0000FF00;
+ private static final int STATS_ARRAY_LENGTH_SHIFT =
+ Integer.numberOfTrailingZeros(STATS_ARRAY_LENGTH_MASK);
+ public static final int MAX_STATS_ARRAY_LENGTH =
+ 2 ^ Integer.bitCount(STATS_ARRAY_LENGTH_MASK) - 1;
+ private static final int UID_STATS_ARRAY_LENGTH_MASK = 0x00FF0000;
+ private static final int UID_STATS_ARRAY_LENGTH_SHIFT =
+ Integer.numberOfTrailingZeros(UID_STATS_ARRAY_LENGTH_MASK);
+ public static final int MAX_UID_STATS_ARRAY_LENGTH =
+ (2 ^ Integer.bitCount(UID_STATS_ARRAY_LENGTH_MASK)) - 1;
+
+ /**
+ * Descriptor of the stats collected for a given power component (e.g. CPU, WiFi etc).
+ * This descriptor is used for storing PowerStats and can also be used by power models
+ * to adjust the algorithm in accordance with the stats available on the device.
+ */
+ public static class Descriptor {
+ /**
+ * {@link BatteryConsumer.PowerComponent} (e.g. CPU, WIFI etc) that this snapshot relates
+ * to; or a custom power component ID (if the value
+ * is >= {@link BatteryConsumer#FIRST_CUSTOM_POWER_COMPONENT_ID}).
+ */
+ public final int powerComponentId;
+ public final String name;
+
+ public final int statsArrayLength;
+ public final int uidStatsArrayLength;
+
+ /**
+ * Extra parameters specific to the power component, e.g. the availability of power
+ * monitors.
+ */
+ public final PersistableBundle extras;
+
+ public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId,
+ int statsArrayLength, int uidStatsArrayLength, @NonNull PersistableBundle extras) {
+ this(powerComponentId, BatteryConsumer.powerComponentIdToString(powerComponentId),
+ statsArrayLength, uidStatsArrayLength, extras);
+ }
+
+ public Descriptor(int customPowerComponentId, String name, int statsArrayLength,
+ int uidStatsArrayLength, PersistableBundle extras) {
+ if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) {
+ throw new IllegalArgumentException(
+ "statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH);
+ }
+ if (uidStatsArrayLength > MAX_UID_STATS_ARRAY_LENGTH) {
+ throw new IllegalArgumentException(
+ "uidStatsArrayLength is too high. Max = " + MAX_UID_STATS_ARRAY_LENGTH);
+ }
+ this.powerComponentId = customPowerComponentId;
+ this.name = name;
+ this.statsArrayLength = statsArrayLength;
+ this.uidStatsArrayLength = uidStatsArrayLength;
+ this.extras = extras;
+ }
+
+ /**
+ * Writes the Descriptor into the parcel.
+ */
+ public void writeSummaryToParcel(Parcel parcel) {
+ int firstWord = ((PARCEL_FORMAT_VERSION << PARCEL_FORMAT_VERSION_SHIFT)
+ & PARCEL_FORMAT_VERSION_MASK)
+ | ((statsArrayLength << STATS_ARRAY_LENGTH_SHIFT)
+ & STATS_ARRAY_LENGTH_MASK)
+ | ((uidStatsArrayLength << UID_STATS_ARRAY_LENGTH_SHIFT)
+ & UID_STATS_ARRAY_LENGTH_MASK);
+ parcel.writeInt(firstWord);
+ parcel.writeInt(powerComponentId);
+ parcel.writeString(name);
+ extras.writeToParcel(parcel, 0);
+ }
+
+ /**
+ * Reads a Descriptor from the parcel. If the parcel has an incompatible format,
+ * returns null.
+ */
+ @Nullable
+ public static Descriptor readSummaryFromParcel(Parcel parcel) {
+ int firstWord = parcel.readInt();
+ int version = (firstWord & PARCEL_FORMAT_VERSION_MASK) >>> PARCEL_FORMAT_VERSION_SHIFT;
+ if (version != PARCEL_FORMAT_VERSION) {
+ Log.w(TAG, "Cannot read PowerStats from Parcel - the parcel format version has "
+ + "changed from " + version + " to " + PARCEL_FORMAT_VERSION);
+ return null;
+ }
+ int statsArrayLength =
+ (firstWord & STATS_ARRAY_LENGTH_MASK) >>> STATS_ARRAY_LENGTH_SHIFT;
+ int uidStatsArrayLength =
+ (firstWord & UID_STATS_ARRAY_LENGTH_MASK) >>> UID_STATS_ARRAY_LENGTH_SHIFT;
+ int powerComponentId = parcel.readInt();
+ String name = parcel.readString();
+ PersistableBundle extras = parcel.readPersistableBundle();
+ return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength,
+ extras);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Descriptor)) return false;
+ Descriptor that = (Descriptor) o;
+ return powerComponentId == that.powerComponentId
+ && statsArrayLength == that.statsArrayLength
+ && uidStatsArrayLength == that.uidStatsArrayLength
+ && Objects.equals(name, that.name)
+ && extras.size() == that.extras.size() // Unparcel the Parcel if not yet
+ && Bundle.kindofEquals(extras,
+ that.extras); // Since the Parcel is now unparceled, do a deep comparison
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(powerComponentId);
+ }
+
+ @Override
+ public String toString() {
+ if (extras != null) {
+ extras.size(); // Unparcel
+ }
+ return "PowerStats.Descriptor{"
+ + "powerComponentId=" + powerComponentId
+ + ", name='" + name + '\''
+ + ", statsArrayLength=" + statsArrayLength
+ + ", uidStatsArrayLength=" + uidStatsArrayLength
+ + ", extras=" + extras
+ + '}';
+ }
+ }
+
+ /**
+ * A registry for all supported power component types (e.g. CPU, WiFi).
+ */
+ public static class DescriptorRegistry {
+ private final SparseArray<Descriptor> mDescriptors = new SparseArray<>();
+
+ /**
+ * Adds the specified descriptor to the registry. If the registry already
+ * contained a descriptor for the same power component, then the new one replaces
+ * the old one.
+ */
+ public void register(Descriptor descriptor) {
+ mDescriptors.put(descriptor.powerComponentId, descriptor);
+ }
+
+ /**
+ * @param powerComponentId either a BatteryConsumer.PowerComponent or a custom power
+ * component ID
+ */
+ public Descriptor get(int powerComponentId) {
+ return mDescriptors.get(powerComponentId);
+ }
+ }
+
+ public final Descriptor descriptor;
+
+ /**
+ * Duration, in milliseconds, covered by this snapshot.
+ */
+ public long durationMs;
+
+ /**
+ * Device-wide stats.
+ */
+ public long[] stats;
+
+ /**
+ * Per-UID CPU stats.
+ */
+ public final SparseArray<long[]> uidStats = new SparseArray<>();
+
+ public PowerStats(Descriptor descriptor) {
+ this.descriptor = descriptor;
+ stats = new long[descriptor.statsArrayLength];
+ }
+
+ /**
+ * Writes the object into the parcel.
+ */
+ public void writeToParcel(Parcel parcel) {
+ int lengthPos = parcel.dataPosition();
+ parcel.writeInt(0); // Placeholder for length
+
+ int startPos = parcel.dataPosition();
+ parcel.writeInt(descriptor.powerComponentId);
+ parcel.writeLong(durationMs);
+ VARINT_PARCELER.writeLongArray(parcel, stats);
+ parcel.writeInt(uidStats.size());
+ for (int i = 0; i < uidStats.size(); i++) {
+ parcel.writeInt(uidStats.keyAt(i));
+ VARINT_PARCELER.writeLongArray(parcel, uidStats.valueAt(i));
+ }
+
+ int endPos = parcel.dataPosition();
+ parcel.setDataPosition(lengthPos);
+ parcel.writeInt(endPos - startPos);
+ parcel.setDataPosition(endPos);
+ }
+
+ /**
+ * Reads a PowerStats object from the supplied Parcel. If the parcel has an incompatible
+ * format, returns null.
+ */
+ @Nullable
+ public static PowerStats readFromParcel(Parcel parcel, DescriptorRegistry registry) {
+ int length = parcel.readInt();
+ int startPos = parcel.dataPosition();
+ int endPos = startPos + length;
+
+ try {
+ int powerComponentId = parcel.readInt();
+
+ Descriptor descriptor = registry.get(powerComponentId);
+ if (descriptor == null) {
+ Log.e(TAG, "Unsupported PowerStats for power component ID: " + powerComponentId);
+ return null;
+ }
+ PowerStats stats = new PowerStats(descriptor);
+ stats.durationMs = parcel.readLong();
+ stats.stats = new long[descriptor.statsArrayLength];
+ VARINT_PARCELER.readLongArray(parcel, stats.stats);
+ int uidCount = parcel.readInt();
+ for (int i = 0; i < uidCount; i++) {
+ int uid = parcel.readInt();
+ long[] uidStats = new long[descriptor.uidStatsArrayLength];
+ VARINT_PARCELER.readLongArray(parcel, uidStats);
+ stats.uidStats.put(uid, uidStats);
+ }
+ if (parcel.dataPosition() != endPos) {
+ Log.e(TAG, "Corrupted PowerStats parcel. Expected length: " + length
+ + ", actual length: " + (parcel.dataPosition() - startPos));
+ return null;
+ }
+ return stats;
+ } finally {
+ // Unconditionally skip to the end of the written data, even if the actual parcel
+ // format is incompatible
+ parcel.setDataPosition(endPos);
+ }
+ }
+
+ /**
+ * Formats the stats as a string suitable to be included in the Battery History dump.
+ */
+ public String formatForBatteryHistory(String uidPrefix) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("duration=").append(durationMs).append(" ").append(descriptor.name);
+ if (stats.length > 0) {
+ sb.append("=").append(Arrays.toString(stats));
+ }
+ for (int i = 0; i < uidStats.size(); i++) {
+ sb.append(uidPrefix)
+ .append(UserHandle.formatUid(uidStats.keyAt(i)))
+ .append(": ").append(Arrays.toString(uidStats.valueAt(i)));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Prints the contents of the stats snapshot.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("PowerStats: " + descriptor.name + " (" + descriptor.powerComponentId + ')');
+ pw.increaseIndent();
+ pw.print("duration", durationMs).println();
+ for (int i = 0; i < uidStats.size(); i++) {
+ pw.print("UID ");
+ pw.print(uidStats.keyAt(i));
+ pw.print(": ");
+ pw.print(Arrays.toString(uidStats.valueAt(i)));
+ pw.println();
+ }
+ pw.decreaseIndent();
+ }
+
+ @Override
+ public String toString() {
+ return "PowerStats: " + formatForBatteryHistory(" UID ");
+ }
+}
diff --git a/core/java/com/android/internal/widget/BigPictureNotificationImageView.java b/core/java/com/android/internal/widget/BigPictureNotificationImageView.java
index 3a7cf74..f95f5db 100644
--- a/core/java/com/android/internal/widget/BigPictureNotificationImageView.java
+++ b/core/java/com/android/internal/widget/BigPictureNotificationImageView.java
@@ -37,13 +37,16 @@
* Icon.loadDrawable().
*/
@RemoteViews.RemoteView
-public class BigPictureNotificationImageView extends ImageView {
+public class BigPictureNotificationImageView extends ImageView implements
+ NotificationDrawableConsumer {
private static final String TAG = BigPictureNotificationImageView.class.getSimpleName();
private final int mMaximumDrawableWidth;
private final int mMaximumDrawableHeight;
+ private NotificationIconManager mIconManager;
+
public BigPictureNotificationImageView(@NonNull Context context) {
this(context, null, 0, 0);
}
@@ -69,6 +72,19 @@
: R.dimen.notification_big_picture_max_height);
}
+
+ /**
+ * Sets an {@link NotificationIconManager} on this ImageView, which handles the loading of
+ * icons, instead of using the {@link LocalImageResolver} directly.
+ * If set, it overrides the behaviour of {@link #setImageIconAsync} and {@link #setImageIcon},
+ * and it expects that the content of this imageView is only updated calling these two methods.
+ *
+ * @param iconManager to be called, when the icon is updated
+ */
+ public void setIconManager(NotificationIconManager iconManager) {
+ mIconManager = iconManager;
+ }
+
@Override
@android.view.RemotableViewMethod(asyncImpl = "setImageURIAsync")
public void setImageURI(@Nullable Uri uri) {
@@ -84,11 +100,20 @@
@Override
@android.view.RemotableViewMethod(asyncImpl = "setImageIconAsync")
public void setImageIcon(@Nullable Icon icon) {
+ if (mIconManager != null) {
+ mIconManager.updateIcon(this, icon).run();
+ return;
+ }
+ // old code path
setImageDrawable(loadImage(icon));
}
/** @hide **/
public Runnable setImageIconAsync(@Nullable Icon icon) {
+ if (mIconManager != null) {
+ return mIconManager.updateIcon(this, icon);
+ }
+ // old code path
final Drawable drawable = loadImage(icon);
return () -> setImageDrawable(drawable);
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 5b6b360..42be784 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -149,6 +149,7 @@
private View mAppNameDivider;
private TouchDelegateComposite mTouchDelegate = new TouchDelegateComposite(this);
private ArrayList<MessagingLinearLayout.MessagingChild> mToRecycle = new ArrayList<>();
+ private boolean mPrecomputedTextEnabled = false;
public ConversationLayout(@NonNull Context context) {
super(context);
@@ -389,36 +390,37 @@
*/
@RemotableViewMethod(asyncImpl = "setDataAsync")
public void setData(Bundle extras) {
+ bind(parseMessagingData(extras, /* usePrecomputedText= */ false));
+ }
+
+ @NonNull
+ private MessagingData parseMessagingData(Bundle extras, boolean usePrecomputedText) {
Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
- List<Notification.MessagingStyle.Message> newMessages
- = Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
+ List<Notification.MessagingStyle.Message> newMessages =
+ Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
Parcelable[] histMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
- List<Notification.MessagingStyle.Message> newHistoricMessages
- = Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
+ List<Notification.MessagingStyle.Message> newHistoricMessages =
+ Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
// mUser now set (would be nice to avoid the side effect but WHATEVER)
final Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON, Person.class);
// Append remote input history to newMessages (again, side effect is lame but WHATEVS)
RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[])
- extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, android.app.RemoteInputHistoryItem.class);
+ extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS,
+ RemoteInputHistoryItem.class);
addRemoteInputHistoryToMessages(newMessages, history);
boolean showSpinner =
extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
int unreadCount = extras.getInt(Notification.EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT);
- // convert MessagingStyle.Message to MessagingMessage, re-using ones from a previous binding
- // if they exist
final List<MessagingMessage> newMessagingMessages =
- createMessages(newMessages, /* isHistoric= */false,
- /* usePrecomputedText= */false);
+ createMessages(newMessages, /* isHistoric= */false, usePrecomputedText);
final List<MessagingMessage> newHistoricMessagingMessages =
- createMessages(newHistoricMessages, /* isHistoric= */true,
- /* usePrecomputedText= */false);
- // bind it, baby
- bindViews(user, showSpinner, unreadCount,
- newMessagingMessages,
- newHistoricMessagingMessages);
+ createMessages(newHistoricMessages, /* isHistoric= */true, usePrecomputedText);
+
+ return new MessagingData(user, showSpinner, unreadCount,
+ newHistoricMessagingMessages, newMessagingMessages);
}
/**
@@ -430,7 +432,33 @@
*/
@NonNull
public Runnable setDataAsync(Bundle extras) {
- return () -> setData(extras);
+ if (!mPrecomputedTextEnabled) {
+ return () -> setData(extras);
+ }
+
+ final MessagingData messagingData =
+ parseMessagingData(extras, /* usePrecomputedText= */ true);
+
+ return () -> {
+ finalizeInflate(messagingData.getHistoricMessagingMessages());
+ finalizeInflate(messagingData.getNewMessagingMessages());
+
+ bind(messagingData);
+ };
+ }
+
+ /**
+ * enable/disable precomputed text usage
+ * @hide
+ */
+ public void setPrecomputedTextEnabled(boolean precomputedTextEnabled) {
+ mPrecomputedTextEnabled = precomputedTextEnabled;
+ }
+
+ private void finalizeInflate(List<MessagingMessage> historicMessagingMessages) {
+ for (MessagingMessage messagingMessage : historicMessagingMessages) {
+ messagingMessage.finalizeInflate();
+ }
}
@Override
@@ -460,17 +488,12 @@
}
}
+ private void bind(MessagingData messagingData) {
+ setUser(messagingData.getUser());
+ setUnreadCount(messagingData.getUnreadCount());
- private void bindViews(Person user,
- boolean showSpinner, int unreadCount, List<MessagingMessage> newMessagingMessages,
- List<MessagingMessage> newHistoricMessagingMessages) {
- setUser(user);
- setUnreadCount(unreadCount);
- bind(showSpinner, newMessagingMessages, newHistoricMessagingMessages);
- }
-
- private void bind(boolean showSpinner, List<MessagingMessage> messages,
- List<MessagingMessage> historicMessages) {
+ List<MessagingMessage> messages = messagingData.getNewMessagingMessages();
+ List<MessagingMessage> historicMessages = messagingData.getHistoricMessagingMessages();
// Copy our groups, before they get clobbered
ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups);
@@ -483,7 +506,7 @@
// Let's now create the views and reorder them accordingly
// side-effect: updates mGroups, mAddedGroups
- createGroupViews(groups, senders, showSpinner);
+ createGroupViews(groups, senders, messagingData.getShowSpinner());
// Let's first check which groups were removed altogether and remove them in one animation
removeGroups(oldGroups);
@@ -585,7 +608,7 @@
// When collapsed, we're displaying the image message in a dedicated container
// on the right of the layout instead of inline. Let's add the isolated image there
- MessagingGroup messagingGroup = mGroups.get(mGroups.size() -1);
+ MessagingGroup messagingGroup = mGroups.get(mGroups.size() - 1);
MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
if (isolatedMessage != null) {
newMessage = isolatedMessage.getView();
@@ -1042,7 +1065,7 @@
}
if (visibleChildren > 0 && group.getVisibility() == GONE) {
group.setVisibility(VISIBLE);
- } else if (visibleChildren == 0 && group.getVisibility() != GONE) {
+ } else if (visibleChildren == 0 && group.getVisibility() != GONE) {
group.setVisibility(GONE);
}
}
@@ -1259,7 +1282,7 @@
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
- for (TouchDelegate delegate: mDelegates) {
+ for (TouchDelegate delegate : mDelegates) {
event.setLocation(x, y);
if (delegate.onTouchEvent(event)) {
return true;
diff --git a/core/java/com/android/internal/widget/MessagingData.java b/core/java/com/android/internal/widget/MessagingData.java
new file mode 100644
index 0000000..85b0201
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingData.java
@@ -0,0 +1,70 @@
+/*
+ * 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.internal.widget;
+
+import android.app.Person;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+final class MessagingData {
+ private final Person mUser;
+ private final boolean mShowSpinner;
+ private final List<MessagingMessage> mHistoricMessagingMessages;
+ private final List<MessagingMessage> mNewMessagingMessages;
+ private final int mUnreadCount;
+
+ MessagingData(Person user, boolean showSpinner,
+ List<MessagingMessage> historicMessagingMessages,
+ List<MessagingMessage> newMessagingMessages) {
+ this(user, showSpinner, /* unreadCount= */0,
+ historicMessagingMessages, newMessagingMessages);
+ }
+
+ MessagingData(Person user, boolean showSpinner,
+ int unreadCount,
+ List<MessagingMessage> historicMessagingMessages,
+ List<MessagingMessage> newMessagingMessages) {
+ mUser = user;
+ mShowSpinner = showSpinner;
+ mUnreadCount = unreadCount;
+ mHistoricMessagingMessages = historicMessagingMessages;
+ mNewMessagingMessages = newMessagingMessages;
+ }
+
+ public Person getUser() {
+ return mUser;
+ }
+
+ public boolean getShowSpinner() {
+ return mShowSpinner;
+ }
+
+ public List<MessagingMessage> getHistoricMessagingMessages() {
+ return mHistoricMessagingMessages;
+ }
+
+ public List<MessagingMessage> getNewMessagingMessages() {
+ return mNewMessagingMessages;
+ }
+
+ public int getUnreadCount() {
+ return mUnreadCount;
+ }
+}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 83557cd..b6d7503 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -87,7 +87,7 @@
private ImageResolver mImageResolver;
private CharSequence mConversationTitle;
private ArrayList<MessagingLinearLayout.MessagingChild> mToRecycle = new ArrayList<>();
-
+ private boolean mPrecomputedTextEnabled = false;
public MessagingLayout(@NonNull Context context) {
super(context);
}
@@ -162,15 +162,23 @@
*/
@RemotableViewMethod(asyncImpl = "setDataAsync")
public void setData(Bundle extras) {
+ bind(parseMessagingData(extras, /* usePrecomputedText= */false));
+ }
+
+ @NonNull
+ private MessagingData parseMessagingData(Bundle extras, boolean usePrecomputedText) {
Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
- List<Notification.MessagingStyle.Message> newMessages
- = Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
+ List<Notification.MessagingStyle.Message> newMessages =
+ Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
Parcelable[] histMessages = extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
- List<Notification.MessagingStyle.Message> newHistoricMessages
- = Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
- setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON, android.app.Person.class));
- RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[])
- extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, android.app.RemoteInputHistoryItem.class);
+ List<Notification.MessagingStyle.Message> newHistoricMessages =
+ Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
+ setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON,
+ Person.class));
+ RemoteInputHistoryItem[] history =
+ (RemoteInputHistoryItem[]) extras.getParcelableArray(
+ Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS,
+ RemoteInputHistoryItem.class);
addRemoteInputHistoryToMessages(newMessages, history);
final Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON, Person.class);
@@ -178,10 +186,12 @@
extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
final List<MessagingMessage> historicMessagingMessages = createMessages(newHistoricMessages,
- /* isHistoric= */true, /* usePrecomputedText= */ false);
+ /* isHistoric= */true, usePrecomputedText);
final List<MessagingMessage> newMessagingMessages =
- createMessages(newMessages, /* isHistoric= */false, /* usePrecomputedText= */false);
- bindViews(user, showSpinner, historicMessagingMessages, newMessagingMessages);
+ createMessages(newMessages, /* isHistoric */false, usePrecomputedText);
+
+ return new MessagingData(user, showSpinner,
+ historicMessagingMessages, newMessagingMessages);
}
/**
@@ -193,7 +203,32 @@
*/
@NonNull
public Runnable setDataAsync(Bundle extras) {
- return () -> setData(extras);
+ if (!mPrecomputedTextEnabled) {
+ return () -> setData(extras);
+ }
+
+ final MessagingData messagingData =
+ parseMessagingData(extras, /* usePrecomputedText= */true);
+
+ return () -> {
+ finalizeInflate(messagingData.getHistoricMessagingMessages());
+ finalizeInflate(messagingData.getNewMessagingMessages());
+ bind(messagingData);
+ };
+ }
+
+ /**
+ * enable/disable precomputed text usage
+ * @hide
+ */
+ public void setPrecomputedTextEnabled(boolean precomputedTextEnabled) {
+ mPrecomputedTextEnabled = precomputedTextEnabled;
+ }
+
+ private void finalizeInflate(List<MessagingMessage> historicMessagingMessages) {
+ for (MessagingMessage messagingMessage: historicMessagingMessages) {
+ messagingMessage.finalizeInflate();
+ }
}
@Override
@@ -218,17 +253,13 @@
}
}
- private void bindViews(Person user, boolean showSpinner,
- List<MessagingMessage> historicMessagingMessages,
- List<MessagingMessage> newMessagingMessages) {
- setUser(user);
- bind(showSpinner, historicMessagingMessages, newMessagingMessages);
- }
+ private void bind(MessagingData messagingData) {
+ setUser(messagingData.getUser());
- private void bind(boolean showSpinner, List<MessagingMessage> historicMessages,
- List<MessagingMessage> messages) {
+ List<MessagingMessage> historicMessages = messagingData.getHistoricMessagingMessages();
+ List<MessagingMessage> messages = messagingData.getNewMessagingMessages();
ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups);
- addMessagesToGroups(historicMessages, messages, showSpinner);
+ addMessagesToGroups(historicMessages, messages, messagingData.getShowSpinner());
// Let's first check which groups were removed altogether and remove them in one animation
removeGroups(oldGroups);
diff --git a/core/java/com/android/internal/widget/NotificationDrawableConsumer.java b/core/java/com/android/internal/widget/NotificationDrawableConsumer.java
new file mode 100644
index 0000000..7c4d929
--- /dev/null
+++ b/core/java/com/android/internal/widget/NotificationDrawableConsumer.java
@@ -0,0 +1,34 @@
+/*
+ * 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.internal.widget;
+
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.Nullable;
+
+/**
+ * An interface for the class, who will use {@link NotificationIconManager} to load icons.
+ */
+public interface NotificationDrawableConsumer {
+
+ /**
+ * Sets a drawable as the content of this consumer.
+ *
+ * @param drawable the {@link Drawable} to set, or {@code null} to clear the content
+ */
+ void setImageDrawable(@Nullable Drawable drawable);
+}
diff --git a/core/java/com/android/internal/widget/NotificationIconManager.java b/core/java/com/android/internal/widget/NotificationIconManager.java
new file mode 100644
index 0000000..221845c
--- /dev/null
+++ b/core/java/com/android/internal/widget/NotificationIconManager.java
@@ -0,0 +1,42 @@
+/*
+ * 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.internal.widget;
+
+import android.graphics.drawable.Icon;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * An interface used for Notification views to delegate handling the loading of icons.
+ */
+public interface NotificationIconManager {
+
+ /**
+ * Called when a new icon is provided to display.
+ *
+ * @param drawableConsumer a consumer, which can display the loaded drawable.
+ * @param icon the updated icon to be displayed.
+ *
+ * @return a {@link Runnable} that sets the drawable on the consumer
+ */
+ @NonNull
+ Runnable updateIcon(
+ @NonNull NotificationDrawableConsumer drawableConsumer,
+ @Nullable Icon icon
+ );
+}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 9aa992b..b5d70d3 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -353,7 +353,7 @@
JNIEnv* env;
jmethodID methodId;
- ALOGD("Calling main entry %s", className.string());
+ ALOGD("Calling main entry %s", className.c_str());
env = getJNIEnv();
if (clazz == NULL || env == NULL) {
@@ -362,7 +362,7 @@
methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");
if (methodId == NULL) {
- ALOGE("ERROR: could not find method %s.main(String[])\n", className.string());
+ ALOGE("ERROR: could not find method %s.main(String[])\n", className.c_str());
return UNKNOWN_ERROR;
}
@@ -378,7 +378,7 @@
strArray = env->NewObjectArray(numArgs, stringClass, NULL);
for (size_t i = 0; i < numArgs; i++) {
- jstring argStr = env->NewStringUTF(args[i].string());
+ jstring argStr = env->NewStringUTF(args[i].c_str());
env->SetObjectArrayElement(strArray, i, argStr);
}
@@ -1269,7 +1269,7 @@
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i) {
- jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
+ jstring optionsStr = env->NewStringUTF(options.itemAt(i).c_str());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index de429a0..760037f 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -52,7 +52,7 @@
env->ReleaseStringUTFChars(file, filePath);
- const char* packageNameStr = obb->getPackageName().string();
+ const char* packageNameStr = obb->getPackageName().c_str();
jstring packageName = env->NewStringUTF(packageNameStr);
if (packageName == NULL) {
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 9c6a534..deb138f 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -150,7 +150,7 @@
return gStringOffsets.emptyString;
}
- ScopedLocalRef<jstring> javaString(env, env->NewStringUTF(string.string()));
+ ScopedLocalRef<jstring> javaString(env, env->NewStringUTF(string.c_str()));
jstring internedString = (jstring)
env->CallObjectMethod(javaString.get(), gStringOffsets.intern);
return internedString;
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index eb5f297..ea3c70f 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -35,6 +35,7 @@
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
#include "jni.h"
+#include "jni_common.h"
namespace android {
@@ -57,10 +58,7 @@
jfieldID layoutParamsFlags;
jfieldID layoutParamsType;
jfieldID dispatchingTimeoutMillis;
- jfieldID frameLeft;
- jfieldID frameTop;
- jfieldID frameRight;
- jfieldID frameBottom;
+ jfieldID frame;
jfieldID surfaceInset;
jfieldID scaleFactor;
jfieldID touchableRegion;
@@ -128,14 +126,11 @@
mInfo.dispatchingTimeout = std::chrono::milliseconds(
env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis));
- mInfo.frameLeft = env->GetIntField(obj,
- gInputWindowHandleClassInfo.frameLeft);
- mInfo.frameTop = env->GetIntField(obj,
- gInputWindowHandleClassInfo.frameTop);
- mInfo.frameRight = env->GetIntField(obj,
- gInputWindowHandleClassInfo.frameRight);
- mInfo.frameBottom = env->GetIntField(obj,
- gInputWindowHandleClassInfo.frameBottom);
+
+ ScopedLocalRef<jobject> frameObj(env,
+ env->GetObjectField(obj, gInputWindowHandleClassInfo.frame));
+ mInfo.frame = JNICommon::rectFromObj(env, frameObj.get());
+
mInfo.surfaceInset = env->GetIntField(obj,
gInputWindowHandleClassInfo.surfaceInset);
mInfo.globalScaleFactor = env->GetFloatField(obj,
@@ -283,13 +278,9 @@
std::chrono::duration_cast<std::chrono::milliseconds>(
windowInfo.dispatchingTimeout)
.count());
- env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameLeft,
- windowInfo.frameLeft);
- env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameTop, windowInfo.frameTop);
- env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameRight,
- windowInfo.frameRight);
- env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameBottom,
- windowInfo.frameBottom);
+ ScopedLocalRef<jobject> rectObj(env, JNICommon::objFromRect(env, windowInfo.frame));
+ env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.frame, rectObj.get());
+
env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.surfaceInset,
windowInfo.surfaceInset);
env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.scaleFactor,
@@ -400,17 +391,7 @@
GET_FIELD_ID(gInputWindowHandleClassInfo.dispatchingTimeoutMillis, clazz,
"dispatchingTimeoutMillis", "J");
- GET_FIELD_ID(gInputWindowHandleClassInfo.frameLeft, clazz,
- "frameLeft", "I");
-
- GET_FIELD_ID(gInputWindowHandleClassInfo.frameTop, clazz,
- "frameTop", "I");
-
- GET_FIELD_ID(gInputWindowHandleClassInfo.frameRight, clazz,
- "frameRight", "I");
-
- GET_FIELD_ID(gInputWindowHandleClassInfo.frameBottom, clazz,
- "frameBottom", "I");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.frame, clazz, "frame", "Landroid/graphics/Rect;");
GET_FIELD_ID(gInputWindowHandleClassInfo.surfaceInset, clazz,
"surfaceInset", "I");
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 979c9e3..f04b901 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -295,7 +295,7 @@
if (alloc.length() <= 0) {
return nullptr;
}
- return env->NewStringUTF(alloc.string());
+ return env->NewStringUTF(alloc.c_str());
}
static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) {
@@ -456,7 +456,7 @@
}
for (size_t i = 0; i < file_count; i++) {
- jstring java_string = env->NewStringUTF(asset_dir->getFileName(i).string());
+ jstring java_string = env->NewStringUTF(asset_dir->getFileName(i).c_str());
// Check for errors creating the strings (if malformed or no memory).
if (env->ExceptionCheck()) {
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 624bd5f..69fc515 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -292,7 +292,7 @@
if (status) {
String8 message;
message.appendFormat("Failed to initialize display event receiver. status=%d", status);
- jniThrowRuntimeException(env, message.string());
+ jniThrowRuntimeException(env, message.c_str());
return 0;
}
@@ -316,7 +316,7 @@
if (status) {
String8 message;
message.appendFormat("Failed to schedule next vertical sync pulse. status=%d", status);
- jniThrowRuntimeException(env, message.string());
+ jniThrowRuntimeException(env, message.c_str());
}
}
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 061f669..833952d 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -340,7 +340,7 @@
if (status) {
String8 message;
message.appendFormat("Failed to initialize input event sender. status=%d", status);
- jniThrowRuntimeException(env, message.string());
+ jniThrowRuntimeException(env, message.c_str());
return 0;
}
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 76f5c10..6920211 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -65,6 +65,16 @@
counter->updateValue(*vector, timestamp);
}
+static void native_incrementValues(jlong nativePtr, jlong longArrayContainerNativePtr,
+ jlong timestamp) {
+ battery::LongArrayMultiStateCounter *counter =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ std::vector<uint64_t> *vector =
+ reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);
+
+ counter->incrementValue(*vector, timestamp);
+}
+
static void native_addCounts(jlong nativePtr, jlong longArrayContainerNativePtr) {
battery::LongArrayMultiStateCounter *counter =
reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
@@ -202,6 +212,8 @@
// @CriticalNative
{"native_updateValues", "(JJJ)V", (void *)native_updateValues},
// @CriticalNative
+ {"native_incrementValues", "(JJJ)V", (void *)native_incrementValues},
+ // @CriticalNative
{"native_addCounts", "(JJ)V", (void *)native_addCounts},
// @CriticalNative
{"native_reset", "(J)V", (void *)native_reset},
diff --git a/core/jni/jni_common.cpp b/core/jni/jni_common.cpp
index 8d376cf..b81c9b6 100644
--- a/core/jni/jni_common.cpp
+++ b/core/jni/jni_common.cpp
@@ -26,6 +26,8 @@
namespace android {
static struct {
+ jclass clazz;
+ jmethodID ctor;
jfieldID bottom;
jfieldID left;
jfieldID right;
@@ -40,8 +42,15 @@
return Rect(left, top, right, bottom);
}
+jobject JNICommon::objFromRect(JNIEnv* env, Rect rect) {
+ return env->NewObject(gRectClassInfo.clazz, gRectClassInfo.ctor, rect.left, rect.top,
+ rect.right, rect.bottom);
+}
+
int register_jni_common(JNIEnv* env) {
jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
+ gRectClassInfo.clazz = MakeGlobalRefOrDie(env, rectClazz);
+ gRectClassInfo.ctor = GetMethodIDOrDie(env, rectClazz, "<init>", "(IIII)V");
gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I");
gRectClassInfo.left = GetFieldIDOrDie(env, rectClazz, "left", "I");
gRectClassInfo.right = GetFieldIDOrDie(env, rectClazz, "right", "I");
diff --git a/core/jni/jni_common.h b/core/jni/jni_common.h
index a2bf6fb..d670a7d 100644
--- a/core/jni/jni_common.h
+++ b/core/jni/jni_common.h
@@ -22,5 +22,6 @@
class JNICommon {
public:
static Rect rectFromObj(JNIEnv* env, jobject rectObj);
+ static jobject objFromRect(JNIEnv* env, Rect rect);
};
} // namespace android
\ No newline at end of file
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index d7969cf..ed0081c 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -137,6 +137,7 @@
optional SettingProto gesture_setup_complete = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto touch_gesture_enabled = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto long_press_home_enabled = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto search_press_hold_nav_handle_enabled = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Assist assist = 7;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e129f7d..f55501a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7790,8 +7790,9 @@
android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.PlatLogoActivity"
- android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen"
+ android:theme="@style/Theme.NoTitleBar.Fullscreen"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
+ android:enableOnBackInvokedCallback="true"
android:icon="@drawable/platlogo"
android:process=":ui">
</activity>
diff --git a/core/res/res/drawable/focus_event_rotary_input_background.xml b/core/res/res/drawable/focus_event_rotary_input_background.xml
new file mode 100644
index 0000000..512cd68
--- /dev/null
+++ b/core/res/res/drawable/focus_event_rotary_input_background.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="focus_event_rotary_input_background"
+ android:shape="rectangle">
+
+ <!-- View background color -->
+ <solid android:color="#80741b47" />
+
+ <!-- View border color and width -->
+ <stroke android:width="1dp" android:color="#ffff00ff" />
+
+ <!-- The radius makes the corners rounded -->
+ <corners android:radius="4dp" />
+
+</shape>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4614dce..aeeaaca 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -542,6 +542,9 @@
<bool name="config_goToSleepOnButtonPressTheaterMode">true</bool>
<!-- If this is true, long press on power button will be available from the non-interactive state -->
<bool name="config_supportLongPressPowerWhenNonInteractive">false</bool>
+ <!-- If this is true, short press on power button will be available whenever the default display
+ is on even if the device is non-interactive (dreaming). -->
+ <bool name="config_supportShortPressPowerWhenDefaultDisplayOn">false</bool>
<!-- If this is true, then keep dreaming when unplugging.
This config was formerly known as config_keepDreamingWhenUndocking.
@@ -5041,6 +5044,21 @@
</array>
<!-- See DisplayWhiteBalanceController.
+ A float array containing a list of ambient brightnesses, in Lux. This array,
+ together with config_displayWhiteBalanceLowLightAmbientBiasesStrong, is used to generate a
+ lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
+ ambient brightness readings to a bias, where the bias is used to linearly interpolate
+ between ambient color temperature and
+ config_displayWhiteBalanceLowLightAmbientColorTemperatureIdle.
+ This table is optional. If used, this array must,
+ 1) Contain at least two entries
+ 2) Be the same length as config_displayWhiteBalanceLowLightAmbientBiasesStrong. -->
+ <array name ="config_displayWhiteBalanceLowLightAmbientBrightnessesStrong">
+ <item>10.0</item>
+ <item>10.0</item>
+ </array>
+
+ <!-- See DisplayWhiteBalanceController.
An array containing a list of biases. See
config_displayWhiteBalanceLowLightAmbientBrightnesses for additional details.
This array must be in the range of [0.0, 1.0]. -->
@@ -5050,12 +5068,28 @@
</array>
<!-- See DisplayWhiteBalanceController.
+ An array containing a list of biases. See
+ config_displayWhiteBalanceLowLightAmbientBrightnessesStrong for additional details.
+ This array must be in the range of [0.0, 1.0]. -->
+ <array name ="config_displayWhiteBalanceLowLightAmbientBiasesStrong">
+ <item>0.0</item>
+ <item>1.0</item>
+ </array>
+
+ <!-- See DisplayWhiteBalanceController.
The ambient color temperature (in cct) to which we interpolate towards using the
the look up table generated by config_displayWhiteBalanceLowLightAmbientBrightnesses
and config_displayWhiteBalanceLowLightAmbientBiases. -->
<item name="config_displayWhiteBalanceLowLightAmbientColorTemperature" format="float" type="dimen">6500.0</item>
<!-- See DisplayWhiteBalanceController.
+ The ambient color temperature (in cct) to which we interpolate towards using the
+ the look up table generated by config_displayWhiteBalanceLowLightAmbientBrightnessesStrong
+ and config_displayWhiteBalanceLowLightAmbientBiasesStrong. Used when device is in Idle Screen
+ Brightness mode. -->
+ <item name="config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong" format="float" type="dimen">6500.0</item>
+
+ <!-- See DisplayWhiteBalanceController.
A float array containing a list of ambient brightnesses, in Lux. This array,
together with config_displayWhiteBalanceHighLightAmbientBiases, is used to generate a
lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
@@ -5069,6 +5103,19 @@
</array>
<!-- See DisplayWhiteBalanceController.
+ A float array containing a list of ambient brightnesses, in Lux. This array,
+ together with config_displayWhiteBalanceHighLightAmbientBiasesStrong, is used to generate a
+ lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
+ ambient brightness readings to a bias, where the bias is used to linearly interpolate
+ between ambient color temperature and
+ config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong.
+ This table is optional. If used, this array must,
+ 1) Contain at least two entries
+ 2) Be the same length as config_displayWhiteBalanceHighLightAmbientBiasesStrong. -->
+ <array name ="config_displayWhiteBalanceHighLightAmbientBrightnessesStrong">
+ </array>
+
+ <!-- See DisplayWhiteBalanceController.
An array containing a list of biases. See
config_displayWhiteBalanceHighLightAmbientBrightnesses for additional details.
This array must be in the range of [0.0, 1.0]. -->
@@ -5076,12 +5123,26 @@
</array>
<!-- See DisplayWhiteBalanceController.
+ An array containing a list of biases. See
+ config_displayWhiteBalanceHighLightAmbientBrightnessesStrong for additional details.
+ This array must be in the range of [0.0, 1.0]. -->
+ <array name ="config_displayWhiteBalanceHighLightAmbientBiasesStrong">
+ </array>
+
+ <!-- See DisplayWhiteBalanceController.
The ambient color temperature (in cct) to which we interpolate towards using the
the look up table generated by config_displayWhiteBalanceHighLightAmbientBrightnesses
and config_displayWhiteBalanceHighLightAmbientBiases. -->
<item name="config_displayWhiteBalanceHighLightAmbientColorTemperature" format="float" type="dimen">8000.0</item>
<!-- See DisplayWhiteBalanceController.
+ The ambient color temperature (in cct) to which we interpolate towards using the
+ the look up table generated by config_displayWhiteBalanceHighLightAmbientBrightnessesStrong
+ and config_displayWhiteBalanceHighLightAmbientBiasesStrong. Used when device is in Idle
+ Screen Brightness mode. -->
+ <item name="config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong" format="float" type="dimen">8000.0</item>
+
+ <!-- See DisplayWhiteBalanceController.
A float array containing a list of ambient color temperatures, in Kelvin. This array,
together with config_displayWhiteBalanceDisplayColorTemperatures, is used to generate a
lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
@@ -6114,6 +6175,9 @@
<!-- Default value for Settings.ASSIST_TOUCH_GESTURE_ENABLED -->
<bool name="config_assistTouchGestureEnabledDefault">true</bool>
+ <!-- Default value for Settings.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED -->
+ <bool name="config_searchPressHoldNavHandleEnabledDefault">true</bool>
+
<!-- The maximum byte size of the information contained in the bundle of
HotwordDetectedResult. -->
<integer translatable="false" name="config_hotwordDetectedResultMaxBundleSize">0</integer>
@@ -6560,11 +6624,6 @@
<!-- Whether to show weather on the lock screen by default. -->
<bool name="config_lockscreenWeatherEnabledByDefault">false</bool>
- <!-- Whether to reset Battery Stats on unplug when the battery level is high. -->
- <bool name="config_batteryStatsResetOnUnplugHighBatteryLevel">true</bool>
- <!-- Whether to reset Battery Stats on unplug if the battery was significantly charged -->
- <bool name="config_batteryStatsResetOnUnplugAfterSignificantCharge">true</bool>
-
<!-- Whether we should persist the brightness value in nits for the default display even if
the underlying display device changes. -->
<bool name="config_persistBrightnessNitsForDefaultDisplay">false</bool>
diff --git a/core/res/res/values/config_battery_stats.xml b/core/res/res/values/config_battery_stats.xml
new file mode 100644
index 0000000..8fb48bc
--- /dev/null
+++ b/core/res/res/values/config_battery_stats.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. Do not translate.
+
+ NOTE: The naming convention is "config_camelCaseValue". -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether to reset Battery Stats on unplug when the battery level is high. -->
+ <bool name="config_batteryStatsResetOnUnplugHighBatteryLevel">true</bool>
+ <!-- Whether to reset Battery Stats on unplug if the battery was significantly charged -->
+ <bool name="config_batteryStatsResetOnUnplugAfterSignificantCharge">true</bool>
+
+ <!-- CPU power stats collection throttle period in milliseconds. Since power stats collection
+ is a relatively expensive operation, this throttle period may need to be adjusted for low-power
+ devices-->
+ <integer name="config_defaultPowerStatsThrottlePeriodCpu">60000</integer>
+</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c5aa8b0..207927a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1895,22 +1895,22 @@
<!-- Content description which should be used for the fingerprint icon. -->
<string name="fingerprint_icon_content_description">Fingerprint icon</string>
+ <!-- Notification name shown when the system requires the user to set up device unlock. [CHAR LIMIT=NONE] -->
+ <string name="device_unlock_notification_name">Device unlock</string>
+ <!-- Notification title shown when the system suggests the user to set up another way to unlock. [CHAR LIMIT=NONE] -->
+ <string name="alternative_unlock_setup_notification_title">Try another way to unlock</string>
+ <!-- Notification content shown when the system suggests the user to enroll their face. [CHAR LIMIT=NONE] -->
+ <string name="alternative_face_setup_notification_content">Use Face Unlock when your fingerprint isn\'t recognized, like when your fingers are wet</string>
+ <!-- Notification content shown when the system suggests the user to enroll their fingerprint. [CHAR LIMIT=NONE] -->
+ <string name="alternative_fp_setup_notification_content">Use Fingerprint Unlock when your face isn\'t recognized, like when there\'s not enough light</string>
<!-- Notification name shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
<string name="face_recalibrate_notification_name">Face Unlock</string>
<!-- Notification title shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
<string name="face_recalibrate_notification_title">Issue with Face Unlock</string>
<!-- Notification content shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
<string name="face_recalibrate_notification_content">Tap to delete your face model, then add your face again</string>
- <!-- Title of a notification that directs the user to set up Face Unlock by enrolling their face. [CHAR LIMIT=NONE] -->
- <string name="face_setup_notification_title">Set up Face Unlock</string>
- <!-- Contents of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] -->
- <string name="face_setup_notification_content">Unlock your phone by looking at it</string>
<!-- Error message indicating that the camera privacy sensor has been turned on [CHAR LIMIT=NONE] -->
<string name="face_sensor_privacy_enabled">To use Face Unlock, turn on <b>Camera access</b> in Settings > Privacy</string>
- <!-- Title of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
- <string name="fingerprint_setup_notification_title">Set up more ways to unlock</string>
- <!-- Contents of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
- <string name="fingerprint_setup_notification_content">Tap to add a fingerprint</string>
<!-- Notification name shown when the system requires the user to re-calibrate their fingerprint. [CHAR LIMIT=NONE] -->
<string name="fingerprint_recalibrate_notification_name">Fingerprint Unlock</string>
@@ -5561,7 +5561,7 @@
<!-- Title for the autofill update dialog shown when the the contents of the activity can be updated
in an autofill service, and the service knows what the activity represents, and it represents 3 types of
data (for example, username, password and credit card info) [CHAR LIMIT=NONE] -->
- <string name="autofill_update_title_with_3types">Update these items in <b><xliff:g id="label" example="MyPass">%4$s</xliff:g></b>: <xliff:g id="type" example="Username">%1$s</xliff:g>, <xliff:g id="type" example="Password">%2$s</xliff:g>, and <xliff:g id="type" example="Credit Card">%3$s</xliff:g> ?</string>
+ <string name="autofill_update_title_with_3types">Update these items in <b><xliff:g id="label" example="MyPass">%4$s</xliff:g></b>: <xliff:g id="type" example="Username">%1$s</xliff:g>, <xliff:g id="type" example="Password">%2$s</xliff:g>, and <xliff:g id="type" example="Credit Card">%3$s</xliff:g>?</string>
<!-- Label for the autofill save button [CHAR LIMIT=NONE] -->
<string name="autofill_save_yes">Save</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c4cdd48..8330f7b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1968,6 +1968,7 @@
<java-symbol type="integer" name="config_keyguardDrawnTimeout" />
<java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" />
<java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" />
+ <java-symbol type="bool" name="config_supportShortPressPowerWhenDefaultDisplayOn" />
<java-symbol type="bool" name="config_wimaxEnabled" />
<java-symbol type="bool" name="show_ongoing_ime_switcher" />
<java-symbol type="color" name="config_defaultNotificationColor" />
@@ -2597,6 +2598,12 @@
<!-- Biometric FRR config -->
<java-symbol type="fraction" name="config_biometricNotificationFrrThreshold" />
+ <!-- Biometric FRR notification messages -->
+ <java-symbol type="string" name="device_unlock_notification_name" />
+ <java-symbol type="string" name="alternative_unlock_setup_notification_title" />
+ <java-symbol type="string" name="alternative_face_setup_notification_content" />
+ <java-symbol type="string" name="alternative_fp_setup_notification_content" />
+
<!-- Device credential strings for BiometricManager -->
<java-symbol type="string" name="screen_lock_app_setting_name" />
<java-symbol type="string" name="screen_lock_dialog_default_subtitle" />
@@ -2637,8 +2644,6 @@
<java-symbol type="string" name="fingerprint_recalibrate_notification_name" />
<java-symbol type="string" name="fingerprint_recalibrate_notification_title" />
<java-symbol type="string" name="fingerprint_recalibrate_notification_content" />
- <java-symbol type="string" name="fingerprint_setup_notification_title" />
- <java-symbol type="string" name="fingerprint_setup_notification_content" />
<java-symbol type="string" name="fingerprint_error_power_pressed" />
<!-- Fingerprint config -->
@@ -2706,7 +2711,6 @@
<java-symbol type="string" name="face_authenticated_no_confirmation_required" />
<java-symbol type="string" name="face_authenticated_confirmation_required" />
<java-symbol type="string" name="face_error_security_update_required" />
- <java-symbol type="string" name="face_setup_notification_title" />
<java-symbol type="string" name="config_biometric_prompt_ui_package" />
<java-symbol type="array" name="config_biometric_sensors" />
@@ -4170,11 +4174,17 @@
<java-symbol type="array" name="config_displayWhiteBalanceIncreaseThresholds" />
<java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" />
<java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBrightnesses" />
+ <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBrightnessesStrong" />
<java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBiases" />
+ <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBiasesStrong" />
<java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
+ <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong" />
<java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBrightnesses" />
+ <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBrightnessesStrong" />
<java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBiases" />
+ <java-symbol type="array" name="config_displayWhiteBalanceHighLightAmbientBiasesStrong" />
<java-symbol type="dimen" name="config_displayWhiteBalanceHighLightAmbientColorTemperature" />
+ <java-symbol type="dimen" name="config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong" />
<java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" />
<java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" />
<java-symbol type="array" name="config_displayWhiteBalanceStrongAmbientColorTemperatures" />
@@ -4898,6 +4908,8 @@
<java-symbol type="bool" name="config_assistLongPressHomeEnabledDefault" />
<java-symbol type="bool" name="config_assistTouchGestureEnabledDefault" />
+ <java-symbol type="bool" name="config_searchPressHoldNavHandleEnabledDefault" />
+
<java-symbol type="integer" name="config_hotwordDetectedResultMaxBundleSize" />
<java-symbol type="dimen" name="config_wallpaperDimAmount" />
@@ -5077,6 +5089,7 @@
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" />
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" />
+ <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodCpu" />
<java-symbol name="materialColorOnSecondaryFixedVariant" type="attr"/>
<java-symbol name="materialColorOnTertiaryFixedVariant" type="attr"/>
@@ -5188,6 +5201,7 @@
<java-symbol type="style" name="ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent" />
<java-symbol type="drawable" name="focus_event_pressed_key_background" />
+ <java-symbol type="drawable" name="focus_event_rotary_input_background" />
<java-symbol type="string" name="config_defaultShutdownVibrationFile" />
<java-symbol type="string" name="lockscreen_too_many_failed_attempts_countdown" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index c14da29..7f56eb7 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -88,7 +88,7 @@
resource_dirs: ["res"],
resource_zips: [":FrameworksCoreTests_apks_as_resources"],
- java_resources: [":ApkVerityTestCertDer"],
+ java_resources: [":FrameworksCoreTests_unit_test_cert_der"],
data: [
":BinderDeathRecipientHelperApp1",
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 31755ef..a358c4f 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1749,6 +1749,15 @@
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
</intent-filter>
</activity>
+
+ <activity android:name="android.view.ViewGroupTestActivity"
+ android:label="ViewGroup Test"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/coretests/certs/Android.bp b/core/tests/coretests/certs/Android.bp
index 8d4ecf4..cefdc4d 100644
--- a/core/tests/coretests/certs/Android.bp
+++ b/core/tests/coretests/certs/Android.bp
@@ -13,3 +13,8 @@
name: "FrameworksCoreTests_unit_test_cert",
certificate: "unit_test",
}
+
+filegroup {
+ name: "FrameworksCoreTests_unit_test_cert_der",
+ srcs: ["unit_test.der"],
+}
diff --git a/core/tests/coretests/certs/README b/core/tests/coretests/certs/README
index 00917a1..b5c096e 100644
--- a/core/tests/coretests/certs/README
+++ b/core/tests/coretests/certs/README
@@ -2,3 +2,5 @@
development/tools/make_key unit_test '/CN=unit_test'
development/tools/make_key unit_test_diff '/CN=unit_test_diff'
+
+openssl x509 -in unit_test.x509.pem -out unit_test.der -outform der
diff --git a/core/tests/coretests/certs/unit_test.der b/core/tests/coretests/certs/unit_test.der
new file mode 100644
index 0000000..4dbbc49
--- /dev/null
+++ b/core/tests/coretests/certs/unit_test.der
Binary files differ
diff --git a/core/tests/coretests/res/layout/viewgroup_test.xml b/core/tests/coretests/res/layout/viewgroup_test.xml
new file mode 100644
index 0000000..04f4f52
--- /dev/null
+++ b/core/tests/coretests/res/layout/viewgroup_test.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<!-- Demonstrates adding/removing views from ViewGroup. See corresponding Java code. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/linear_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <EditText
+ android:id="@+id/view"
+ android:layout_width="20dp"
+ android:layout_height="10dp"
+ android:text="Hello World!"
+ android:background="#2F00FF00" />
+ <EditText
+ android:id="@+id/view_scale"
+ android:layout_width="20dp"
+ android:layout_height="10dp"
+ android:scaleX="0.5"
+ android:scaleY="2"
+ android:transformPivotX="0dp"
+ android:transformPivotY="0dp"
+ android:text="Hello World!"
+ android:background="#2F00FF00" />
+ <EditText
+ android:id="@+id/view_translate"
+ android:layout_width="20dp"
+ android:layout_height="10dp"
+ android:translationX="10dp"
+ android:translationY="20dp"
+ android:text="Hello World!"
+ android:background="#2F00FF00" />
+ <FrameLayout
+ android:layout_width="20dp"
+ android:layout_height="10dp">
+ <EditText
+ android:id="@+id/view_overlap_bottom"
+ android:layout_width="20dp"
+ android:layout_height="10dp"
+ android:text="Hello World!"/>
+ <Button
+ android:id="@+id/view_overlap_top"
+ android:layout_width="10dp"
+ android:layout_height="10dp"/>
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="20dp"
+ android:layout_height="10dp">
+ <EditText
+ android:id="@+id/view_cover_bottom"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:text="Hello World!"/>
+ <Button
+ android:id="@+id/view_cover_top"
+ android:layout_width="10dp"
+ android:layout_height="10dp"/>
+ </FrameLayout>
+
+</LinearLayout>
diff --git a/core/tests/coretests/res/raw/fsverity_sig b/core/tests/coretests/res/raw/fsverity_sig
index b2f335d..2c28f0b 100644
--- a/core/tests/coretests/res/raw/fsverity_sig
+++ b/core/tests/coretests/res/raw/fsverity_sig
Binary files differ
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index a936cea..f9377fc 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -19,6 +19,8 @@
import static android.app.Notification.CarExtender.UnreadConversation.KEY_ON_READ;
import static android.app.Notification.CarExtender.UnreadConversation.KEY_ON_REPLY;
import static android.app.Notification.CarExtender.UnreadConversation.KEY_REMOTE_INPUT;
+import static android.app.Notification.DEFAULT_SOUND;
+import static android.app.Notification.DEFAULT_VIBRATE;
import static android.app.Notification.EXTRA_ANSWER_INTENT;
import static android.app.Notification.EXTRA_BUILDER_APPLICATION_INFO;
import static android.app.Notification.EXTRA_CALL_PERSON;
@@ -35,6 +37,9 @@
import static android.app.Notification.EXTRA_PICTURE_ICON;
import static android.app.Notification.EXTRA_SUMMARY_TEXT;
import static android.app.Notification.EXTRA_TITLE;
+import static android.app.Notification.GROUP_ALERT_CHILDREN;
+import static android.app.Notification.GROUP_ALERT_SUMMARY;
+import static android.app.Notification.GROUP_KEY_SILENT;
import static android.app.Notification.MessagingStyle.Message.KEY_DATA_URI;
import static android.app.Notification.MessagingStyle.Message.KEY_SENDER_PERSON;
import static android.app.Notification.MessagingStyle.Message.KEY_TEXT;
@@ -511,6 +516,75 @@
}
@Test
+ public void testBuilder_setSilent_summaryBehavior_groupAlertChildren() {
+ Notification summaryNotif = new Notification.Builder(mContext, "channelId")
+ .setGroupSummary(true)
+ .setGroup("groupKey")
+ .setSilent(true)
+ .build();
+ assertEquals(GROUP_ALERT_CHILDREN, summaryNotif.getGroupAlertBehavior());
+ }
+
+ @Test
+ public void testBuilder_setSilent_childBehavior_groupAlertSummary() {
+ Notification childNotif = new Notification.Builder(mContext, "channelId")
+ .setGroupSummary(false)
+ .setGroup("groupKey")
+ .setSilent(true)
+ .build();
+ assertEquals(GROUP_ALERT_SUMMARY, childNotif.getGroupAlertBehavior());
+ }
+
+ @Test
+ public void testBuilder_setSilent_emptyGroupKey_groupKeySilent() {
+ Notification emptyGroupKeyNotif = new Notification.Builder(mContext, "channelId")
+ .setGroup("")
+ .setSilent(true)
+ .build();
+ assertEquals(GROUP_KEY_SILENT, emptyGroupKeyNotif.getGroup());
+ }
+
+ @Test
+ public void testBuilder_setSilent_vibrateNull() {
+ Notification silentNotif = new Notification.Builder(mContext, "channelId")
+ .setGroup("")
+ .setSilent(true)
+ .build();
+
+ assertNull(silentNotif.vibrate);
+ }
+
+ @Test
+ public void testBuilder_setSilent_soundNull() {
+ Notification silentNotif = new Notification.Builder(mContext, "channelId")
+ .setGroup("")
+ .setSilent(true)
+ .build();
+
+ assertNull(silentNotif.sound);
+ }
+
+ @Test
+ public void testBuilder_setSilent_noDefaultSound() {
+ Notification silentNotif = new Notification.Builder(mContext, "channelId")
+ .setGroup("")
+ .setSilent(true)
+ .build();
+
+ assertEquals(0, (silentNotif.defaults & DEFAULT_SOUND));
+ }
+
+ @Test
+ public void testBuilder_setSilent_noDefaultVibrate() {
+ Notification silentNotif = new Notification.Builder(mContext, "channelId")
+ .setGroup("")
+ .setSilent(true)
+ .build();
+
+ assertEquals(0, (silentNotif.defaults & DEFAULT_VIBRATE));
+ }
+
+ @Test
public void testCallStyle_getSystemActions_forIncomingCall() {
PendingIntent answerIntent = createPendingIntent("answer");
PendingIntent declineIntent = createPendingIntent("decline");
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 91c4dde..a923479 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -67,6 +67,7 @@
import android.window.WindowContextInfo;
import android.window.WindowTokenClientController;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
@@ -629,6 +630,7 @@
});
}
+ @FlakyTest(bugId = 295234586)
@Test
public void testHandleConfigurationChanged_DoesntOverrideActivityConfig() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
@@ -643,7 +645,8 @@
new Configuration(oldAppResources.getConfiguration());
final DisplayMetrics oldApplicationMetrics = new DisplayMetrics();
oldApplicationMetrics.setTo(oldAppResources.getDisplayMetrics());
- assertEquals("Process config must match the top activity config by default",
+ assertEquals("Process config must match the top activity config by default"
+ + ", activity=" + oldActivityConfig + ", app=" + oldAppConfig,
0, oldActivityConfig.diffPublicOnly(oldAppConfig));
assertEquals("Process config must match the top activity config by default",
oldActivityMetrics, oldApplicationMetrics);
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index 10452fd..bc69901 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -110,6 +110,7 @@
public Intent makeBroadcastIntent(String action) {
Intent intent = new Intent(action, null);
intent.putExtra("caller", mCallTarget);
+ intent.setPackage(getContext().getPackageName());
return intent;
}
@@ -179,7 +180,8 @@
public void registerMyReceiver(IntentFilter filter, String permission) {
mReceiverRegistered = true;
//System.out.println("Registering: " + mReceiver);
- getContext().registerReceiver(mReceiver, filter, permission, null);
+ getContext().registerReceiver(mReceiver, filter, permission, null,
+ Context.RECEIVER_EXPORTED);
}
public void unregisterMyReceiver() {
@@ -255,7 +257,7 @@
}
@FlakyTest
- public void testMulti() throws Exception {
+ public void ignore_testMulti() throws Exception {
runLaunchpad(LaunchpadActivity.BROADCAST_MULTI);
}
@@ -278,8 +280,11 @@
Bundle map = new Bundle();
map.putString("foo", "you");
map.putString("remove", "me");
+ final Intent intent = new Intent(
+ "com.android.frameworks.coretests.activity.BROADCAST_RESULT")
+ .setPackage(getContext().getPackageName());
getContext().sendOrderedBroadcast(
- new Intent("com.android.frameworks.coretests.activity.BROADCAST_RESULT"),
+ intent,
null, broadcastReceiver, null, 1, "foo", map);
while (!broadcastReceiver.mHaveResult) {
try {
@@ -313,7 +318,7 @@
addIntermediate("finished-broadcast");
IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
- Intent sticky = getContext().registerReceiver(null, filter);
+ Intent sticky = getContext().registerReceiver(null, filter, Context.RECEIVER_EXPORTED);
assertNotNull("Sticky not found", sticky);
assertEquals(LaunchpadActivity.DATA_1, sticky.getStringExtra("test"));
}
@@ -329,7 +334,7 @@
addIntermediate("finished-unbroadcast");
IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
- Intent sticky = getContext().registerReceiver(null, filter);
+ Intent sticky = getContext().registerReceiver(null, filter, Context.RECEIVER_EXPORTED);
assertNull("Sticky not found", sticky);
}
@@ -343,7 +348,7 @@
addIntermediate("finished-broadcast");
IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
- Intent sticky = getContext().registerReceiver(null, filter);
+ Intent sticky = getContext().registerReceiver(null, filter, Context.RECEIVER_EXPORTED);
assertNotNull("Sticky not found", sticky);
assertEquals(LaunchpadActivity.DATA_2, sticky.getStringExtra("test"));
}
@@ -371,7 +376,7 @@
runLaunchpad(LaunchpadActivity.BROADCAST_STICKY2);
}
- public void testRegisteredReceivePermissionGranted() throws Exception {
+ public void ignore_testRegisteredReceivePermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_REG});
registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), PERMISSION_GRANTED);
addIntermediate("after-register");
@@ -396,7 +401,7 @@
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
- public void testRegisteredBroadcastPermissionGranted() throws Exception {
+ public void ignore_testRegisteredBroadcastPermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_REG});
registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), null);
addIntermediate("after-register");
@@ -430,7 +435,7 @@
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
- public void testLocalReceivePermissionDenied() throws Exception {
+ public void ignore_testLocalReceivePermissionDenied() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_RESULTS});
BroadcastReceiver finish = new BroadcastReceiver() {
@@ -446,7 +451,7 @@
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
- public void testLocalBroadcastPermissionGranted() throws Exception {
+ public void ignore_testLocalBroadcastPermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
getContext().sendBroadcast(
makeBroadcastIntent(BROADCAST_LOCAL),
@@ -476,7 +481,7 @@
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
- public void testRemoteReceivePermissionDenied() throws Exception {
+ public void ignore_testRemoteReceivePermissionDenied() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_RESULTS});
BroadcastReceiver finish = new BroadcastReceiver() {
@@ -492,7 +497,7 @@
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
- public void testRemoteBroadcastPermissionGranted() throws Exception {
+ public void ignore_testRemoteBroadcastPermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_REMOTE});
getContext().sendBroadcast(
makeBroadcastIntent(BROADCAST_REMOTE),
@@ -516,7 +521,7 @@
waitForResultOrThrow(BROADCAST_TIMEOUT);
}
- public void testReceiverCanNotRegister() throws Exception {
+ public void ignore_testReceiverCanNotRegister() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_LOCAL});
getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_REGISTER));
waitForResultOrThrow(BROADCAST_TIMEOUT);
diff --git a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
index 1b52f80..a0645ce 100644
--- a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
+++ b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
@@ -27,7 +27,7 @@
@LargeTest
public class IntentSenderTest extends BroadcastTest {
- public void testRegisteredReceivePermissionGranted() throws Exception {
+ public void ignore_testRegisteredReceivePermissionGranted() throws Exception {
setExpectedReceivers(new String[]{RECEIVER_REG});
registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), PERMISSION_GRANTED);
addIntermediate("after-register");
@@ -71,7 +71,7 @@
is.cancel();
}
- public void testLocalReceivePermissionDenied() throws Exception {
+ public void ignore_testLocalReceivePermissionDenied() throws Exception {
final Intent intent = makeBroadcastIntent(BROADCAST_LOCAL_DENIED)
.setPackage(getContext().getPackageName());
diff --git a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
index 7662456..fda249f 100644
--- a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
+++ b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
@@ -438,6 +438,7 @@
private Intent makeBroadcastIntent(String action) {
Intent intent = new Intent(action, null);
intent.putExtra("caller", mCallTarget);
+ intent.setPackage(getPackageName());
return intent;
}
@@ -466,7 +467,7 @@
private void registerMyReceiver(IntentFilter filter) {
mReceiverRegistered = true;
//System.out.println("Registering: " + mReceiver);
- registerReceiver(mReceiver, filter);
+ registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
}
private void unregisterMyReceiver() {
diff --git a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
index 2120a1d..3271b8f 100644
--- a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
+++ b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
@@ -19,11 +19,11 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.os.RemoteException;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.RemoteException;
-class LocalDeniedReceiver extends BroadcastReceiver {
+public class LocalDeniedReceiver extends BroadcastReceiver {
public LocalDeniedReceiver() {
}
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
index a84ac55..55ded9c 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -136,7 +136,11 @@
NotificationListenerService.RankingMap retrievedRankings =
retrievedRankingUpdate.getRankingMap();
assertNotNull(retrievedRankings);
- assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed());
+ // The rankingUpdate file descriptor is only non-null in the new path.
+ if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
+ SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
+ assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed());
+ }
NotificationListenerService.Ranking retrievedRanking =
new NotificationListenerService.Ranking();
assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking));
diff --git a/core/tests/coretests/src/android/service/quicksettings/TileTest.java b/core/tests/coretests/src/android/service/quicksettings/TileTest.java
new file mode 100644
index 0000000..ca6c3b4
--- /dev/null
+++ b/core/tests/coretests/src/android/service/quicksettings/TileTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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 android.service.quicksettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TileTest {
+
+ private static final String DEFAULT_LABEL = "DEFAULT_LABEL";
+ private static final String CUSTOM_APP_LABEL = "CUSTOM_LABEL";
+
+ @Test
+ public void testGetLabel_labelSet_usesCustomLabel() {
+ Tile tile = new Tile();
+ tile.setDefaultLabel(DEFAULT_LABEL);
+ tile.setLabel(CUSTOM_APP_LABEL);
+
+ assertThat(tile.getLabel()).isEqualTo(CUSTOM_APP_LABEL);
+ }
+
+ @Test
+ public void testGetLabel_labelNotSet_usesDefaultLabel() {
+ Tile tile = new Tile();
+ tile.setDefaultLabel(DEFAULT_LABEL);
+
+ assertThat(tile.getLabel()).isEqualTo(DEFAULT_LABEL);
+ }
+
+ @Test
+ public void testGetCustomLabel_labelSet() {
+ Tile tile = new Tile();
+ tile.setDefaultLabel(DEFAULT_LABEL);
+ tile.setLabel(CUSTOM_APP_LABEL);
+
+ assertThat(tile.getCustomLabel()).isEqualTo(CUSTOM_APP_LABEL);
+ }
+
+ @Test
+ public void testGetCustomLabel_labelNotSet() {
+ Tile tile = new Tile();
+ tile.setDefaultLabel(DEFAULT_LABEL);
+
+ assertThat(tile.getCustomLabel()).isNull();
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java b/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java
new file mode 100644
index 0000000..60a0a2a
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewGroupGetChildLocalHitRegionTest.java
@@ -0,0 +1,216 @@
+/*
+ * 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 android.view;
+
+import static com.google.common.truth.Truth.assertThat;
+
+
+import android.graphics.Matrix;
+import android.graphics.Region;
+import android.platform.test.annotations.Presubmit;
+import android.widget.LinearLayout;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.filters.SmallTest;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Test basic functions of ViewGroup.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:ViewGroupTest
+ */
+@Presubmit
+@SmallTest
+public class ViewGroupGetChildLocalHitRegionTest {
+ @Rule
+ public ActivityScenarioRule<ViewGroupTestActivity> mScenarioRule =
+ new ActivityScenarioRule<>(ViewGroupTestActivity.class);
+
+ private LinearLayout mRoot;
+ private final int[] mRootLocation = new int[2];
+
+ @Before
+ public void setup() {
+ mScenarioRule.getScenario().onActivity(activity -> {
+ mRoot = activity.findViewById(R.id.linear_layout);
+ mRoot.getLocationInWindow(mRootLocation);
+ });
+ }
+
+ @Test
+ public void testGetChildLocalHitRegion() {
+ assertGetChildLocalHitRegion(R.id.view);
+ }
+
+ @Test
+ public void testGetChildLocalHitRegion_withScale() {
+ assertGetChildLocalHitRegion(R.id.view_scale);
+ }
+
+ @Test
+ public void testGetChildLocalHitRegion_withTranslate() {
+ assertGetChildLocalHitRegion(R.id.view_translate);
+ }
+
+ @Test
+ public void testGetChildLocalHitRegion_overlap_noMotionEvent() {
+ assertGetChildLocalHitRegion(R.id.view_overlap_bottom);
+ }
+ @Test
+ public void testGetChildLocalHitRegion_overlap_withMotionEvent() {
+ // In this case, view_cover_bottom is partially covered by the view_cover_top.
+ // The returned region is the bounds of the bottom view subtract the bounds of the top view.
+ assertGetChildLocalHitRegion(R.id.view_overlap_top, R.id.view_overlap_bottom);
+ }
+
+ @Test
+ public void testGetChildLocalHitRegion_cover_withMotionEvent() {
+ // In this case, view_cover_bottom is completely covered by the view_cover_top.
+ // The returned region is expected to be empty.
+ assertGetChildLocalHitRegionEmpty(R.id.view_cover_top, R.id.view_cover_bottom);
+ }
+
+ private void injectMotionEvent(View view, boolean isHover) {
+ int[] location = new int[2];
+ view.getLocationInWindow(location);
+
+ float x = location[0] + view.getWidth() / 2f;
+ float y = location[1] + view.getHeight() / 2f;
+
+ int action = isHover ? MotionEvent.ACTION_HOVER_ENTER : MotionEvent.ACTION_DOWN;
+ MotionEvent motionEvent = MotionEvent.obtain(/* downtime= */ 0, /* eventTime= */ 0, action,
+ x, y, /* pressure= */ 0, /* size= */ 0, /* metaState= */ 0,
+ /* xPrecision= */ 1, /* yPrecision= */ 1, /* deviceId= */0, /* edgeFlags= */0);
+
+ View rootView = view.getRootView();
+ rootView.dispatchPointerEvent(motionEvent);
+ }
+
+ private void assertGetChildLocalHitRegion(int viewId) {
+ assertGetChildLocalHitRegion(viewId, /* isHover= */ true);
+ assertGetChildLocalHitRegion(viewId, /* isHover= */ false);
+ }
+
+ /**
+ * Assert ViewParent#getChildLocalHitRegion for a single view.
+ * @param viewId the viewId of the tested view.
+ * @param isHover if true, check the hit region of the hover events. Otherwise, check the hit
+ * region of the touch events.
+ */
+ private void assertGetChildLocalHitRegion(int viewId, boolean isHover) {
+ mScenarioRule.getScenario().onActivity(activity -> {
+ View view = activity.findViewById(viewId);
+
+ Matrix actualMatrix = new Matrix();
+ Region actualRegion = new Region(0, 0, view.getWidth(), view.getHeight());
+ boolean actualNotEmpty = view.getParent()
+ .getChildLocalHitRegion(view, actualRegion, actualMatrix, isHover);
+
+ int[] windowLocation = new int[2];
+ view.getLocationInWindow(windowLocation);
+ Matrix expectMatrix = new Matrix();
+ expectMatrix.preScale(1 / view.getScaleX(), 1 / view.getScaleY());
+ expectMatrix.preTranslate(-windowLocation[0], -windowLocation[1]);
+
+ Region expectRegion = new Region(0, 0, view.getWidth(), view.getHeight());
+
+ assertThat(actualNotEmpty).isTrue();
+ assertThat(actualMatrix).isEqualTo(expectMatrix);
+ assertThat(actualRegion).isEqualTo(expectRegion);
+ });
+ }
+
+ private void assertGetChildLocalHitRegion(int viewIdTop, int viewIdBottom) {
+ assertGetChildLocalHitRegion(viewIdTop, viewIdBottom, /* isHover= */ true);
+ assertGetChildLocalHitRegion(viewIdTop, viewIdBottom, /* isHover= */ false);
+ }
+
+ /**
+ * Assert ViewParent#getChildLocalHitRegion of a view that is covered by another view. It will
+ * inject {@link MotionEvent}s to the view on top first and then get the hit region of the
+ * bottom view.
+ *
+ * @param viewIdTop the view id of the test view on top.
+ * @param viewIdBottom the view id of the test view at the bottom.
+ * @param isHover if true, check the hit region of the hover events. Otherwise, check the hit
+ * region of the touch events.
+ */
+ private void assertGetChildLocalHitRegion(int viewIdTop, int viewIdBottom, boolean isHover) {
+ mScenarioRule.getScenario().onActivity(activity -> {
+ View viewTop = activity.findViewById(viewIdTop);
+ View viewBottom = activity.findViewById(viewIdBottom);
+
+ injectMotionEvent(viewTop, isHover);
+
+ Matrix actualMatrix = new Matrix();
+ Region actualRegion = new Region(0, 0, viewBottom.getWidth(), viewBottom.getHeight());
+ boolean actualNotEmpty = viewBottom.getParent()
+ .getChildLocalHitRegion(viewBottom, actualRegion, actualMatrix, isHover);
+
+ int[] windowLocation = new int[2];
+ viewBottom.getLocationInWindow(windowLocation);
+ Matrix expectMatrix = new Matrix();
+ expectMatrix.preTranslate(-windowLocation[0], -windowLocation[1]);
+
+ Region expectRegion = new Region(0, 0, viewBottom.getWidth(), viewBottom.getHeight());
+ expectRegion.op(0, 0, viewTop.getWidth(), viewTop.getHeight(), Region.Op.DIFFERENCE);
+
+ assertThat(actualNotEmpty).isTrue();
+ assertThat(actualMatrix).isEqualTo(expectMatrix);
+ assertThat(actualRegion).isEqualTo(expectRegion);
+ });
+ }
+
+ private void assertGetChildLocalHitRegionEmpty(int viewIdTop, int viewIdBottom) {
+ assertGetChildLocalHitRegionEmpty(viewIdTop, viewIdBottom, /* isHover= */ true);
+ assertGetChildLocalHitRegionEmpty(viewIdTop, viewIdBottom, /* isHover= */ false);
+ }
+
+ /**
+ * Assert ViewParent#getChildLocalHitRegion returns an empty region for a view that is
+ * completely covered by another view. It will inject {@link MotionEvent}s to the view on top
+ * first and then get the hit region of the
+ * bottom view.
+ *
+ * @param viewIdTop the view id of the test view on top.
+ * @param viewIdBottom the view id of the test view at the bottom.
+ * @param isHover if true, check the hit region of the hover events. Otherwise, check the hit
+ * region of the touch events.
+ */
+ private void assertGetChildLocalHitRegionEmpty(int viewIdTop, int viewIdBottom,
+ boolean isHover) {
+ mScenarioRule.getScenario().onActivity(activity -> {
+ View viewTop = activity.findViewById(viewIdTop);
+ View viewBottom = activity.findViewById(viewIdBottom);
+
+ injectMotionEvent(viewTop, isHover);
+
+ Region actualRegion = new Region(0, 0, viewBottom.getWidth(), viewBottom.getHeight());
+ boolean actualNotEmpty = viewBottom.getParent()
+ .getChildLocalHitRegion(viewBottom, actualRegion, new Matrix(), isHover);
+
+ assertThat(actualNotEmpty).isFalse();
+ assertThat(actualRegion.isEmpty()).isTrue();
+ });
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ViewGroupTestActivity.java b/core/tests/coretests/src/android/view/ViewGroupTestActivity.java
new file mode 100644
index 0000000..b94bda5
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewGroupTestActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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 android.view;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
+
+public class ViewGroupTestActivity extends Activity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.viewgroup_test);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java b/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java
index 388a996..b4c72ca 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingTestUtil.java
@@ -21,7 +21,9 @@
import android.app.Instrumentation;
import android.content.Context;
+import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.Region;
import android.view.View;
import android.view.ViewGroup;
@@ -45,7 +47,7 @@
float handwritingBoundsOffsetRight, float handwritingBoundsOffsetBottom) {
final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
final Context context = instrumentation.getTargetContext();
- // mock a parent so that HandwritingInitiator can get
+ // mock a parent so that HandwritingInitiator can get visible rect and hit region.
final ViewGroup parent = new ViewGroup(context) {
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
@@ -56,6 +58,14 @@
r.set(handwritingArea);
return true;
}
+
+ @Override
+ public boolean getChildLocalHitRegion(View child, Region region, Matrix matrix,
+ boolean isHover) {
+ matrix.reset();
+ region.set(handwritingArea);
+ return true;
+ }
};
View view = spy(new View(context));
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 38c3aa0..2cbeaa1 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -35,8 +35,9 @@
LongArrayMultiStateCounterTest.class,
LongMultiStateCounterTest.class,
PowerProfileTest.class,
+ PowerStatsTest.class,
EnergyConsumerStatsTest.class
})
public class BatteryStatsTests {
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
new file mode 100644
index 0000000..29da231
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+import android.os.Parcel;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PowerStatsTest {
+
+ private PowerStats.DescriptorRegistry mRegistry;
+ private PowerStats.Descriptor mDescriptor;
+
+ @Before
+ public void setup() {
+ mRegistry = new PowerStats.DescriptorRegistry();
+ PersistableBundle extras = new PersistableBundle();
+ extras.putBoolean("hasPowerMonitor", true);
+ mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, 2, extras);
+ mRegistry.register(mDescriptor);
+ }
+
+ @Test
+ public void parceling_compatibleParcel() {
+ PowerStats stats = new PowerStats(mDescriptor);
+ stats.durationMs = 1234;
+ stats.stats[0] = 10;
+ stats.stats[1] = 20;
+ stats.stats[2] = 30;
+ stats.uidStats.put(42, new long[]{40, 50});
+ stats.uidStats.put(99, new long[]{60, 70});
+
+ Parcel parcel = Parcel.obtain();
+ mDescriptor.writeSummaryToParcel(parcel);
+ stats.writeToParcel(parcel);
+ parcel.writeString("END");
+
+ Parcel newParcel = marshallAndUnmarshall(parcel);
+
+ PowerStats.Descriptor newDescriptor =
+ PowerStats.Descriptor.readSummaryFromParcel(newParcel);
+ assertThat(newDescriptor.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU);
+ assertThat(newDescriptor.name).isEqualTo("cpu");
+ assertThat(newDescriptor.statsArrayLength).isEqualTo(3);
+ assertThat(newDescriptor.uidStatsArrayLength).isEqualTo(2);
+ assertThat(newDescriptor.extras.getBoolean("hasPowerMonitor")).isTrue();
+
+ mRegistry.register(newDescriptor);
+
+ PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry);
+ assertThat(newStats.durationMs).isEqualTo(1234);
+ assertThat(newStats.stats).isEqualTo(new long[]{10, 20, 30});
+ assertThat(newStats.uidStats.size()).isEqualTo(2);
+ assertThat(newStats.uidStats.get(42)).isEqualTo(new long[]{40, 50});
+ assertThat(newStats.uidStats.get(99)).isEqualTo(new long[]{60, 70});
+
+ String end = newParcel.readString();
+ assertThat(end).isEqualTo("END");
+ }
+
+ @Test
+ public void parceling_unrecognizedPowerComponent() {
+ PowerStats stats = new PowerStats(
+ new PowerStats.Descriptor(777, "luck", 3, 2, new PersistableBundle()));
+ stats.durationMs = 1234;
+
+ Parcel parcel = Parcel.obtain();
+ stats.writeToParcel(parcel);
+ parcel.writeString("END");
+
+ Parcel newParcel = marshallAndUnmarshall(parcel);
+
+ PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry);
+ assertThat(newStats).isNull();
+
+ String end = newParcel.readString();
+ assertThat(end).isEqualTo("END");
+ }
+
+ @Test
+ public void parceling_wrongArrayLength() {
+ PowerStats stats = new PowerStats(mDescriptor);
+ stats.stats = new long[5]; // Is expected to be 3
+
+ Parcel parcel = Parcel.obtain();
+ stats.writeToParcel(parcel);
+ parcel.writeString("END");
+
+ Parcel newParcel = marshallAndUnmarshall(parcel);
+
+ PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry);
+ assertThat(newStats).isNull();
+
+ String end = newParcel.readString();
+ assertThat(end).isEqualTo("END");
+ }
+
+ private static Parcel marshallAndUnmarshall(Parcel parcel) {
+ byte[] bytes = parcel.marshall();
+ parcel.recycle();
+
+ Parcel newParcel = Parcel.obtain();
+ newParcel.unmarshall(bytes, 0, bytes.length);
+ newParcel.setDataPosition(0);
+ return newParcel;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java b/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java
index 1513654..a978e3b 100644
--- a/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/security/VerityUtilsTest.java
@@ -229,11 +229,12 @@
@Test
public void testSignatureGeneratedExternally() throws Exception {
var context = InstrumentationRegistry.getInstrumentation().getContext();
- byte[] cert = getClass().getClassLoader().getResourceAsStream("ApkVerityTestCert.der")
+ byte[] cert = getClass().getClassLoader().getResourceAsStream("unit_test.der")
.readAllBytes();
// The signature is generated by:
- // fsverity sign <(echo -n fs-verity) fsverity_sig --key=ApkVerityTestKey.pem \
- // --cert=ApkVerityTestCert.pem
+ // openssl pkcs8 -topk8 -nocrypt -in certs/unit_test.pk8 -out certs/unit_test.key.pem
+ // fsverity sign <(echo -n fs-verity) fsverity_sig --key=certs/unit_test.key.pem \
+ // --cert=certs/unit_test.x509.pem
byte[] sig = context.getResources().openRawResource(R.raw.fsverity_sig).readAllBytes();
// The fs-verity digest is generated by:
// fsverity digest --compact <(echo -n fs-verity)
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index 8be489e..e875875 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -41,8 +41,6 @@
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
-import androidx.test.InstrumentationRegistry;
-
import com.android.internal.R;
import org.jetbrains.annotations.NotNull;
@@ -838,31 +836,29 @@
@Test
public void testAreVibrationFeaturesSupported_allSegmentsSupported() {
- Vibrator vibrator =
- createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
- .build());
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build();
assertTrue(VibrationEffect.createWaveform(
/* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(VibrationEffect.createWaveform(
/* timings= */ new long[] {1, 2, 3},
/* amplitudes= */ new int[] {10, 20, 40},
/* repeatIndex= */ 2)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(
VibrationEffect.startComposition()
.addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
.repeatEffectIndefinitely(TEST_ONE_SHOT)
.compose()
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testAreVibrationFeaturesSupported_withUnsupportedSegments() {
- Vibrator vibrator =
- createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1).build());
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1).build();
assertFalse(
VibrationEffect.startComposition()
@@ -872,7 +868,7 @@
/* amplitudes= */ new int[] {10, 20, 40},
/* repeatIndex= */ -1))
.compose()
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
@@ -996,13 +992,4 @@
return context;
}
-
- private Vibrator createVibratorWithCustomInfo(VibratorInfo info) {
- return new SystemVibrator(InstrumentationRegistry.getContext()) {
- @Override
- public VibratorInfo getInfo() {
- return info;
- }
- };
- }
}
diff --git a/core/tests/vibrator/src/android/os/VibratorInfoTest.java b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
index ff917aa..73cd464 100644
--- a/core/tests/vibrator/src/android/os/VibratorInfoTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
@@ -57,6 +57,17 @@
}
@Test
+ public void testHasFrequencyControl() {
+ VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+ assertFalse(noCapabilities.hasFrequencyControl());
+ VibratorInfo composeAndFrequencyControl = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setCapabilities(
+ IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
+ .build();
+ assertTrue(composeAndFrequencyControl.hasFrequencyControl());
+ }
+
+ @Test
public void testHasCapabilities() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
@@ -246,8 +257,13 @@
@Test
public void testEquals() {
- VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID);
+ // Create a builder with a different ID, but same properties the same as the first one.
+ VibratorInfo.Builder completeBuilder2 = new VibratorInfo.Builder(TEST_VIBRATOR_ID + 2);
+
+ for (VibratorInfo.Builder builder :
+ new VibratorInfo.Builder[] {completeBuilder, completeBuilder2}) {
+ builder.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.setPrimitiveDelayMax(100)
@@ -257,31 +273,43 @@
.setPwleSizeMax(20)
.setQFactor(2f)
.setFrequencyProfile(TEST_FREQUENCY_PROFILE);
+ }
VibratorInfo complete = completeBuilder.build();
assertEquals(complete, complete);
+ assertTrue(complete.equalContent(complete));
assertEquals(complete, completeBuilder.build());
+ assertTrue(complete.equalContent(completeBuilder.build()));
assertEquals(complete.hashCode(), completeBuilder.build().hashCode());
+ // The infos from the two builders should have equal content, but should not be equal due to
+ // their different IDs.
+ assertNotEquals(complete, completeBuilder2.build());
+ assertTrue(complete.equalContent(completeBuilder2.build()));
+
VibratorInfo completeWithComposeControl = completeBuilder
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.build();
assertNotEquals(complete, completeWithComposeControl);
+ assertFalse(complete.equalContent(completeWithComposeControl));
VibratorInfo completeWithNoEffects = completeBuilder
.setSupportedEffects(new int[0])
.build();
assertNotEquals(complete, completeWithNoEffects);
+ assertFalse(complete.equalContent(completeWithNoEffects));
VibratorInfo completeWithUnknownEffects = completeBuilder
.setSupportedEffects(null)
.build();
assertNotEquals(complete, completeWithUnknownEffects);
+ assertFalse(complete.equalContent(completeWithUnknownEffects));
VibratorInfo completeWithDifferentPrimitiveDuration = completeBuilder
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
.build();
assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
+ assertFalse(complete.equalContent(completeWithDifferentPrimitiveDuration));
VibratorInfo completeWithDifferentFrequencyProfile = completeBuilder
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
@@ -291,31 +319,37 @@
TEST_AMPLITUDE_MAP))
.build();
assertNotEquals(complete, completeWithDifferentFrequencyProfile);
+ assertFalse(complete.equalContent(completeWithDifferentFrequencyProfile));
VibratorInfo completeWithEmptyFrequencyProfile = completeBuilder
.setFrequencyProfile(EMPTY_FREQUENCY_PROFILE)
.build();
assertNotEquals(complete, completeWithEmptyFrequencyProfile);
+ assertFalse(complete.equalContent(completeWithEmptyFrequencyProfile));
VibratorInfo completeWithUnknownQFactor = completeBuilder.setQFactor(Float.NaN).build();
assertNotEquals(complete, completeWithUnknownQFactor);
+ assertFalse(complete.equalContent(completeWithUnknownQFactor));
VibratorInfo completeWithDifferentQFactor = completeBuilder
.setQFactor(complete.getQFactor() + 3f)
.build();
assertNotEquals(complete, completeWithDifferentQFactor);
+ assertFalse(complete.equalContent(completeWithDifferentQFactor));
VibratorInfo unknownEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
VibratorInfo knownEmptyEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setSupportedEffects(new int[0])
.build();
assertNotEquals(unknownEffectSupport, knownEmptyEffectSupport);
+ assertFalse(unknownEffectSupport.equalContent(knownEmptyEffectSupport));
VibratorInfo unknownBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
VibratorInfo knownEmptyBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setSupportedBraking(new int[0])
.build();
assertNotEquals(unknownBrakingSupport, knownEmptyBrakingSupport);
+ assertFalse(unknownBrakingSupport.equalContent(knownEmptyBrakingSupport));
}
@Test
@@ -335,4 +369,186 @@
assertEquals(original, restored);
assertEquals(original.hashCode(), restored.hashCode());
}
+
+ @Test
+ public void areVibrationFeaturesSupported_noAmplitudeControl_fractionalAmplitudeUnsupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1).build();
+
+ // Have at least one fractional amplitude (amplitude not min (0) or max (255) or DEFAULT).
+ assertFalse(info.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30)));
+ assertFalse(info.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 255)));
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 40)));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_noAmplitudeControl_nonFractionalAmplitudeSupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1).build();
+
+ // All amplitudes are min, max, or default. Requires no amplitude control.
+ assertTrue(info.areVibrationFeaturesSupported(
+ waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 255)));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_withAmplitudeControl_allWaveformsSupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build();
+
+ // All forms of amplitudes are valid when amplitude control is available.
+ assertTrue(info.areVibrationFeaturesSupported(
+ waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)));
+ assertTrue(info.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30, 50)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ waveformWithAmplitudes(7, 255, 0, 0, 60)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 255)));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.createOneShot(20, /* amplitude= */ 40)));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_compositionsWithSupportedPrimitivesSupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose()));
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ /* scale= */ 0.2f,
+ /* delay= */ 200)
+ .compose()));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_compositionsWithUnupportedPrimitivesUnsupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .compose()));
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
+ .compose()));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_composedEffects_allComponentsSupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP)
+ .build();
+
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
+ .compose()));
+
+ info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_CLICK)
+ .build();
+
+ assertTrue(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .addEffect(VibrationEffect.createWaveform(
+ // These timings are given either 0 or default amplitudes, which
+ // do not require vibrator's amplitude control.
+ /* timings= */ new long[] {1, 2, 3},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
+ .compose()));
+ }
+
+ @Test
+ public void areVibrationFeaturesSupported_composedEffects_someComponentsUnupported() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP)
+ .build();
+
+ // Not supported due to the TICK primitive, which the vibrator has no support for.
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .compose()));
+ // Not supported due to the THUD effect, which the vibrator has no support for.
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_THUD))
+ .compose()));
+
+ info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10)
+ .setSupportedEffects(VibrationEffect.EFFECT_POP)
+ .build();
+
+ // Not supported due to fractional amplitudes (amplitudes not min (0) or max (255) or
+ // DEFAULT), because the vibrator has no amplitude control.
+ assertFalse(info.areVibrationFeaturesSupported(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .addEffect(VibrationEffect.createWaveform(
+ /* timings= */ new long[] {1, 2, 3},
+ /* amplitudes= */ new int[] {10, 20, 255},
+ /* repeatIndex= */ -1))
+ .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
+ .compose()));
+ }
+
+ private static VibrationEffect waveformWithAmplitudes(int...amplitudes) {
+ long[] timings = new long[amplitudes.length];
+ for (int i = 0; i < timings.length; i++) {
+ timings[i] = i * 2; // Arbitrary timings.
+ }
+ return VibrationEffect.createWaveform(timings, amplitudes, /* repeatIndex= */ -1);
+ }
}
diff --git a/core/tests/vibrator/src/android/os/VibratorTest.java b/core/tests/vibrator/src/android/os/VibratorTest.java
index c559e34..cfa12bb 100644
--- a/core/tests/vibrator/src/android/os/VibratorTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorTest.java
@@ -37,7 +37,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
-import android.hardware.vibrator.IVibrator;
import android.media.AudioAttributes;
import android.os.test.TestLooper;
@@ -60,8 +59,6 @@
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
- private static final float TEST_TOLERANCE = 1e-5f;
-
private Context mContextSpy;
private Vibrator mVibratorSpy;
private TestLooper mTestLooper;
@@ -79,9 +76,6 @@
@Test
public void getId_returnsDefaultId() {
assertEquals(-1, mVibratorSpy.getId());
- assertEquals(-1, new SystemVibrator.NoVibratorInfo().getId());
- assertEquals(-1, new SystemVibrator.MultiVibratorInfo(new VibratorInfo[] {
- VibratorInfo.EMPTY_VIBRATOR_INFO, VibratorInfo.EMPTY_VIBRATOR_INFO }).getId());
}
@Test
@@ -95,53 +89,6 @@
}
@Test
- public void areEffectsSupported_noVibrator_returnsAlwaysNo() {
- VibratorInfo info = new SystemVibrator.NoVibratorInfo();
- assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
- info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
- }
-
- @Test
- public void areEffectsSupported_unsupportedInOneVibrator_returnsNo() {
- VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
- .build();
- VibratorInfo unsupportedVibrator = new VibratorInfo.Builder(/* id= */ 2)
- .setSupportedEffects(new int[0])
- .build();
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
- assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
- info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
- }
-
- @Test
- public void areEffectsSupported_unknownInOneVibrator_returnsUnknown() {
- VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
- .build();
- VibratorInfo unknownSupportVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{supportedVibrator, unknownSupportVibrator});
- assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
- info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
- }
-
- @Test
- public void arePrimitivesSupported_supportedInAllVibrators_returnsYes() {
- VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
- .build();
- VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
- .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
- .build();
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, secondVibrator});
- assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
- info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
- }
-
- @Test
public void arePrimitivesSupported_returnsArrayOfSameSize() {
assertEquals(0, mVibratorSpy.arePrimitivesSupported(new int[0]).length);
assertEquals(1, mVibratorSpy.arePrimitivesSupported(
@@ -152,39 +99,6 @@
}
@Test
- public void arePrimitivesSupported_noVibrator_returnsAlwaysFalse() {
- VibratorInfo info = new SystemVibrator.NoVibratorInfo();
- assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
- }
-
- @Test
- public void arePrimitivesSupported_unsupportedInOneVibrator_returnsFalse() {
- VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
- .build();
- VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
- assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
- }
-
- @Test
- public void arePrimitivesSupported_supportedInAllVibrators_returnsTrue() {
- VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
- .build();
- VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 15)
- .build();
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, secondVibrator});
- assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
- }
-
- @Test
public void getPrimitivesDurations_returnsArrayOfSameSize() {
assertEquals(0, mVibratorSpy.getPrimitiveDurations(new int[0]).length);
assertEquals(1, mVibratorSpy.getPrimitiveDurations(
@@ -195,245 +109,6 @@
}
@Test
- public void getPrimitivesDurations_noVibrator_returnsAlwaysZero() {
- VibratorInfo info = new SystemVibrator.NoVibratorInfo();
- assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
- }
-
- @Test
- public void getPrimitivesDurations_unsupportedInOneVibrator_returnsZero() {
- VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
- .build();
- VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
- assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
- }
-
- @Test
- public void getPrimitivesDurations_supportedInAllVibrators_returnsMaxDuration() {
- VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
- .build();
- VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
- .build();
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, secondVibrator});
- assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
- }
-
- @Test
- public void getQFactorAndResonantFrequency_noVibrator_returnsNaN() {
- VibratorInfo info = new SystemVibrator.NoVibratorInfo();
-
- assertTrue(Float.isNaN(info.getQFactor()));
- assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
- }
-
- @Test
- public void getQFactorAndResonantFrequency_differentValues_returnsNaN() {
- VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setQFactor(1f)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
- .build();
- VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setQFactor(2f)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 2, 2, null))
- .build();
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, secondVibrator});
-
- assertTrue(Float.isNaN(info.getQFactor()));
- assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
- assertEmptyFrequencyProfileAndControl(info);
-
- // One vibrator with values undefined.
- VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3).build();
- info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, thirdVibrator});
-
- assertTrue(Float.isNaN(info.getQFactor()));
- assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
- assertEmptyFrequencyProfileAndControl(info);
- }
-
- @Test
- public void getQFactorAndResonantFrequency_sameValues_returnsValue() {
- VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setQFactor(10f)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(
- /* resonantFrequencyHz= */ 11, 10, 0.5f, null))
- .build();
- VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setQFactor(10f)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(
- /* resonantFrequencyHz= */ 11, 5, 1, null))
- .build();
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, secondVibrator});
-
- assertEquals(10f, info.getQFactor(), TEST_TOLERANCE);
- assertEquals(11f, info.getResonantFrequencyHz(), TEST_TOLERANCE);
-
- // No frequency range defined.
- assertTrue(info.getFrequencyProfile().isEmpty());
- assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
- }
-
- @Test
- public void getFrequencyProfile_noVibrator_returnsEmpty() {
- VibratorInfo info = new SystemVibrator.NoVibratorInfo();
-
- assertEmptyFrequencyProfileAndControl(info);
- }
-
- @Test
- public void getFrequencyProfile_differentResonantFrequencyOrResolutionValues_returnsEmpty() {
- VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
- new float[] { 0, 1 }))
- .build();
- VibratorInfo differentResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 1, 1,
- new float[] { 0, 1 }))
- .build();
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, differentResonantFrequency});
-
- assertEmptyFrequencyProfileAndControl(info);
-
- VibratorInfo differentFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 2,
- new float[] { 0, 1 }))
- .build();
- info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, differentFrequencyResolution});
-
- assertEmptyFrequencyProfileAndControl(info);
- }
-
- @Test
- public void getFrequencyProfile_missingValues_returnsEmpty() {
- VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
- new float[] { 0, 1 }))
- .build();
- VibratorInfo missingResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(Float.NaN, 1, 1,
- new float[] { 0, 1 }))
- .build();
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, missingResonantFrequency});
-
- assertEmptyFrequencyProfileAndControl(info);
-
- VibratorInfo missingMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, Float.NaN, 1,
- new float[] { 0, 1 }))
- .build();
- info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, missingMinFrequency});
-
- assertEmptyFrequencyProfileAndControl(info);
-
- VibratorInfo missingFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, Float.NaN,
- new float[] { 0, 1 }))
- .build();
- info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, missingFrequencyResolution});
-
- assertEmptyFrequencyProfileAndControl(info);
-
- VibratorInfo missingMaxAmplitudes = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
- .build();
- info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, missingMaxAmplitudes});
-
- assertEmptyFrequencyProfileAndControl(info);
- }
-
- @Test
- public void getFrequencyProfile_unalignedMaxAmplitudes_returnsEmpty() {
- VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
- new float[] { 0, 1, 1, 0 }))
- .build();
- VibratorInfo unalignedMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.1f, 0.5f,
- new float[] { 0, 1, 1, 0 }))
- .build();
- VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
- new float[] { 0, 1, 1, 0 }))
- .build();
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, unalignedMinFrequency, thirdVibrator});
-
- assertEmptyFrequencyProfileAndControl(info);
- }
-
- @Test
- public void getFrequencyProfile_alignedProfiles_returnsIntersection() {
- VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
- new float[] { 0.5f, 1, 1, 0.5f }))
- .build();
- VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
- new float[] { 1, 1, 1 }))
- .build();
- VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3)
- .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
- new float[] { 0.8f, 1, 0.8f, 0.5f }))
- .build();
- VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, secondVibrator, thirdVibrator});
-
- assertEquals(
- new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }),
- info.getFrequencyProfile());
- assertEquals(true, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
-
- // Third vibrator without frequency control capability.
- thirdVibrator = new VibratorInfo.Builder(/* id= */ 3)
- .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
- new float[] { 0.8f, 1, 0.8f, 0.5f }))
- .build();
- info = new SystemVibrator.MultiVibratorInfo(
- new VibratorInfo[]{firstVibrator, secondVibrator, thirdVibrator});
-
- assertEquals(
- new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }),
- info.getFrequencyProfile());
- assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
- }
-
- @Test
public void onVibratorStateChanged_noVibrator_registersNoListenerToVibratorManager() {
VibratorManager mockVibratorManager = mock(VibratorManager.class);
when(mockVibratorManager.getVibratorIds()).thenReturn(new int[0]);
@@ -577,212 +252,4 @@
VibrationAttributes vibrationAttributes = captor.getValue();
assertEquals(new VibrationAttributes.Builder().build(), vibrationAttributes);
}
-
- @Test
- public void areVibrationFeaturesSupported_noAmplitudeControl_fractionalAmplitudes() {
- Vibrator vibrator =
- createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedEffects(VibrationEffect.EFFECT_THUD)
- .build());
-
- // Have at least one fractional amplitude (amplitude not min (0) or max (255) or DEFAULT).
- assertFalse(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30)));
- assertFalse(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 255)));
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, /* amplitude= */ 40)));
- }
-
- @Test
- public void areVibrationFeaturesSupported_noAmplitudeControl_nonFractionalAmplitudes() {
- Vibrator vibrator =
- createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedEffects(VibrationEffect.EFFECT_THUD)
- .build());
-
- // All amplitudes are min, max, or default. Requires no amplitude control.
- assertTrue(vibrator.areVibrationFeaturesSupported(
- waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, /* amplitude= */ 255)));
- }
-
- @Test
- public void areVibrationFeaturesSupported_withAmplitudeControl() {
- Vibrator vibrator =
- createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
- .build());
-
- // All forms of amplitudes are valid when amplitude control is available.
- assertTrue(vibrator.areVibrationFeaturesSupported(
- waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1)));
- assertTrue(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30, 50)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- waveformWithAmplitudes(7, 255, 0, 0, 60)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, /* amplitude= */ 255)));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.createOneShot(20, /* amplitude= */ 40)));
- }
-
- @Test
- public void areVibrationFeaturesSupported_primitiveCompositionsWithSupportedPrimitives() {
- Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
- .build());
-
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .compose()));
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(
- VibrationEffect.Composition.PRIMITIVE_CLICK,
- /* scale= */ 0.2f,
- /* delay= */ 200)
- .compose()));
- }
-
- @Test
- public void areVibrationFeaturesSupported_primitiveCompositionsWithUnupportedPrimitives() {
- Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
- .build());
-
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
- .compose()));
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
- .compose()));
- }
-
- @Test
- public void areVibrationFeaturesSupported_composedEffects_allComponentsSupported() {
- Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
- .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP)
- .build());
-
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .addEffect(VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3},
- /* amplitudes= */ new int[] {10, 20, 255},
- /* repeatIndex= */ -1))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
- .compose()));
-
- vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10)
- .setSupportedEffects(VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_CLICK)
- .build());
-
- assertTrue(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
- .addEffect(VibrationEffect.createWaveform(
- // These timings are given either 0 or default amplitudes, which
- // do not require vibrator's amplitude control.
- /* timings= */ new long[] {1, 2, 3},
- /* repeatIndex= */ -1))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK))
- .compose()));
- }
-
- @Test
- public void areVibrationFeaturesSupported_composedEffects_someComponentsUnupported() {
- Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
- .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP)
- .build());
-
- // Not supported due to the TICK primitive, which the vibrator has no support for.
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
- .addEffect(VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3},
- /* amplitudes= */ new int[] {10, 20, 255},
- /* repeatIndex= */ -1))
- .compose()));
- // Not supported due to the THUD effect, which the vibrator has no support for.
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .addEffect(VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3},
- /* amplitudes= */ new int[] {10, 20, 255},
- /* repeatIndex= */ -1))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_THUD))
- .compose()));
-
- vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10)
- .setSupportedEffects(VibrationEffect.EFFECT_POP)
- .build());
-
- // Not supported due to fractional amplitudes (amplitudes not min (0) or max (255) or
- // DEFAULT), because the vibrator has no amplitude control.
- assertFalse(vibrator.areVibrationFeaturesSupported(
- VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
- .addEffect(VibrationEffect.createWaveform(
- /* timings= */ new long[] {1, 2, 3},
- /* amplitudes= */ new int[] {10, 20, 255},
- /* repeatIndex= */ -1))
- .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP))
- .compose()));
- }
-
- /**
- * Asserts that the frequency profile is empty, and therefore frequency control isn't supported.
- */
- void assertEmptyFrequencyProfileAndControl(VibratorInfo info) {
- assertTrue(info.getFrequencyProfile().isEmpty());
- assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
- }
-
- private Vibrator createVibratorWithCustomInfo(VibratorInfo info) {
- return new SystemVibrator(mContextSpy) {
- @Override
- public VibratorInfo getInfo() {
- return info;
- }
- };
- }
-
- private static VibrationEffect waveformWithAmplitudes(int...amplitudes) {
- long[] timings = new long[amplitudes.length];
- for (int i = 0; i < timings.length; i++) {
- timings[i] = i * 2; // Arbitrary timings.
- }
- return VibrationEffect.createWaveform(timings, amplitudes, /* repeatIndex= */ -1);
- }
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java b/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java
new file mode 100644
index 0000000..fc31ac4
--- /dev/null
+++ b/core/tests/vibrator/src/android/os/vibrator/MultiVibratorInfoTest.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright 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 android.os.vibrator;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertEquals;
+
+import android.hardware.vibrator.IVibrator;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.os.VibratorInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MultiVibratorInfoTest {
+ private static final float TEST_TOLERANCE = 1e-5f;
+
+ @Test
+ public void testGetId() {
+ VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
+ VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2)
+ .setSupportedEffects(new int[0])
+ .build();
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 3,
+ new VibratorInfo[]{firstInfo, secondInfo});
+
+ assertEquals(3, info.getId());
+ }
+
+ @Test
+ public void testIsEffectSupported_supportedInAllVibrators_returnsYes() {
+ VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
+ VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK)
+ .build();
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, secondInfo});
+
+ assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
+ info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+ }
+
+ @Test
+ public void testIsEffectSupported_unsupportedInOneVibrator_returnsNo() {
+ VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
+ VibratorInfo unsupportedVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setSupportedEffects(new int[0])
+ .build();
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
+
+ assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
+ info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+ }
+
+ @Test
+ public void testIsEffectSupported_unknownInOneVibrator_returnsUnknown() {
+ VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
+ VibratorInfo unknownSupportVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{supportedVibrator, unknownSupportVibrator});
+ assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
+ info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+ }
+
+ @Test
+ public void testIsPrimitiveSupported_unsupportedInOneVibrator_returnsFalse() {
+ VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+ VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
+
+ assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ }
+
+ @Test
+ public void testIsPrimitiveSupported_supportedInAllVibrators_returnsTrue() {
+ VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+ .build();
+ VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 15)
+ .build();
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, secondInfo});
+
+ assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ }
+
+ @Test
+ public void testGetPrimitiveDuration_unsupportedInOneVibrator_returnsZero() {
+ VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+ VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
+
+ assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ }
+
+ @Test
+ public void testGetPrimitiveDuration_supportedInAllVibrators_returnsMaxDuration() {
+ VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+ VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .build();
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, secondInfo});
+
+ assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ }
+
+ @Test
+ public void testGetQFactorAndResonantFrequency_differentValues_returnsNaN() {
+ VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setQFactor(1f)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
+ .build();
+ VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setQFactor(2f)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 2, 2, null))
+ .build();
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, secondInfo});
+
+ assertTrue(Float.isNaN(info.getQFactor()));
+ assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
+ assertEmptyFrequencyProfileAndControl(info);
+
+ // One vibrator with values undefined.
+ VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3).build();
+ info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, thirdVibrator});
+
+ assertTrue(Float.isNaN(info.getQFactor()));
+ assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
+ assertEmptyFrequencyProfileAndControl(info);
+ }
+
+ @Test
+ public void testGetQFactorAndResonantFrequency_sameValues_returnsValue() {
+ VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setQFactor(10f)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(
+ /* resonantFrequencyHz= */ 11, 10, 0.5f, null))
+ .build();
+ VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setQFactor(10f)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(
+ /* resonantFrequencyHz= */ 11, 5, 1, null))
+ .build();
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, secondInfo});
+
+ assertEquals(10f, info.getQFactor(), TEST_TOLERANCE);
+ assertEquals(11f, info.getResonantFrequencyHz(), TEST_TOLERANCE);
+ // No frequency range defined.
+ assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
+ }
+
+ @Test
+ public void testGetFrequencyProfile_differentResonantFrequencyOrResolutions_returnsEmpty() {
+ VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
+ new float[] { 0, 1 }))
+ .build();
+ VibratorInfo differentResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 1, 1,
+ new float[] { 0, 1 }))
+ .build();
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, differentResonantFrequency});
+
+ assertEmptyFrequencyProfileAndControl(info);
+
+ VibratorInfo differentFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 2,
+ new float[] { 0, 1 }))
+ .build();
+ info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, differentFrequencyResolution});
+
+ assertEmptyFrequencyProfileAndControl(info);
+ }
+
+ @Test
+ public void testGetFrequencyProfile_missingValues_returnsEmpty() {
+ VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
+ new float[] { 0, 1 }))
+ .build();
+ VibratorInfo missingResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(Float.NaN, 1, 1,
+ new float[] { 0, 1 }))
+ .build();
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, missingResonantFrequency});
+
+ assertEmptyFrequencyProfileAndControl(info);
+
+ VibratorInfo missingMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, Float.NaN, 1,
+ new float[] { 0, 1 }))
+ .build();
+ info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, missingMinFrequency});
+
+ assertEmptyFrequencyProfileAndControl(info);
+
+ VibratorInfo missingFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, Float.NaN,
+ new float[] { 0, 1 }))
+ .build();
+ info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, missingFrequencyResolution});
+
+ assertEmptyFrequencyProfileAndControl(info);
+
+ VibratorInfo missingMaxAmplitudes = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
+ .build();
+ info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, missingMaxAmplitudes});
+
+ assertEmptyFrequencyProfileAndControl(info);
+ }
+
+ @Test
+ public void testGetFrequencyProfile_unalignedMaxAmplitudes_returnsEmpty() {
+ VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
+ new float[] { 0, 1, 1, 0 }))
+ .build();
+ VibratorInfo unalignedMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.1f, 0.5f,
+ new float[] { 0, 1, 1, 0 }))
+ .build();
+ VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
+ new float[] { 0, 1, 1, 0 }))
+ .build();
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, unalignedMinFrequency, thirdVibrator});
+
+ assertEmptyFrequencyProfileAndControl(info);
+ }
+
+ @Test
+ public void testGetFrequencyProfile_alignedProfiles_returnsIntersection() {
+ VibratorInfo firstInfo = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
+ new float[] { 0.5f, 1, 1, 0.5f }))
+ .build();
+ VibratorInfo secondInfo = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
+ new float[] { 1, 1, 1 }))
+ .build();
+ VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
+ new float[] { 0.8f, 1, 0.8f, 0.5f }))
+ .build();
+
+ VibratorInfo info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, secondInfo, thirdVibrator});
+
+ assertEquals(
+ new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }),
+ info.getFrequencyProfile());
+ assertEquals(true, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
+
+ // Third vibrator without frequency control capability.
+ thirdVibrator = new VibratorInfo.Builder(/* id= */ 3)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
+ new float[] { 0.8f, 1, 0.8f, 0.5f }))
+ .build();
+ info = new MultiVibratorInfo(/* id= */ 1,
+ new VibratorInfo[]{firstInfo, secondInfo, thirdVibrator});
+
+ assertEquals(
+ new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }),
+ info.getFrequencyProfile());
+ assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
+ }
+
+ /**
+ * Asserts that the frequency profile is empty, and therefore frequency control isn't supported.
+ */
+ private void assertEmptyFrequencyProfileAndControl(VibratorInfo info) {
+ assertTrue(info.getFrequencyProfile().isEmpty());
+ assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL));
+ }
+}
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
index 8268077..4f5f3c0 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -25,18 +25,14 @@
import static org.testng.Assert.assertThrows;
import android.os.Parcel;
-import android.os.SystemVibrator;
import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.os.VibratorInfo;
-import androidx.test.InstrumentationRegistry;
-
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.junit.runners.JUnit4;
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(JUnit4.class)
public class PrebakedSegmentTest {
@Test
@@ -149,121 +145,121 @@
@Test
public void testVibrationFeaturesSupport_idsWithFallback_fallbackEnabled_vibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(
VibrationEffect.EFFECT_TICK,
VibrationEffect.EFFECT_CLICK,
VibrationEffect.EFFECT_DOUBLE_CLICK,
VibrationEffect.EFFECT_HEAVY_CLICK);
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithFallback_fallbackEnabled_noVibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]);
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithFallback_fallbackDisabled_vibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(
VibrationEffect.EFFECT_TICK,
VibrationEffect.EFFECT_CLICK,
VibrationEffect.EFFECT_DOUBLE_CLICK,
VibrationEffect.EFFECT_HEAVY_CLICK);
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithFallback_fallbackDisabled_noVibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]);
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackEnabled_vibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(
VibrationEffect.EFFECT_THUD,
VibrationEffect.EFFECT_POP,
VibrationEffect.EFFECT_TEXTURE_TICK);
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_POP)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackEnabled_noVibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]);
assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_POP)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackDisabled_vibratorSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(
VibrationEffect.EFFECT_THUD,
VibrationEffect.EFFECT_POP,
VibrationEffect.EFFECT_TEXTURE_TICK);
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_THUD)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_POP)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackDisabled_noVibSupport() {
- Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]);
+ VibratorInfo info = createVibratorInfoWithSupportedEffects(new int[0]);
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_THUD)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_POP)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
- .areVibrationFeaturesSupported(vibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
@@ -283,14 +279,9 @@
return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
}
- private static Vibrator createVibratorWithSupportedEffects(int... supportedEffects) {
- return new SystemVibrator(InstrumentationRegistry.getContext()) {
- @Override
- public VibratorInfo getInfo() {
- return new VibratorInfo.Builder(/* id= */ 1)
- .setSupportedEffects(supportedEffects)
- .build();
- }
- };
+ private static VibratorInfo createVibratorInfoWithSupportedEffects(int... supportedEffects) {
+ return new VibratorInfo.Builder(/* id= */ 1)
+ .setSupportedEffects(supportedEffects)
+ .build();
}
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
index 6f5adcd..ec5a084 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -25,18 +25,14 @@
import android.hardware.vibrator.IVibrator;
import android.os.Parcel;
-import android.os.SystemVibrator;
import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.os.VibratorInfo;
-import androidx.test.InstrumentationRegistry;
-
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.junit.runners.JUnit4;
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(JUnit4.class)
public class PrimitiveSegmentTest {
private static final float TOLERANCE = 1e-2f;
@@ -146,15 +142,15 @@
public void testVibrationFeaturesSupport_primitiveSupportedByVibrator() {
assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK)));
assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_THUD)));
assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)));
}
@@ -162,15 +158,15 @@
public void testVibrationFeaturesSupport_primitiveNotSupportedByVibrator() {
assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_THUD)));
assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK)));
assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD)
.areVibrationFeaturesSupported(
- createVibratorWithSupportedPrimitive(
+ createVibratorInfoWithSupportedPrimitive(
VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)));
}
@@ -193,15 +189,10 @@
return new PrimitiveSegment(primitiveId, 0.2f, 10);
}
- private static Vibrator createVibratorWithSupportedPrimitive(int primitiveId) {
- return new SystemVibrator(InstrumentationRegistry.getContext()) {
- @Override
- public VibratorInfo getInfo() {
- return new VibratorInfo.Builder(/* id= */ 1)
- .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(primitiveId, 10)
- .build();
- }
- };
+ private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId) {
+ return new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(primitiveId, 10)
+ .build();
}
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
index 68870e5..5caa86b 100644
--- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
@@ -23,31 +23,21 @@
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
+import android.hardware.vibrator.IVibrator;
import android.os.Parcel;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.junit.MockitoRule;
+import org.junit.runners.JUnit4;
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(JUnit4.class)
public class RampSegmentTest {
private static final float TOLERANCE = 1e-2f;
- @Rule
- public MockitoRule mMockitoRule = MockitoJUnit.rule();
-
- @Mock
- private Vibrator mVibrator;
-
@Test
public void testCreation() {
RampSegment ramp = new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
@@ -147,71 +137,71 @@
@Test
public void testVibrationFeaturesSupport_amplitudeAndFrequencyControls_supported() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
// Increasing amplitude
- assertTrue(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertTrue(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(info));
// Increasing frequency
- assertTrue(new RampSegment(0.5f, 0.5f, 0, 1, 10).areVibrationFeaturesSupported(mVibrator));
+ assertTrue(new RampSegment(0.5f, 0.5f, 0, 1, 10).areVibrationFeaturesSupported(info));
// Decreasing amplitude
- assertTrue(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertTrue(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(info));
// Decreasing frequency
- assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 10).areVibrationFeaturesSupported(info));
// Zero duration
- assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 0).areVibrationFeaturesSupported(mVibrator));
+ assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 0).areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_noAmplitudeControl_unsupportedForChangingAmplitude() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true);
// Test with increasing/decreasing amplitudes.
- assertFalse(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_noAmplitudeControl_fractionalAmplitudeUnsupported() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true);
- assertFalse(new RampSegment(0.2f, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(0, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(0.2f, 0, 0, 0, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0.2f, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(0, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(0.2f, 0, 0, 0, 10).areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_unchangingZeroAmplitude_supported() {
RampSegment amplitudeZeroWithIncreasingFrequency = new RampSegment(1, 1, 0.5f, 0.8f, 10);
RampSegment amplitudeZeroWithDecreasingFrequency = new RampSegment(1, 1, 0.8f, 0.5f, 10);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true);
- assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
- assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_unchangingOneAmplitude_supported() {
RampSegment amplitudeOneWithIncreasingFrequency = new RampSegment(1, 1, 0.5f, 0.8f, 10);
RampSegment amplitudeOneWithDecreasingFrequency = new RampSegment(1, 1, 0.8f, 0.5f, 10);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true);
- assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
- assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(info));
}
@Test
@@ -220,52 +210,52 @@
new RampSegment(DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, 0.5f, 0.8f, 10);
RampSegment defaultAmplitudeDecreasingFrequency =
new RampSegment(DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, 0.8f, 0.5f, 10);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ true);
- assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
- assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(mVibrator));
- assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(info));
+ assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_noFrequencyControl_unsupportedForChangingFrequency() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
- when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ false);
// Test with increasing/decreasing frequencies.
- assertFalse(new RampSegment(0, 0, 0.2f, 0.4f, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(0, 0, 0.4f, 0.2f, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0, 0, 0.2f, 0.4f, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(0, 0, 0.4f, 0.2f, 10).areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_noFrequencyControl_fractionalFrequencyUnsupported() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
- when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ false);
- assertFalse(new RampSegment(0, 0, 0.2f, 0.2f, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(0, 0, 0.2f, 0, 10).areVibrationFeaturesSupported(mVibrator));
- assertFalse(new RampSegment(0, 0, 0, 0.2f, 10).areVibrationFeaturesSupported(mVibrator));
+ assertFalse(new RampSegment(0, 0, 0.2f, 0.2f, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(0, 0, 0.2f, 0, 10).areVibrationFeaturesSupported(info));
+ assertFalse(new RampSegment(0, 0, 0, 0.2f, 10).areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_unchangingZeroFrequency_supported() {
RampSegment frequencyZeroWithIncreasingAmplitude = new RampSegment(0.1f, 1, 0, 0, 10);
RampSegment frequencyZeroWithDecreasingAmplitude = new RampSegment(1, 0.1f, 0, 0, 10);
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
- when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ VibratorInfo info =
+ createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ false);
- assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
- assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(info));
+ assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(info));
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ info = createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
- assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
- assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(info));
+ assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(info));
}
@Test
@@ -274,4 +264,17 @@
// duration checked in VibrationEffect implementations.
assertTrue(new RampSegment(0.5f, 1, 0, 0, 5_000).isHapticFeedbackCandidate());
}
+
+ private static VibratorInfo createVibInfo(
+ boolean hasAmplitudeControl, boolean hasFrequencyControl) {
+ VibratorInfo.Builder builder = new VibratorInfo.Builder(/* id= */ 1);
+ long capabilities = 0;
+ if (hasAmplitudeControl) {
+ capabilities |= IVibrator.CAP_AMPLITUDE_CONTROL;
+ }
+ if (hasFrequencyControl) {
+ capabilities |= (IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ }
+ return builder.setCapabilities(capabilities).build();
+ }
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
index 34bb892..44db306 100644
--- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
@@ -21,31 +21,20 @@
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
+import android.hardware.vibrator.IVibrator;
import android.os.Parcel;
import android.os.VibrationEffect;
-import android.os.Vibrator;
+import android.os.VibratorInfo;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.junit.MockitoRule;
+import org.junit.runners.JUnit4;
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(JUnit4.class)
public class StepSegmentTest {
private static final float TOLERANCE = 1e-2f;
-
- @Rule
- public MockitoRule mMockitoRule = MockitoJUnit.rule();
-
- @Mock
- private Vibrator mVibrator;
-
@Test
public void testCreation() {
StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequencyHz= */ 1f,
@@ -160,26 +149,26 @@
public void testVibrationFeaturesSupport_zeroAmplitude_supported() {
StepSegment segment =
new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 0);
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_maxAmplitude_supported() {
StepSegment segment =
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 0);
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
}
@Test
@@ -189,60 +178,60 @@
/* amplitude= */ VibrationEffect.DEFAULT_AMPLITUDE,
/* frequencyHz= */ 0,
/* duration= */ 0);
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_fractionalAmplitude_hasAmplitudeCtrl_supported() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(true);
+ VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true);
assertTrue(new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 0, /* duration= */ 0)
- .areVibrationFeaturesSupported(mVibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_fractionalAmplitude_hasNoAmplitudeCtrl_notSupported() {
- when(mVibrator.hasAmplitudeControl()).thenReturn(false);
+ VibratorInfo info = createVibInfoForAmplitude(/* hasAmplitudeControl= */ false);
assertFalse(new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 0, /* duration= */ 0)
- .areVibrationFeaturesSupported(mVibrator));
+ .areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_zeroFrequency_supported() {
StepSegment segment =
new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0, /* duration= */ 0);
- when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ VibratorInfo info = createVibInfoForFrequency(/* hasFrequencyControl= */ false);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ info = createVibInfoForFrequency(/* hasFrequencyControl= */ true);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_nonZeroFrequency_hasFrequencyCtrl_supported() {
StepSegment segment =
new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0.2f, /* duration= */ 0);
- when(mVibrator.hasFrequencyControl()).thenReturn(true);
+ VibratorInfo info = createVibInfoForFrequency(/* hasFrequencyControl= */ true);
- assertTrue(segment.areVibrationFeaturesSupported(mVibrator));
+ assertTrue(segment.areVibrationFeaturesSupported(info));
}
@Test
public void testVibrationFeaturesSupport_nonZeroFrequency_hasNoFrequencyCtrl_notSupported() {
StepSegment segment =
new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0.2f, /* duration= */ 0);
- when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ VibratorInfo info = createVibInfoForFrequency(/* hasFrequencyControl= */ false);
- assertFalse(segment.areVibrationFeaturesSupported(mVibrator));
+ assertFalse(segment.areVibrationFeaturesSupported(info));
}
@Test
@@ -251,4 +240,21 @@
// duration checked in VibrationEffect implementations.
assertTrue(new StepSegment(0, 0, 5_000).isHapticFeedbackCandidate());
}
+
+ private static VibratorInfo createVibInfoForAmplitude(boolean hasAmplitudeControl) {
+ VibratorInfo.Builder builder = new VibratorInfo.Builder(/* id= */ 1);
+ if (hasAmplitudeControl) {
+ builder.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ }
+ return builder.build();
+ }
+
+ private static VibratorInfo createVibInfoForFrequency(boolean hasFrequencyControl) {
+ VibratorInfo.Builder builder = new VibratorInfo.Builder(/* id= */ 1);
+ if (hasFrequencyControl) {
+ builder.setCapabilities(
+ IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ }
+ return builder.build();
+ }
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/VibratorInfoFactoryTest.java b/core/tests/vibrator/src/android/os/vibrator/VibratorInfoFactoryTest.java
new file mode 100644
index 0000000..df4822f
--- /dev/null
+++ b/core/tests/vibrator/src/android/os/vibrator/VibratorInfoFactoryTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 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 android.os.vibrator;
+
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertEquals;
+
+import android.hardware.vibrator.IVibrator;
+import android.os.VibrationEffect;
+import android.os.VibratorInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class VibratorInfoFactoryTest {
+
+ @Test
+ public void testCreatedInfo_hasTheRequestedId() {
+ // Empty info list.
+ VibratorInfo infoFromEmptyInfos =
+ VibratorInfoFactory.create(/* id= */ 3, new VibratorInfo[] {});
+ VibratorInfo info1 = new VibratorInfo.Builder(/* id= */ 1)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
+ VibratorInfo info2 = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+ VibratorInfo infoFromOneInfo =
+ VibratorInfoFactory.create(/* id= */ -1, new VibratorInfo[] {info1});
+ VibratorInfo infoFromTwoInfos =
+ VibratorInfoFactory.create(/* id= */ -3, new VibratorInfo[] {info1, info2});
+
+ assertEquals(3, infoFromEmptyInfos.getId());
+ assertEquals(-1, infoFromOneInfo.getId());
+ assertEquals(-3, infoFromTwoInfos.getId());
+ }
+
+ @Test
+ public void testCreatedInfo_fromEmptyVibratorInfos_returnsEmptyVibratorInfo() {
+ VibratorInfo info = VibratorInfoFactory.create(/* id= */ 2, new VibratorInfo[] {});
+
+ assertEqualContent(VibratorInfo.EMPTY_VIBRATOR_INFO, info);
+ }
+
+ @Test
+ public void testCreatedInfo_fromSingleVibratorInfo_hasEqualContent() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_FREQUENCY_CONTROL)
+ .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_THUD)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 30)
+ .build();
+
+ VibratorInfo createdInfo =
+ VibratorInfoFactory.create(/* id= */ -1, new VibratorInfo[] {info});
+
+ assertEqualContent(info, createdInfo);
+ }
+
+ @Test
+ public void testCreatedInfo_hasEqualContentRegardlessOfSourceInfoOrder() {
+ VibratorInfo info1 = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_FREQUENCY_CONTROL)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
+ VibratorInfo info2 = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+
+ assertEqualContent(
+ VibratorInfoFactory.create(/* id= */ -1, new VibratorInfo[] {info1, info2}),
+ VibratorInfoFactory.create(/* id= */ -1, new VibratorInfo[] {info2, info1}));
+ }
+
+ @Test
+ public void testCreatedInfoContents() {
+ VibratorInfo info1 = new VibratorInfo.Builder(/* id= */ -1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_FREQUENCY_CONTROL)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_POP)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 5)
+ .build();
+ VibratorInfo info2 = new VibratorInfo.Builder(/* id= */ -2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL)
+ .setSupportedEffects(VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_THUD)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 20)
+ .build();
+ VibratorInfo info3 = new VibratorInfo.Builder(/* id= */ -3)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build();
+
+ assertEquals(
+ new VibratorInfo.Builder(/* id= */ 3)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedEffects(VibrationEffect.EFFECT_POP)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 20)
+ .build(),
+ VibratorInfoFactory.create(/* id= */ 3, new VibratorInfo[] {info1, info2}));
+ assertEquals(
+ new VibratorInfo.Builder(/* id= */ 3)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build(),
+ VibratorInfoFactory.create(/* id= */ 3, new VibratorInfo[] {info2, info3}));
+ assertEquals(
+ new VibratorInfo.Builder(/* id= */ 3).build(),
+ VibratorInfoFactory.create(/* id= */ 3, new VibratorInfo[] {info1, info3}));
+ }
+
+ private static void assertEqualContent(VibratorInfo info1, VibratorInfo info2) {
+ assertTrue(info1.equalContent(info2));
+ }
+}
diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java
index f639521..b5fb13d 100644
--- a/graphics/java/android/graphics/Gainmap.java
+++ b/graphics/java/android/graphics/Gainmap.java
@@ -124,8 +124,6 @@
/**
* Creates a new gainmap using the provided gainmap as the metadata source and the provided
* bitmap as the replacement for the gainmapContents
- * TODO: Make public, it's useful
- * @hide
*/
public Gainmap(@NonNull Gainmap gainmap, @NonNull Bitmap gainmapContents) {
this(gainmapContents, nCreateCopy(gainmap.mNativePtr));
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
new file mode 100644
index 0000000..a0a06f1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<com.android.wm.shell.common.bubbles.BubblePopupView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginHorizontal="@dimen/bubble_popup_margin_horizontal"
+ android:layout_marginTop="@dimen/bubble_popup_margin_top"
+ android:elevation="@dimen/bubble_manage_menu_elevation"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:tint="?android:attr/colorAccent"
+ android:contentDescription="@null"
+ android:src="@drawable/pip_ic_settings"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:maxWidth="@dimen/bubble_popup_content_max_width"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Headline"
+ android:textColor="?android:attr/textColorPrimary"
+ android:text="@string/bubble_bar_education_manage_title"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:maxWidth="@dimen/bubble_popup_content_max_width"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAlignment="center"
+ android:text="@string/bubble_bar_education_manage_text"/>
+
+</com.android.wm.shell.common.bubbles.BubblePopupView>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 597e899d..20bf81d 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -226,6 +226,20 @@
<dimen name="bubble_user_education_padding_end">58dp</dimen>
<!-- Padding between the bubble and the user education text. -->
<dimen name="bubble_user_education_stack_padding">16dp</dimen>
+ <!-- Max width for the bubble popup view. -->
+ <dimen name="bubble_popup_content_max_width">300dp</dimen>
+ <!-- Horizontal margin for the bubble popup view. -->
+ <dimen name="bubble_popup_margin_horizontal">32dp</dimen>
+ <!-- Top margin for the bubble popup view. -->
+ <dimen name="bubble_popup_margin_top">16dp</dimen>
+ <!-- Width for the bubble popup view arrow. -->
+ <dimen name="bubble_popup_arrow_width">12dp</dimen>
+ <!-- Height for the bubble popup view arrow. -->
+ <dimen name="bubble_popup_arrow_height">10dp</dimen>
+ <!-- Corner radius for the bubble popup view arrow. -->
+ <dimen name="bubble_popup_arrow_corner_radius">2dp</dimen>
+ <!-- Padding for the bubble popup view contents. -->
+ <dimen name="bubble_popup_padding">24dp</dimen>
<!-- The size of the caption bar inset at the top of bubble bar expanded view. -->
<dimen name="bubble_bar_expanded_view_caption_height">32dp</dimen>
<!-- The height of the dots shown for the caption menu in the bubble bar expanded view.. -->
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 8cbc3d0..00c63d7 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -163,6 +163,11 @@
<!-- [CHAR LIMIT=NONE] Empty overflow subtitle -->
<string name="bubble_overflow_empty_subtitle">Recent bubbles and dismissed bubbles will appear here</string>
+ <!-- Title text for the bubble bar "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=60]-->
+ <string name="bubble_bar_education_manage_title">Control bubbles anytime</string>
+ <!-- Descriptive text for the bubble bar "manage" button tool tip highlighting where users can go to control bubble settings. [CHAR LIMIT=80]-->
+ <string name="bubble_bar_education_manage_text">Tap here to manage which apps and conversations can bubble</string>
+
<!-- [CHAR LIMIT=100] Notification Importance title -->
<string name="notification_bubble_title">Bubble</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 7e09c98..ff67110 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -60,7 +60,6 @@
/**
* Encapsulates the data and UI elements of a bubble.
*/
-@VisibleForTesting
public class Bubble implements BubbleViewProvider {
private static final String TAG = "Bubble";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEducationController.kt
new file mode 100644
index 0000000..e57f02c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleEducationController.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.bubbles
+
+import android.content.Context
+import android.util.Log
+import androidx.core.content.edit
+import com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION
+import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES
+import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME
+
+/** Manages bubble education flags. Provides convenience methods to check the education state */
+class BubbleEducationController(private val context: Context) {
+ private val prefs = context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
+
+ /** Whether the user has seen the stack education */
+ @get:JvmName(name = "hasSeenStackEducation")
+ var hasSeenStackEducation: Boolean
+ get() = prefs.getBoolean(PREF_STACK_EDUCATION, false)
+ set(value) = prefs.edit { putBoolean(PREF_STACK_EDUCATION, value) }
+
+ /** Whether the user has seen the expanded view "manage" menu education */
+ @get:JvmName(name = "hasSeenManageEducation")
+ var hasSeenManageEducation: Boolean
+ get() = prefs.getBoolean(PREF_MANAGED_EDUCATION, false)
+ set(value) = prefs.edit { putBoolean(PREF_MANAGED_EDUCATION, value) }
+
+ /** Whether education view should show for the collapsed stack. */
+ fun shouldShowStackEducation(bubble: BubbleViewProvider?): Boolean {
+ val shouldShow = bubble != null &&
+ bubble.isConversationBubble && // show education for conversation bubbles only
+ (!hasSeenStackEducation || BubbleDebugConfig.forceShowUserEducation(context))
+ logDebug("Show stack edu: $shouldShow")
+ return shouldShow
+ }
+
+ /** Whether the educational view should show for the expanded view "manage" menu. */
+ fun shouldShowManageEducation(bubble: BubbleViewProvider?): Boolean {
+ val shouldShow = bubble != null &&
+ bubble.isConversationBubble && // show education for conversation bubbles only
+ (!hasSeenManageEducation || BubbleDebugConfig.forceShowUserEducation(context))
+ logDebug("Show manage edu: $shouldShow")
+ return shouldShow
+ }
+
+ private fun logDebug(message: String) {
+ if (DEBUG_USER_EDUCATION) {
+ Log.d(TAG, message)
+ }
+ }
+
+ companion object {
+ private val TAG = if (TAG_WITH_CLASS_NAME) "BubbleEducationController" else TAG_BUBBLES
+ const val PREF_STACK_EDUCATION: String = "HasSeenBubblesOnboarding"
+ const val PREF_MANAGED_EDUCATION: String = "HasSeenBubblesManageOnboarding"
+ }
+}
+
+/** Convenience extension method to check if the bubble is a conversation bubble */
+private val BubbleViewProvider.isConversationBubble: Boolean
+ get() = if (this is Bubble) isConversation else false
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
new file mode 100644
index 0000000..bdb09e1
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.bubbles
+
+import android.graphics.Color
+import com.android.wm.shell.R
+import com.android.wm.shell.common.bubbles.BubblePopupDrawable
+import com.android.wm.shell.common.bubbles.BubblePopupView
+
+/**
+ * A convenience method to setup the [BubblePopupView] with the correct config using local resources
+ */
+fun BubblePopupView.setup() {
+ val attrs =
+ context.obtainStyledAttributes(
+ intArrayOf(
+ com.android.internal.R.attr.materialColorSurface,
+ android.R.attr.dialogCornerRadius
+ )
+ )
+
+ val res = context.resources
+ val config =
+ BubblePopupDrawable.Config(
+ color = attrs.getColor(0, Color.WHITE),
+ cornerRadius = attrs.getDimension(1, 0f),
+ contentPadding = res.getDimensionPixelSize(R.dimen.bubble_popup_padding),
+ arrowWidth = res.getDimension(R.dimen.bubble_popup_arrow_width),
+ arrowHeight = res.getDimension(R.dimen.bubble_popup_arrow_height),
+ arrowRadius = res.getDimension(R.dimen.bubble_popup_arrow_corner_radius)
+ )
+ attrs.recycle()
+ setupBackground(config)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index da5974f..8ae12c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -46,7 +46,6 @@
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
-import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Log;
import android.view.Choreographer;
@@ -108,12 +107,6 @@
*/
public class BubbleStackView extends FrameLayout
implements ViewTreeObserver.OnComputeInternalInsetsListener {
-
- // LINT.IfChange
- public static final boolean ENABLE_FLING_TO_DISMISS_BUBBLE =
- SystemProperties.getBoolean("persist.wm.debug.fling_to_dismiss_bubble", true);
- // LINT.ThenChange(com/android/launcher3/taskbar/bubbles/BubbleDismissController.java)
-
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
/** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
@@ -138,7 +131,7 @@
private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
- private static final float SCRIM_ALPHA = 0.6f;
+ private static final float SCRIM_ALPHA = 0.32f;
/** Minimum alpha value for scrim when alpha is being changed via drag */
private static final float MIN_SCRIM_ALPHA_FOR_DRAG = 0.2f;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 33629f9..c20733a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -19,7 +19,6 @@
import static android.view.View.LAYOUT_DIRECTION_RTL;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
-import static com.android.wm.shell.bubbles.BubbleStackView.ENABLE_FLING_TO_DISMISS_BUBBLE;
import android.content.res.Resources;
import android.graphics.Path;
@@ -355,7 +354,6 @@
mMagnetizedBubbleDraggingOut.setMagnetListener(listener);
mMagnetizedBubbleDraggingOut.setHapticsEnabled(true);
mMagnetizedBubbleDraggingOut.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
- mMagnetizedBubbleDraggingOut.setFlingToTargetEnabled(ENABLE_FLING_TO_DISMISS_BUBBLE);
}
private void springBubbleTo(View bubble, float x, float y) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 5533842..4bb1ab4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -17,7 +17,6 @@
package com.android.wm.shell.bubbles.animation;
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
-import static com.android.wm.shell.bubbles.BubbleStackView.ENABLE_FLING_TO_DISMISS_BUBBLE;
import android.content.ContentResolver;
import android.content.res.Resources;
@@ -1026,7 +1025,6 @@
};
mMagnetizedStack.setHapticsEnabled(true);
mMagnetizedStack.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
- mMagnetizedStack.setFlingToTargetEnabled(ENABLE_FLING_TO_DISMISS_BUBBLE);
}
final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 6b6d6ba..79f188a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -39,7 +39,6 @@
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.taskview.TaskView;
-import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -48,6 +47,18 @@
* {@link BubbleController#isShowingAsBubbleBar()}
*/
public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskViewHelper.Listener {
+ /**
+ * The expanded view listener notifying the {@link BubbleBarLayerView} about the internal
+ * actions and events
+ */
+ public interface Listener {
+ /** Called when the task view task is first created. */
+ void onTaskCreated();
+ /** Called when expanded view needs to un-bubble the given conversation */
+ void onUnBubbleConversation(String bubbleKey);
+ /** Called when expanded view task view back button pressed */
+ void onBackPressed();
+ }
private static final String TAG = BubbleBarExpandedView.class.getSimpleName();
private static final int INVALID_TASK_ID = -1;
@@ -57,7 +68,7 @@
private BubbleTaskViewHelper mBubbleTaskViewHelper;
private BubbleBarMenuViewController mMenuViewController;
private @Nullable Supplier<Rect> mLayerBoundsSupplier;
- private @Nullable Consumer<String> mUnBubbleConversationCallback;
+ private @Nullable Listener mListener;
private BubbleBarHandleView mHandleView = new BubbleBarHandleView(getContext());
private @Nullable TaskView mTaskView;
@@ -145,15 +156,13 @@
mMenuViewController.setListener(new BubbleBarMenuViewController.Listener() {
@Override
public void onMenuVisibilityChanged(boolean visible) {
- if (mTaskView == null || mLayerBoundsSupplier == null) return;
- // Updates the obscured touchable region for the task surface.
- mTaskView.setObscuredTouchRect(visible ? mLayerBoundsSupplier.get() : null);
+ setObscured(visible);
}
@Override
public void onUnBubbleConversation(Bubble bubble) {
- if (mUnBubbleConversationCallback != null) {
- mUnBubbleConversationCallback.accept(bubble.getKey());
+ if (mListener != null) {
+ mListener.onUnBubbleConversation(bubble.getKey());
}
}
@@ -231,6 +240,9 @@
public void onTaskCreated() {
setContentVisibility(true);
updateHandleColor(false /* animated */);
+ if (mListener != null) {
+ mListener.onTaskCreated();
+ }
}
@Override
@@ -240,7 +252,8 @@
@Override
public void onBackPressed() {
- mController.collapseStack();
+ if (mListener == null) return;
+ mListener.onBackPressed();
}
/** Cleans up task view, should be called when the bubble is no longer active. */
@@ -254,6 +267,18 @@
mMenuViewController.hideMenu(false /* animated */);
}
+ /**
+ * Hides the current modal menu view or collapses the bubble stack.
+ * Called from {@link BubbleBarLayerView}
+ */
+ public void hideMenuOrCollapse() {
+ if (mMenuViewController.isMenuVisible()) {
+ mMenuViewController.hideMenu(/* animated = */ true);
+ } else {
+ mController.collapseStack();
+ }
+ }
+
/** Updates the bubble shown in the expanded view. */
public void update(Bubble bubble) {
mBubbleTaskViewHelper.update(bubble);
@@ -270,10 +295,16 @@
mLayerBoundsSupplier = supplier;
}
- /** Sets the function to call to un-bubble the given conversation. */
- public void setUnBubbleConversationCallback(
- @Nullable Consumer<String> unBubbleConversationCallback) {
- mUnBubbleConversationCallback = unBubbleConversationCallback;
+ /** Sets expanded view listener */
+ void setListener(@Nullable Listener listener) {
+ mListener = listener;
+ }
+
+ /** Sets whether the view is obscured by some modal view */
+ void setObscured(boolean obscured) {
+ if (mTaskView == null || mLayerBoundsSupplier == null) return;
+ // Updates the obscured touchable region for the task surface.
+ mTaskView.setObscuredTouchRect(obscured ? mLayerBoundsSupplier.get() : null);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index bc04bfc..8f11253 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -52,6 +52,7 @@
private final BubbleController mBubbleController;
private final BubblePositioner mPositioner;
private final BubbleBarAnimationHelper mAnimationHelper;
+ private final BubbleEducationViewController mEducationViewController;
private final View mScrimView;
@Nullable
@@ -80,6 +81,10 @@
mAnimationHelper = new BubbleBarAnimationHelper(context,
this, mPositioner);
+ mEducationViewController = new BubbleEducationViewController(context, (boolean visible) -> {
+ if (mExpandedView == null) return;
+ mExpandedView.setObscured(visible);
+ });
mScrimView = new View(getContext());
mScrimView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
@@ -90,9 +95,7 @@
mScrimView.setBackgroundDrawable(new ColorDrawable(
getResources().getColor(android.R.color.system_neutral1_1000)));
- setOnClickListener(view -> {
- mBubbleController.collapseStack();
- });
+ setOnClickListener(view -> hideMenuOrCollapse());
}
@Override
@@ -108,6 +111,7 @@
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
if (mExpandedView != null) {
+ mEducationViewController.hideManageEducation(/* animated = */ false);
removeView(mExpandedView);
mExpandedView = null;
}
@@ -162,14 +166,27 @@
final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
mExpandedView.setVisibility(GONE);
- mExpandedView.setUnBubbleConversationCallback(mUnBubbleConversationCallback);
+ mExpandedView.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height);
mExpandedView.setLayerBoundsSupplier(() -> new Rect(0, 0, getWidth(), getHeight()));
- mExpandedView.setUnBubbleConversationCallback(bubbleKey -> {
- if (mUnBubbleConversationCallback != null) {
- mUnBubbleConversationCallback.accept(bubbleKey);
+ mExpandedView.setListener(new BubbleBarExpandedView.Listener() {
+ @Override
+ public void onTaskCreated() {
+ mEducationViewController.maybeShowManageEducation(b, mExpandedView);
+ }
+
+ @Override
+ public void onUnBubbleConversation(String bubbleKey) {
+ if (mUnBubbleConversationCallback != null) {
+ mUnBubbleConversationCallback.accept(bubbleKey);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ hideMenuOrCollapse();
}
});
- mExpandedView.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height);
+
addView(mExpandedView, new FrameLayout.LayoutParams(width, height));
}
@@ -193,6 +210,7 @@
public void collapse() {
mIsExpanded = false;
final BubbleBarExpandedView viewToRemove = mExpandedView;
+ mEducationViewController.hideManageEducation(/* animated = */ true);
mAnimationHelper.animateCollapse(() -> removeView(viewToRemove));
mBubbleController.getSysuiProxy().onStackExpandChanged(false);
mExpandedView = null;
@@ -206,6 +224,17 @@
mUnBubbleConversationCallback = unBubbleConversationCallback;
}
+ /** Hides the current modal education/menu view, expanded view or collapses the bubble stack */
+ private void hideMenuOrCollapse() {
+ if (mEducationViewController.isManageEducationVisible()) {
+ mEducationViewController.hideManageEducation(/* animated = */ true);
+ } else if (isExpanded() && mExpandedView != null) {
+ mExpandedView.hideMenuOrCollapse();
+ } else {
+ mBubbleController.collapseStack();
+ }
+ }
+
/** Updates the expanded view size and position. */
private void updateExpandedView() {
if (mExpandedView == null) return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index 8be140c..81e7582 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -56,6 +56,11 @@
SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
}
+ /** Tells if the menu is visible or being animated */
+ boolean isMenuVisible() {
+ return mMenuView != null && mMenuView.getVisibility() == View.VISIBLE;
+ }
+
/** Sets menu actions listener */
void setListener(@Nullable Listener listener) {
mListener = listener;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
new file mode 100644
index 0000000..7b39c6f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -0,0 +1,148 @@
+/*
+ * 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.bubbles.bar
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.core.view.doOnLayout
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.wm.shell.R
+import com.android.wm.shell.animation.PhysicsAnimator
+import com.android.wm.shell.bubbles.BubbleEducationController
+import com.android.wm.shell.bubbles.BubbleViewProvider
+import com.android.wm.shell.bubbles.setup
+import com.android.wm.shell.common.bubbles.BubblePopupView
+
+/** Manages bubble education presentation and animation */
+class BubbleEducationViewController(private val context: Context, private val listener: Listener) {
+ interface Listener {
+ fun onManageEducationVisibilityChanged(isVisible: Boolean)
+ }
+
+ private var rootView: ViewGroup? = null
+ private var educationView: BubblePopupView? = null
+ private var animator: PhysicsAnimator<BubblePopupView>? = null
+
+ private val springConfig by lazy {
+ PhysicsAnimator.SpringConfig(
+ SpringForce.STIFFNESS_MEDIUM,
+ SpringForce.DAMPING_RATIO_LOW_BOUNCY
+ )
+ }
+
+ private val controller by lazy { BubbleEducationController(context) }
+
+ /** Whether the education view is visible or being animated */
+ val isManageEducationVisible: Boolean
+ get() = educationView != null && rootView != null
+
+ /**
+ * Show manage bubble education if hasn't been shown before
+ *
+ * @param bubble the bubble used for the manage education check
+ * @param root the view to show manage education in
+ */
+ fun maybeShowManageEducation(bubble: BubbleViewProvider, root: ViewGroup) {
+ if (!controller.shouldShowManageEducation(bubble)) return
+ showManageEducation(root)
+ }
+
+ /**
+ * Hide the manage education view if visible
+ *
+ * @param animated whether should hide with animation
+ */
+ fun hideManageEducation(animated: Boolean) {
+ rootView?.let {
+ fun cleanUp() {
+ it.removeView(educationView)
+ rootView = null
+ listener.onManageEducationVisibilityChanged(isVisible = false)
+ }
+
+ if (animated) {
+ animateTransition(show = false, ::cleanUp)
+ } else {
+ cleanUp()
+ }
+ }
+ }
+
+ /**
+ * Show manage education with animation
+ *
+ * @param root the view to show manage education in
+ */
+ private fun showManageEducation(root: ViewGroup) {
+ hideManageEducation(animated = false)
+ if (educationView == null) {
+ val eduView = createEducationView(root)
+ educationView = eduView
+ animator = createAnimation(eduView)
+ }
+ root.addView(educationView)
+ rootView = root
+ animateTransition(show = true) {
+ controller.hasSeenManageEducation = true
+ listener.onManageEducationVisibilityChanged(isVisible = true)
+ }
+ }
+
+ /**
+ * Animate show/hide transition for the education view
+ *
+ * @param show whether to show or hide the view
+ * @param endActions a closure to be called when the animation completes
+ */
+ private fun animateTransition(show: Boolean, endActions: () -> Unit) {
+ animator?.let { animator ->
+ animator
+ .spring(DynamicAnimation.ALPHA, if (show) 1f else 0f)
+ .spring(DynamicAnimation.SCALE_X, if (show) 1f else EDU_SCALE_HIDDEN)
+ .spring(DynamicAnimation.SCALE_Y, if (show) 1f else EDU_SCALE_HIDDEN)
+ .withEndActions(endActions)
+ .start()
+ } ?: endActions()
+ }
+
+ private fun createEducationView(root: ViewGroup): BubblePopupView {
+ val view =
+ LayoutInflater.from(context).inflate(R.layout.bubble_bar_manage_education, root, false)
+ as BubblePopupView
+
+ return view.apply {
+ setup()
+ alpha = 0f
+ pivotY = 0f
+ scaleX = EDU_SCALE_HIDDEN
+ scaleY = EDU_SCALE_HIDDEN
+ doOnLayout { it.pivotX = it.width / 2f }
+ setOnClickListener { hideManageEducation(animated = true) }
+ }
+ }
+
+ private fun createAnimation(view: BubblePopupView): PhysicsAnimator<BubblePopupView> {
+ val animator = PhysicsAnimator.getInstance(view)
+ animator.setDefaultSpringConfig(springConfig)
+ return animator
+ }
+
+ companion object {
+ private const val EDU_SCALE_HIDDEN = 0.5f
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
new file mode 100644
index 0000000..8b5283d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
@@ -0,0 +1,232 @@
+/*
+ * 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.common.bubbles
+
+import android.annotation.ColorInt
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.Matrix
+import android.graphics.Outline
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.drawable.Drawable
+import kotlin.math.atan
+import kotlin.math.cos
+import kotlin.math.sin
+import kotlin.properties.Delegates
+
+/** A drawable for the [BubblePopupView] that draws a popup background with a directional arrow */
+class BubblePopupDrawable(private val config: Config) : Drawable() {
+ /** The direction of the arrow in the popup drawable */
+ enum class ArrowDirection {
+ UP,
+ DOWN
+ }
+
+ /** The arrow position on the side of the popup bubble */
+ sealed class ArrowPosition {
+ object Start : ArrowPosition()
+ object Center : ArrowPosition()
+ object End : ArrowPosition()
+ class Custom(val value: Float) : ArrowPosition()
+ }
+
+ /** The configuration for drawable features */
+ data class Config(
+ @ColorInt val color: Int,
+ val cornerRadius: Float,
+ val contentPadding: Int,
+ val arrowWidth: Float,
+ val arrowHeight: Float,
+ val arrowRadius: Float
+ )
+
+ /**
+ * The direction of the arrow in the popup drawable. It affects the content padding and requires
+ * it to be updated in the view.
+ */
+ var arrowDirection: ArrowDirection by
+ Delegates.observable(ArrowDirection.UP) { _, _, _ -> requestPathUpdate() }
+
+ /**
+ * Arrow position along the X axis and its direction. The position is adjusted to the content
+ * corner radius when applied so it doesn't go into rounded corner area
+ */
+ var arrowPosition: ArrowPosition by
+ Delegates.observable(ArrowPosition.Center) { _, _, _ -> requestPathUpdate() }
+
+ private val path = Path()
+ private val paint = Paint()
+ private var shouldUpdatePath = true
+
+ init {
+ paint.color = config.color
+ paint.style = Paint.Style.FILL
+ paint.isAntiAlias = true
+ }
+
+ override fun draw(canvas: Canvas) {
+ updatePathIfNeeded()
+ canvas.drawPath(path, paint)
+ }
+
+ override fun onBoundsChange(bounds: Rect?) {
+ requestPathUpdate()
+ }
+
+ /** Should be applied to the view padding if arrow direction changes */
+ override fun getPadding(padding: Rect): Boolean {
+ padding.set(
+ config.contentPadding,
+ config.contentPadding,
+ config.contentPadding,
+ config.contentPadding
+ )
+ when (arrowDirection) {
+ ArrowDirection.UP -> padding.top += config.arrowHeight.toInt()
+ ArrowDirection.DOWN -> padding.bottom += config.arrowHeight.toInt()
+ }
+ return true
+ }
+
+ override fun getOutline(outline: Outline) {
+ updatePathIfNeeded()
+ outline.setPath(path)
+ }
+
+ override fun getOpacity(): Int {
+ return paint.alpha
+ }
+
+ override fun setAlpha(alpha: Int) {
+ paint.alpha = alpha
+ }
+
+ override fun setColorFilter(colorFilter: ColorFilter?) {
+ paint.colorFilter = colorFilter
+ }
+
+ /** Schedules path update for the next redraw */
+ private fun requestPathUpdate() {
+ shouldUpdatePath = true
+ }
+
+ /** Updates the path if required, when bounds or arrow direction/position changes */
+ private fun updatePathIfNeeded() {
+ if (shouldUpdatePath) {
+ updatePath()
+ shouldUpdatePath = false
+ }
+ }
+
+ /** Updates the path value using the current bounds, config, arrow direction and position */
+ private fun updatePath() {
+ if (bounds.isEmpty) return
+ // Reset the path state
+ path.reset()
+ // The content rect where the filled rounded rect will be drawn
+ val contentRect = RectF(bounds)
+ when (arrowDirection) {
+ ArrowDirection.UP -> {
+ // Add rounded arrow pointing up to the path
+ addRoundedArrowPositioned(path, arrowPosition)
+ // Inset content rect by the arrow size from the top
+ contentRect.top += config.arrowHeight
+ }
+ ArrowDirection.DOWN -> {
+ val matrix = Matrix()
+ // Flip the path with the matrix to draw arrow pointing down
+ matrix.setScale(1f, -1f, bounds.width() / 2f, bounds.height() / 2f)
+ path.transform(matrix)
+ // Add rounded arrow with the flipped matrix applied, will point down
+ addRoundedArrowPositioned(path, arrowPosition)
+ // Restore the path matrix to the original state with inverted matrix
+ matrix.invert(matrix)
+ path.transform(matrix)
+ // Inset content rect by the arrow size from the bottom
+ contentRect.bottom -= config.arrowHeight
+ }
+ }
+ // Add the content area rounded rect
+ path.addRoundRect(contentRect, config.cornerRadius, config.cornerRadius, Path.Direction.CW)
+ }
+
+ /** Add a rounded arrow pointing up in the horizontal position on the canvas */
+ private fun addRoundedArrowPositioned(path: Path, position: ArrowPosition) {
+ val matrix = Matrix()
+ var translationX = positionValue(position) - config.arrowWidth / 2
+ // Offset to position between rounded corners of the content view
+ translationX = translationX.coerceIn(config.cornerRadius,
+ bounds.width() - config.cornerRadius - config.arrowWidth)
+ // Translate to add the arrow in the center horizontally
+ matrix.setTranslate(-translationX, 0f)
+ path.transform(matrix)
+ // Add rounded arrow
+ addRoundedArrow(path)
+ // Restore the path matrix to the original state with inverted matrix
+ matrix.invert(matrix)
+ path.transform(matrix)
+ }
+
+ /** Adds a rounded arrow pointing up to the path, can be flipped if needed */
+ private fun addRoundedArrow(path: Path) {
+ // Theta is half of the angle inside the triangle tip
+ val thetaTan = config.arrowWidth / (config.arrowHeight * 2f)
+ val theta = atan(thetaTan)
+ val thetaDeg = Math.toDegrees(theta.toDouble()).toFloat()
+ // The center Y value of the circle for the triangle tip
+ val tipCircleCenterY = config.arrowRadius / sin(theta)
+ // The length from triangle tip to intersection point with the circle
+ val tipIntersectionSideLength = config.arrowRadius / thetaTan
+ // The offset from the top to the point of intersection
+ val intersectionTopOffset = tipIntersectionSideLength * cos(theta)
+ // The offset from the center to the point of intersection
+ val intersectionCenterOffset = tipIntersectionSideLength * sin(theta)
+ // The center X of the triangle
+ val arrowCenterX = config.arrowWidth / 2f
+
+ // Set initial position in bottom left of the arrow
+ path.moveTo(0f, config.arrowHeight)
+ // Add the left side of the triangle
+ path.lineTo(arrowCenterX - intersectionCenterOffset, intersectionTopOffset)
+ // Add the arc from the left to the right side of the triangle
+ path.arcTo(
+ /* left = */ arrowCenterX - config.arrowRadius,
+ /* top = */ tipCircleCenterY - config.arrowRadius,
+ /* right = */ arrowCenterX + config.arrowRadius,
+ /* bottom = */ tipCircleCenterY + config.arrowRadius,
+ /* startAngle = */ 180 + thetaDeg,
+ /* sweepAngle = */ 180 - (2 * thetaDeg),
+ /* forceMoveTo = */ false
+ )
+ // Add the right side of the triangle
+ path.lineTo(config.arrowWidth, config.arrowHeight)
+ // Close the path
+ path.close()
+ }
+
+ /** The value of the arrow position provided the position and current bounds */
+ private fun positionValue(position: ArrowPosition): Float {
+ return when (position) {
+ is ArrowPosition.Start -> 0f
+ is ArrowPosition.Center -> bounds.width().toFloat() / 2f
+ is ArrowPosition.End -> bounds.width().toFloat()
+ is ArrowPosition.Custom -> position.value
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
new file mode 100644
index 0000000..f8a4946
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.common.bubbles
+
+import android.content.Context
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.widget.LinearLayout
+
+/** A popup container view that uses [BubblePopupDrawable] as a background */
+open class BubblePopupView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+ private var popupDrawable: BubblePopupDrawable? = null
+
+ /**
+ * Sets up the popup drawable with the config provided. Required to remove dependency on local
+ * resources
+ */
+ fun setupBackground(config: BubblePopupDrawable.Config) {
+ popupDrawable = BubblePopupDrawable(config)
+ background = popupDrawable
+ forceLayout()
+ }
+
+ /**
+ * Sets the arrow direction for the background drawable and updates the padding to fit the
+ * content inside of the popup drawable
+ */
+ fun setArrowDirection(direction: BubblePopupDrawable.ArrowDirection) {
+ popupDrawable?.let {
+ it.arrowDirection = direction
+ val padding = Rect()
+ if (it.getPadding(padding)) {
+ setPadding(padding.left, padding.top, padding.right, padding.bottom)
+ }
+ }
+ }
+
+ /** Sets the arrow position for the background drawable and triggers redraw */
+ fun setArrowPosition(position: BubblePopupDrawable.ArrowPosition) {
+ popupDrawable?.let {
+ it.arrowPosition = position
+ invalidate()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
new file mode 100644
index 0000000..2719cd2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
@@ -0,0 +1,365 @@
+/*
+ * 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.common.pip
+
+import android.annotation.DrawableRes
+import android.annotation.StringRes
+import android.app.PendingIntent
+import android.app.RemoteAction
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.graphics.drawable.Icon
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.MediaSessionManager
+import android.media.session.PlaybackState
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.UserHandle
+import com.android.wm.shell.R
+import com.android.wm.shell.pip.PipUtils
+import java.util.function.Consumer
+
+/**
+ * Interfaces with the [MediaSessionManager] to compose the right set of actions to show (only
+ * if there are no actions from the PiP activity itself). The active media controller is only set
+ * when there is a media session from the top PiP activity.
+ */
+class PipMediaController(private val mContext: Context, private val mMainHandler: Handler) {
+ /**
+ * A listener interface to receive notification on changes to the media actions.
+ */
+ interface ActionListener {
+ /**
+ * Called when the media actions changed.
+ */
+ fun onMediaActionsChanged(actions: List<RemoteAction?>?)
+ }
+
+ /**
+ * A listener interface to receive notification on changes to the media metadata.
+ */
+ interface MetadataListener {
+ /**
+ * Called when the media metadata changed.
+ */
+ fun onMediaMetadataChanged(metadata: MediaMetadata?)
+ }
+
+ /**
+ * A listener interface to receive notification on changes to the media session token.
+ */
+ interface TokenListener {
+ /**
+ * Called when the media session token changed.
+ */
+ fun onMediaSessionTokenChanged(token: MediaSession.Token?)
+ }
+
+ private val mHandlerExecutor: HandlerExecutor = HandlerExecutor(mMainHandler)
+ private val mMediaSessionManager: MediaSessionManager?
+ private var mMediaController: MediaController? = null
+ private val mPauseAction: RemoteAction
+ private val mPlayAction: RemoteAction
+ private val mNextAction: RemoteAction
+ private val mPrevAction: RemoteAction
+ private val mMediaActionReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (mMediaController == null) {
+ // no active media session, bail early.
+ return
+ }
+ when (intent.action) {
+ ACTION_PLAY -> mMediaController!!.transportControls.play()
+ ACTION_PAUSE -> mMediaController!!.transportControls.pause()
+ ACTION_NEXT -> mMediaController!!.transportControls.skipToNext()
+ ACTION_PREV -> mMediaController!!.transportControls.skipToPrevious()
+ }
+ }
+ }
+ private val mPlaybackChangedListener: MediaController.Callback =
+ object : MediaController.Callback() {
+ override fun onPlaybackStateChanged(state: PlaybackState?) {
+ notifyActionsChanged()
+ }
+
+ override fun onMetadataChanged(metadata: MediaMetadata?) {
+ notifyMetadataChanged(metadata)
+ }
+ }
+ private val mSessionsChangedListener =
+ MediaSessionManager.OnActiveSessionsChangedListener { controllers: List<MediaController>? ->
+ resolveActiveMediaController(controllers)
+ }
+ private val mActionListeners = ArrayList<ActionListener>()
+ private val mMetadataListeners = ArrayList<MetadataListener>()
+ private val mTokenListeners = ArrayList<TokenListener>()
+
+ init {
+ val mediaControlFilter = IntentFilter()
+ mediaControlFilter.addAction(ACTION_PLAY)
+ mediaControlFilter.addAction(ACTION_PAUSE)
+ mediaControlFilter.addAction(ACTION_NEXT)
+ mediaControlFilter.addAction(ACTION_PREV)
+ mContext.registerReceiverForAllUsers(
+ mMediaActionReceiver, mediaControlFilter,
+ SYSTEMUI_PERMISSION, mMainHandler, Context.RECEIVER_EXPORTED
+ )
+
+ // Creates the standard media buttons that we may show.
+ mPauseAction = getDefaultRemoteAction(
+ R.string.pip_pause,
+ R.drawable.pip_ic_pause_white, ACTION_PAUSE
+ )
+ mPlayAction = getDefaultRemoteAction(
+ R.string.pip_play,
+ R.drawable.pip_ic_play_arrow_white, ACTION_PLAY
+ )
+ mNextAction = getDefaultRemoteAction(
+ R.string.pip_skip_to_next,
+ R.drawable.pip_ic_skip_next_white, ACTION_NEXT
+ )
+ mPrevAction = getDefaultRemoteAction(
+ R.string.pip_skip_to_prev,
+ R.drawable.pip_ic_skip_previous_white, ACTION_PREV
+ )
+ mMediaSessionManager = mContext.getSystemService(
+ MediaSessionManager::class.java
+ )
+ }
+
+ /**
+ * Handles when an activity is pinned.
+ */
+ fun onActivityPinned() {
+ // Once we enter PiP, try to find the active media controller for the top most activity
+ resolveActiveMediaController(
+ mMediaSessionManager!!.getActiveSessionsForUser(
+ null,
+ UserHandle.CURRENT
+ )
+ )
+ }
+
+ /**
+ * Adds a new media action listener.
+ */
+ fun addActionListener(listener: ActionListener) {
+ if (!mActionListeners.contains(listener)) {
+ mActionListeners.add(listener)
+ listener.onMediaActionsChanged(mediaActions)
+ }
+ }
+
+ /**
+ * Removes a media action listener.
+ */
+ fun removeActionListener(listener: ActionListener) {
+ listener.onMediaActionsChanged(emptyList<RemoteAction>())
+ mActionListeners.remove(listener)
+ }
+
+ /**
+ * Adds a new media metadata listener.
+ */
+ fun addMetadataListener(listener: MetadataListener) {
+ if (!mMetadataListeners.contains(listener)) {
+ mMetadataListeners.add(listener)
+ listener.onMediaMetadataChanged(mediaMetadata)
+ }
+ }
+
+ /**
+ * Removes a media metadata listener.
+ */
+ fun removeMetadataListener(listener: MetadataListener) {
+ listener.onMediaMetadataChanged(null)
+ mMetadataListeners.remove(listener)
+ }
+
+ /**
+ * Adds a new token listener.
+ */
+ fun addTokenListener(listener: TokenListener) {
+ if (!mTokenListeners.contains(listener)) {
+ mTokenListeners.add(listener)
+ listener.onMediaSessionTokenChanged(token)
+ }
+ }
+
+ /**
+ * Removes a token listener.
+ */
+ fun removeTokenListener(listener: TokenListener) {
+ listener.onMediaSessionTokenChanged(null)
+ mTokenListeners.remove(listener)
+ }
+
+ private val token: MediaSession.Token?
+ get() = if (mMediaController == null) {
+ null
+ } else mMediaController!!.sessionToken
+ private val mediaMetadata: MediaMetadata?
+ get() = if (mMediaController != null) mMediaController!!.metadata else null
+
+ private val mediaActions: List<RemoteAction?>
+ /**
+ * Gets the set of media actions currently available.
+ */
+ get() {
+ if (mMediaController == null) {
+ return emptyList<RemoteAction>()
+ }
+ // Cache the PlaybackState since it's a Binder call.
+ // Safe because mMediaController is guaranteed non-null here.
+ val playbackState: PlaybackState = mMediaController!!.playbackState
+ ?: return emptyList<RemoteAction>()
+ val mediaActions = ArrayList<RemoteAction?>()
+ val isPlaying = playbackState.isActive
+ val actions = playbackState.actions
+
+ // Prev action
+ mPrevAction.isEnabled =
+ actions and PlaybackState.ACTION_SKIP_TO_PREVIOUS != 0L
+ mediaActions.add(mPrevAction)
+
+ // Play/pause action
+ if (!isPlaying && actions and PlaybackState.ACTION_PLAY != 0L) {
+ mediaActions.add(mPlayAction)
+ } else if (isPlaying && actions and PlaybackState.ACTION_PAUSE != 0L) {
+ mediaActions.add(mPauseAction)
+ }
+
+ // Next action
+ mNextAction.isEnabled =
+ actions and PlaybackState.ACTION_SKIP_TO_NEXT != 0L
+ mediaActions.add(mNextAction)
+ return mediaActions
+ }
+
+ /** @return Default [RemoteAction] sends broadcast back to SysUI.
+ */
+ private fun getDefaultRemoteAction(
+ @StringRes titleAndDescription: Int,
+ @DrawableRes icon: Int,
+ action: String
+ ): RemoteAction {
+ val titleAndDescriptionStr = mContext.getString(titleAndDescription)
+ val intent = Intent(action)
+ intent.setPackage(mContext.packageName)
+ return RemoteAction(
+ Icon.createWithResource(mContext, icon),
+ titleAndDescriptionStr, titleAndDescriptionStr,
+ PendingIntent.getBroadcast(
+ mContext, 0 /* requestCode */, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ )
+ }
+
+ /**
+ * Re-registers the session listener for the current user.
+ */
+ fun registerSessionListenerForCurrentUser() {
+ mMediaSessionManager!!.removeOnActiveSessionsChangedListener(mSessionsChangedListener)
+ mMediaSessionManager.addOnActiveSessionsChangedListener(
+ null, UserHandle.CURRENT,
+ mHandlerExecutor, mSessionsChangedListener
+ )
+ }
+
+ /**
+ * Tries to find and set the active media controller for the top PiP activity.
+ */
+ private fun resolveActiveMediaController(controllers: List<MediaController>?) {
+ if (controllers != null) {
+ val topActivity = PipUtils.getTopPipActivity(mContext).first
+ if (topActivity != null) {
+ for (i in controllers.indices) {
+ val controller = controllers[i]
+ if (controller.packageName == topActivity.packageName) {
+ setActiveMediaController(controller)
+ return
+ }
+ }
+ }
+ }
+ setActiveMediaController(null)
+ }
+
+ /**
+ * Sets the active media controller for the top PiP activity.
+ */
+ private fun setActiveMediaController(controller: MediaController?) {
+ if (controller != mMediaController) {
+ if (mMediaController != null) {
+ mMediaController!!.unregisterCallback(mPlaybackChangedListener)
+ }
+ mMediaController = controller
+ controller?.registerCallback(mPlaybackChangedListener, mMainHandler)
+ notifyActionsChanged()
+ notifyMetadataChanged(mediaMetadata)
+ notifyTokenChanged(token)
+
+ // TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV)
+ }
+ }
+
+ /**
+ * Notifies all listeners that the actions have changed.
+ */
+ private fun notifyActionsChanged() {
+ if (mActionListeners.isNotEmpty()) {
+ val actions = mediaActions
+ mActionListeners.forEach(
+ Consumer { l: ActionListener -> l.onMediaActionsChanged(actions) })
+ }
+ }
+
+ /**
+ * Notifies all listeners that the metadata have changed.
+ */
+ private fun notifyMetadataChanged(metadata: MediaMetadata?) {
+ if (mMetadataListeners.isNotEmpty()) {
+ mMetadataListeners.forEach(Consumer { l: MetadataListener ->
+ l.onMediaMetadataChanged(
+ metadata
+ )
+ })
+ }
+ }
+
+ private fun notifyTokenChanged(token: MediaSession.Token?) {
+ if (mTokenListeners.isNotEmpty()) {
+ mTokenListeners.forEach(Consumer { l: TokenListener ->
+ l.onMediaSessionTokenChanged(
+ token
+ )
+ })
+ }
+ }
+
+ companion object {
+ private const val SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF"
+ private const val ACTION_PLAY = "com.android.wm.shell.pip.PLAY"
+ private const val ACTION_PAUSE = "com.android.wm.shell.pip.PAUSE"
+ private const val ACTION_NEXT = "com.android.wm.shell.pip.NEXT"
+ private const val ACTION_PREV = "com.android.wm.shell.pip.PREV"
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUiEventLogger.kt
new file mode 100644
index 0000000..642dacc
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUiEventLogger.kt
@@ -0,0 +1,125 @@
+/*
+ * 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.common.pip
+
+import android.app.TaskInfo
+import android.content.pm.PackageManager
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+/**
+ * Helper class that ends PiP log to UiEvent, see also go/uievent
+ */
+class PipUiEventLogger(
+ private val mUiEventLogger: UiEventLogger,
+ private val mPackageManager: PackageManager
+) {
+ private var mPackageName: String? = null
+ private var mPackageUid = INVALID_PACKAGE_UID
+ fun setTaskInfo(taskInfo: TaskInfo?) {
+ if (taskInfo?.topActivity != null) {
+ // safe because topActivity is guaranteed non-null here
+ mPackageName = taskInfo.topActivity!!.packageName
+ mPackageUid = getUid(mPackageName!!, taskInfo.userId)
+ } else {
+ mPackageName = null
+ mPackageUid = INVALID_PACKAGE_UID
+ }
+ }
+
+ /**
+ * Sends log via UiEvent, reference go/uievent for how to debug locally
+ */
+ fun log(event: PipUiEventEnum?) {
+ if (mPackageName == null || mPackageUid == INVALID_PACKAGE_UID) {
+ return
+ }
+ mUiEventLogger.log(event!!, mPackageUid, mPackageName)
+ }
+
+ private fun getUid(packageName: String, userId: Int): Int {
+ var uid = INVALID_PACKAGE_UID
+ try {
+ uid = mPackageManager.getApplicationInfoAsUser(
+ packageName, 0 /* ApplicationInfoFlags */, userId
+ ).uid
+ } catch (e: PackageManager.NameNotFoundException) {
+ // do nothing.
+ }
+ return uid
+ }
+
+ /**
+ * Enums for logging the PiP events to UiEvent
+ */
+ enum class PipUiEventEnum(private val mId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Activity enters picture-in-picture mode")
+ PICTURE_IN_PICTURE_ENTER(603),
+
+ @UiEvent(doc = "Activity enters picture-in-picture mode with auto-enter-pip API")
+ PICTURE_IN_PICTURE_AUTO_ENTER(1313),
+
+ @UiEvent(doc = "Activity enters picture-in-picture mode from content-pip API")
+ PICTURE_IN_PICTURE_ENTER_CONTENT_PIP(1314),
+
+ @UiEvent(doc = "Expands from picture-in-picture to fullscreen")
+ PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN(604),
+
+ @UiEvent(doc = "Removes picture-in-picture by tap close button")
+ PICTURE_IN_PICTURE_TAP_TO_REMOVE(605),
+
+ @UiEvent(doc = "Removes picture-in-picture by drag to dismiss area")
+ PICTURE_IN_PICTURE_DRAG_TO_REMOVE(606),
+
+ @UiEvent(doc = "Shows picture-in-picture menu")
+ PICTURE_IN_PICTURE_SHOW_MENU(607),
+
+ @UiEvent(doc = "Hides picture-in-picture menu")
+ PICTURE_IN_PICTURE_HIDE_MENU(608),
+
+ @UiEvent(
+ doc = "Changes the aspect ratio of picture-in-picture window. This is inherited" +
+ " from previous Tron-based logging and currently not in use."
+ )
+ PICTURE_IN_PICTURE_CHANGE_ASPECT_RATIO(609),
+
+ @UiEvent(doc = "User resize of the picture-in-picture window")
+ PICTURE_IN_PICTURE_RESIZE(610),
+
+ @UiEvent(doc = "User unstashed picture-in-picture")
+ PICTURE_IN_PICTURE_STASH_UNSTASHED(709),
+
+ @UiEvent(doc = "User stashed picture-in-picture to the left side")
+ PICTURE_IN_PICTURE_STASH_LEFT(710),
+
+ @UiEvent(doc = "User stashed picture-in-picture to the right side")
+ PICTURE_IN_PICTURE_STASH_RIGHT(711),
+
+ @UiEvent(doc = "User taps on the settings button in PiP menu")
+ PICTURE_IN_PICTURE_SHOW_SETTINGS(933),
+
+ @UiEvent(doc = "Closes PiP with app-provided close action")
+ PICTURE_IN_PICTURE_CUSTOM_CLOSE(1058);
+
+ override fun getId(): Int {
+ return mId
+ }
+ }
+
+ companion object {
+ private const val INVALID_PACKAGE_UID = -1
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
index ef93a33..be1b9b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.common.split;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -60,7 +61,8 @@
public static final int[] CONTROLLED_WINDOWING_MODES =
{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
public static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
- {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW};
+ {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW,
+ WINDOWING_MODE_FREEFORM};
/** Flag applied to a transition change to identify it as a divider bar for animation. */
public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 54f8984..7bf0893 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -230,8 +230,10 @@
// The user aspect ratio button should not be handled when a new TaskInfo is
// sent because of a double tap or when in multi-window mode.
if (taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
- mUserAspectRatioSettingsLayout.release();
- mUserAspectRatioSettingsLayout = null;
+ if (mUserAspectRatioSettingsLayout != null) {
+ mUserAspectRatioSettingsLayout.release();
+ mUserAspectRatioSettingsLayout = null;
+ }
return;
}
if (!taskInfo.isFromLetterboxDoubleTap) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index c06b22c..9472341 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -20,6 +20,7 @@
import android.app.ActivityTaskManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.SystemProperties;
import android.view.IWindowManager;
@@ -57,6 +58,8 @@
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.compatui.CompatUIConfiguration;
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
@@ -331,6 +334,25 @@
abstract ShellBackAnimationRegistry optionalBackAnimationRegistry();
//
+ // PiP (optional feature)
+ //
+
+ @WMSingleton
+ @Provides
+ static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
+ PackageManager packageManager) {
+ return new PipUiEventLogger(uiEventLogger, packageManager);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipMediaController providePipMediaController(Context context,
+ @ShellMainThread Handler mainHandler) {
+ return new PipMediaController(context, mainHandler);
+ }
+
+
+ //
// Bubbles (optional feature)
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 9bf973f..24ef44a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -32,6 +32,8 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
@@ -41,7 +43,6 @@
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
@@ -49,7 +50,6 @@
import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
index e8fae24..c4ca501 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
@@ -17,15 +17,9 @@
package com.android.wm.shell.dagger.pip;
import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Handler;
-import com.android.internal.logging.UiEventLogger;
-import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.dagger.WMSingleton;
-import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
-import com.android.wm.shell.pip.PipUiEventLogger;
import dagger.Module;
import dagger.Provides;
@@ -36,24 +30,9 @@
*/
@Module
public abstract class Pip1SharedModule {
- // Needs handler for registering broadcast receivers
- @WMSingleton
- @Provides
- static PipMediaController providePipMediaController(Context context,
- @ShellMainThread Handler mainHandler) {
- return new PipMediaController(context, mainHandler);
- }
-
@WMSingleton
@Provides
static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
return new PipSurfaceTransactionHelper(context);
}
-
- @WMSingleton
- @Provides
- static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
- PackageManager packageManager) {
- return new PipUiEventLogger(uiEventLogger, packageManager);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index c7c6e8a..8dec4ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
+import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip2.PipTransition;
@@ -26,9 +27,9 @@
/**
* Provides dependencies from {@link com.android.wm.shell.pip2}, this implementation is meant to be
- * the successor of its sibling {@link Pip1SharedModule}.
+ * the successor of its sibling {@link Pip1Module}.
*/
-@Module
+@Module(includes = WMShellBaseModule.class)
public abstract class Pip2Module {
@WMSingleton
@Provides
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
index 80ffbb0..a6ff9ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
@@ -30,20 +30,20 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.pip.LegacySizeSpecSource;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm;
import com.android.wm.shell.pip.tv.TvPipBoundsController;
import com.android.wm.shell.pip.tv.TvPipBoundsState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 1d46e75..633f627 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -18,6 +18,7 @@
import android.R
import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -301,6 +302,24 @@
}
}
+ /** Move a desktop app to split screen. */
+ fun moveToSplit(task: RunningTaskInfo) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: moveToSplit taskId=%d",
+ task.taskId
+ )
+ val wct = WindowContainerTransaction()
+ wct.setWindowingMode(task.token, WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW)
+ wct.setBounds(task.token, null)
+ wct.setDensityDpi(task.token, getDefaultDensityDpi())
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
+ } else {
+ shellTaskOrganizer.applyTransaction(wct)
+ }
+ }
+
/**
* The second part of the animated move to desktop transition, called after
* {@link startMoveToDesktop}. Move a task to fullscreen after being dragged from fullscreen
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
deleted file mode 100644
index ddffb5b..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Copyright (C) 2020 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.pip;
-
-import static android.app.PendingIntent.FLAG_IMMUTABLE;
-import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
-
-import android.annotation.DrawableRes;
-import android.annotation.StringRes;
-import android.annotation.SuppressLint;
-import android.app.PendingIntent;
-import android.app.RemoteAction;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.drawable.Icon;
-import android.media.MediaMetadata;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
-import android.media.session.MediaSessionManager;
-import android.media.session.PlaybackState;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.UserHandle;
-
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.R;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Interfaces with the {@link MediaSessionManager} to compose the right set of actions to show (only
- * if there are no actions from the PiP activity itself). The active media controller is only set
- * when there is a media session from the top PiP activity.
- */
-public class PipMediaController {
- private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
-
- private static final String ACTION_PLAY = "com.android.wm.shell.pip.PLAY";
- private static final String ACTION_PAUSE = "com.android.wm.shell.pip.PAUSE";
- private static final String ACTION_NEXT = "com.android.wm.shell.pip.NEXT";
- private static final String ACTION_PREV = "com.android.wm.shell.pip.PREV";
-
- /**
- * A listener interface to receive notification on changes to the media actions.
- */
- public interface ActionListener {
- /**
- * Called when the media actions changed.
- */
- void onMediaActionsChanged(List<RemoteAction> actions);
- }
-
- /**
- * A listener interface to receive notification on changes to the media metadata.
- */
- public interface MetadataListener {
- /**
- * Called when the media metadata changed.
- */
- void onMediaMetadataChanged(MediaMetadata metadata);
- }
-
- /**
- * A listener interface to receive notification on changes to the media session token.
- */
- public interface TokenListener {
- /**
- * Called when the media session token changed.
- */
- void onMediaSessionTokenChanged(MediaSession.Token token);
- }
-
- private final Context mContext;
- private final Handler mMainHandler;
- private final HandlerExecutor mHandlerExecutor;
-
- private final MediaSessionManager mMediaSessionManager;
- private MediaController mMediaController;
-
- private RemoteAction mPauseAction;
- private RemoteAction mPlayAction;
- private RemoteAction mNextAction;
- private RemoteAction mPrevAction;
-
- private final BroadcastReceiver mMediaActionReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mMediaController == null || mMediaController.getTransportControls() == null) {
- // no active media session, bail early.
- return;
- }
- switch (intent.getAction()) {
- case ACTION_PLAY:
- mMediaController.getTransportControls().play();
- break;
- case ACTION_PAUSE:
- mMediaController.getTransportControls().pause();
- break;
- case ACTION_NEXT:
- mMediaController.getTransportControls().skipToNext();
- break;
- case ACTION_PREV:
- mMediaController.getTransportControls().skipToPrevious();
- break;
- }
- }
- };
-
- private final MediaController.Callback mPlaybackChangedListener =
- new MediaController.Callback() {
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- notifyActionsChanged();
- }
-
- @Override
- public void onMetadataChanged(@Nullable MediaMetadata metadata) {
- notifyMetadataChanged(metadata);
- }
- };
-
- private final MediaSessionManager.OnActiveSessionsChangedListener mSessionsChangedListener =
- this::resolveActiveMediaController;
-
- private final ArrayList<ActionListener> mActionListeners = new ArrayList<>();
- private final ArrayList<MetadataListener> mMetadataListeners = new ArrayList<>();
- private final ArrayList<TokenListener> mTokenListeners = new ArrayList<>();
-
- public PipMediaController(Context context, Handler mainHandler) {
- mContext = context;
- mMainHandler = mainHandler;
- mHandlerExecutor = new HandlerExecutor(mMainHandler);
- if (!PipUtils.isPip2ExperimentEnabled()) {
- IntentFilter mediaControlFilter = new IntentFilter();
- mediaControlFilter.addAction(ACTION_PLAY);
- mediaControlFilter.addAction(ACTION_PAUSE);
- mediaControlFilter.addAction(ACTION_NEXT);
- mediaControlFilter.addAction(ACTION_PREV);
- mContext.registerReceiverForAllUsers(mMediaActionReceiver, mediaControlFilter,
- SYSTEMUI_PERMISSION, mainHandler, Context.RECEIVER_EXPORTED);
- }
-
- // Creates the standard media buttons that we may show.
- mPauseAction = getDefaultRemoteAction(R.string.pip_pause,
- R.drawable.pip_ic_pause_white, ACTION_PAUSE);
- mPlayAction = getDefaultRemoteAction(R.string.pip_play,
- R.drawable.pip_ic_play_arrow_white, ACTION_PLAY);
- mNextAction = getDefaultRemoteAction(R.string.pip_skip_to_next,
- R.drawable.pip_ic_skip_next_white, ACTION_NEXT);
- mPrevAction = getDefaultRemoteAction(R.string.pip_skip_to_prev,
- R.drawable.pip_ic_skip_previous_white, ACTION_PREV);
-
- mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
- }
-
- /**
- * Handles when an activity is pinned.
- */
- public void onActivityPinned() {
- // Once we enter PiP, try to find the active media controller for the top most activity
- resolveActiveMediaController(mMediaSessionManager.getActiveSessionsForUser(null,
- UserHandle.CURRENT));
- }
-
- /**
- * Adds a new media action listener.
- */
- public void addActionListener(ActionListener listener) {
- if (!mActionListeners.contains(listener)) {
- mActionListeners.add(listener);
- listener.onMediaActionsChanged(getMediaActions());
- }
- }
-
- /**
- * Removes a media action listener.
- */
- public void removeActionListener(ActionListener listener) {
- listener.onMediaActionsChanged(Collections.emptyList());
- mActionListeners.remove(listener);
- }
-
- /**
- * Adds a new media metadata listener.
- */
- public void addMetadataListener(MetadataListener listener) {
- if (!mMetadataListeners.contains(listener)) {
- mMetadataListeners.add(listener);
- listener.onMediaMetadataChanged(getMediaMetadata());
- }
- }
-
- /**
- * Removes a media metadata listener.
- */
- public void removeMetadataListener(MetadataListener listener) {
- listener.onMediaMetadataChanged(null);
- mMetadataListeners.remove(listener);
- }
-
- /**
- * Adds a new token listener.
- */
- public void addTokenListener(TokenListener listener) {
- if (!mTokenListeners.contains(listener)) {
- mTokenListeners.add(listener);
- listener.onMediaSessionTokenChanged(getToken());
- }
- }
-
- /**
- * Removes a token listener.
- */
- public void removeTokenListener(TokenListener listener) {
- listener.onMediaSessionTokenChanged(null);
- mTokenListeners.remove(listener);
- }
-
- private MediaSession.Token getToken() {
- if (mMediaController == null) {
- return null;
- }
- return mMediaController.getSessionToken();
- }
-
- private MediaMetadata getMediaMetadata() {
- return mMediaController != null ? mMediaController.getMetadata() : null;
- }
-
- /**
- * Gets the set of media actions currently available.
- */
- // This is due to using PlaybackState#isActive, which is added in API 31.
- // It can be removed when min_sdk of the app is set to 31 or greater.
- @SuppressLint("NewApi")
- private List<RemoteAction> getMediaActions() {
- // Cache the PlaybackState since it's a Binder call.
- final PlaybackState playbackState;
- if (mMediaController == null
- || (playbackState = mMediaController.getPlaybackState()) == null) {
- return Collections.emptyList();
- }
-
- ArrayList<RemoteAction> mediaActions = new ArrayList<>();
- boolean isPlaying = playbackState.isActive();
- long actions = playbackState.getActions();
-
- // Prev action
- mPrevAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0);
- mediaActions.add(mPrevAction);
-
- // Play/pause action
- if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) {
- mediaActions.add(mPlayAction);
- } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) {
- mediaActions.add(mPauseAction);
- }
-
- // Next action
- mNextAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0);
- mediaActions.add(mNextAction);
- return mediaActions;
- }
-
- /** @return Default {@link RemoteAction} sends broadcast back to SysUI. */
- private RemoteAction getDefaultRemoteAction(@StringRes int titleAndDescription,
- @DrawableRes int icon, String action) {
- final String titleAndDescriptionStr = mContext.getString(titleAndDescription);
- final Intent intent = new Intent(action);
- intent.setPackage(mContext.getPackageName());
- return new RemoteAction(Icon.createWithResource(mContext, icon),
- titleAndDescriptionStr, titleAndDescriptionStr,
- PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent,
- FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
- }
-
- /**
- * Re-registers the session listener for the current user.
- */
- public void registerSessionListenerForCurrentUser() {
- mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener);
- mMediaSessionManager.addOnActiveSessionsChangedListener(null, UserHandle.CURRENT,
- mHandlerExecutor, mSessionsChangedListener);
- }
-
- /**
- * Tries to find and set the active media controller for the top PiP activity.
- */
- private void resolveActiveMediaController(List<MediaController> controllers) {
- if (controllers != null) {
- final ComponentName topActivity = PipUtils.getTopPipActivity(mContext).first;
- if (topActivity != null) {
- for (int i = 0; i < controllers.size(); i++) {
- final MediaController controller = controllers.get(i);
- if (controller.getPackageName().equals(topActivity.getPackageName())) {
- setActiveMediaController(controller);
- return;
- }
- }
- }
- }
- setActiveMediaController(null);
- }
-
- /**
- * Sets the active media controller for the top PiP activity.
- */
- private void setActiveMediaController(MediaController controller) {
- if (controller != mMediaController) {
- if (mMediaController != null) {
- mMediaController.unregisterCallback(mPlaybackChangedListener);
- }
- mMediaController = controller;
- if (controller != null) {
- controller.registerCallback(mPlaybackChangedListener, mMainHandler);
- }
- notifyActionsChanged();
- notifyMetadataChanged(getMediaMetadata());
- notifyTokenChanged(getToken());
-
- // TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV)
- }
- }
-
- /**
- * Notifies all listeners that the actions have changed.
- */
- private void notifyActionsChanged() {
- if (!mActionListeners.isEmpty()) {
- List<RemoteAction> actions = getMediaActions();
- mActionListeners.forEach(l -> l.onMediaActionsChanged(actions));
- }
- }
-
- /**
- * Notifies all listeners that the metadata have changed.
- */
- private void notifyMetadataChanged(MediaMetadata metadata) {
- if (!mMetadataListeners.isEmpty()) {
- mMetadataListeners.forEach(l -> l.onMediaMetadataChanged(metadata));
- }
- }
-
- private void notifyTokenChanged(MediaSession.Token token) {
- if (!mTokenListeners.isEmpty()) {
- mTokenListeners.forEach(l -> l.onMediaSessionTokenChanged(token));
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 19c60c2..296857b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -82,6 +82,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
deleted file mode 100644
index 3e5a19b..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2020 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.pip;
-
-import android.app.TaskInfo;
-import android.content.pm.PackageManager;
-
-import com.android.internal.logging.UiEvent;
-import com.android.internal.logging.UiEventLogger;
-
-/**
- * Helper class that ends PiP log to UiEvent, see also go/uievent
- */
-public class PipUiEventLogger {
-
- private static final int INVALID_PACKAGE_UID = -1;
-
- private final UiEventLogger mUiEventLogger;
- private final PackageManager mPackageManager;
-
- private String mPackageName;
- private int mPackageUid = INVALID_PACKAGE_UID;
-
- public PipUiEventLogger(UiEventLogger uiEventLogger, PackageManager packageManager) {
- mUiEventLogger = uiEventLogger;
- mPackageManager = packageManager;
- }
-
- public void setTaskInfo(TaskInfo taskInfo) {
- if (taskInfo != null && taskInfo.topActivity != null) {
- mPackageName = taskInfo.topActivity.getPackageName();
- mPackageUid = getUid(mPackageName, taskInfo.userId);
- } else {
- mPackageName = null;
- mPackageUid = INVALID_PACKAGE_UID;
- }
- }
-
- /**
- * Sends log via UiEvent, reference go/uievent for how to debug locally
- */
- public void log(PipUiEventEnum event) {
- if (mPackageName == null || mPackageUid == INVALID_PACKAGE_UID) {
- return;
- }
- mUiEventLogger.log(event, mPackageUid, mPackageName);
- }
-
- private int getUid(String packageName, int userId) {
- int uid = INVALID_PACKAGE_UID;
- try {
- uid = mPackageManager.getApplicationInfoAsUser(
- packageName, 0 /* ApplicationInfoFlags */, userId).uid;
- } catch (PackageManager.NameNotFoundException e) {
- // do nothing.
- }
- return uid;
- }
-
- /**
- * Enums for logging the PiP events to UiEvent
- */
- public enum PipUiEventEnum implements UiEventLogger.UiEventEnum {
- @UiEvent(doc = "Activity enters picture-in-picture mode")
- PICTURE_IN_PICTURE_ENTER(603),
-
- @UiEvent(doc = "Activity enters picture-in-picture mode with auto-enter-pip API")
- PICTURE_IN_PICTURE_AUTO_ENTER(1313),
-
- @UiEvent(doc = "Activity enters picture-in-picture mode from content-pip API")
- PICTURE_IN_PICTURE_ENTER_CONTENT_PIP(1314),
-
- @UiEvent(doc = "Expands from picture-in-picture to fullscreen")
- PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN(604),
-
- @UiEvent(doc = "Removes picture-in-picture by tap close button")
- PICTURE_IN_PICTURE_TAP_TO_REMOVE(605),
-
- @UiEvent(doc = "Removes picture-in-picture by drag to dismiss area")
- PICTURE_IN_PICTURE_DRAG_TO_REMOVE(606),
-
- @UiEvent(doc = "Shows picture-in-picture menu")
- PICTURE_IN_PICTURE_SHOW_MENU(607),
-
- @UiEvent(doc = "Hides picture-in-picture menu")
- PICTURE_IN_PICTURE_HIDE_MENU(608),
-
- @UiEvent(doc = "Changes the aspect ratio of picture-in-picture window. This is inherited"
- + " from previous Tron-based logging and currently not in use.")
- PICTURE_IN_PICTURE_CHANGE_ASPECT_RATIO(609),
-
- @UiEvent(doc = "User resize of the picture-in-picture window")
- PICTURE_IN_PICTURE_RESIZE(610),
-
- @UiEvent(doc = "User unstashed picture-in-picture")
- PICTURE_IN_PICTURE_STASH_UNSTASHED(709),
-
- @UiEvent(doc = "User stashed picture-in-picture to the left side")
- PICTURE_IN_PICTURE_STASH_LEFT(710),
-
- @UiEvent(doc = "User stashed picture-in-picture to the right side")
- PICTURE_IN_PICTURE_STASH_RIGHT(711),
-
- @UiEvent(doc = "User taps on the settings button in PiP menu")
- PICTURE_IN_PICTURE_SHOW_SETTINGS(933),
-
- @UiEvent(doc = "Closes PiP with app-provided close action")
- PICTURE_IN_PICTURE_CUSTOM_CLOSE(1058);
-
- private final int mId;
-
- PipUiEventEnum(int id) {
- mId = id;
- }
-
- @Override
- public int getId() {
- return mId;
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 5e1b6be..cc182ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -38,12 +38,12 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipMediaController.ActionListener;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipMediaController;
-import com.android.wm.shell.pip.PipMediaController.ActionListener;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
-import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index b872267..5c65d78 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -76,6 +76,7 @@
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.IPip;
@@ -87,7 +88,6 @@
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
-import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -123,14 +123,6 @@
private static final long PIP_KEEP_CLEAR_AREAS_DELAY =
SystemProperties.getLong("persist.wm.debug.pip_keep_clear_areas_delay", 200);
- private boolean mEnablePipKeepClearAlgorithm =
- SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", true);
-
- @VisibleForTesting
- void setEnablePipKeepClearAlgorithm(boolean value) {
- mEnablePipKeepClearAlgorithm = value;
- }
-
private Context mContext;
protected ShellExecutor mMainExecutor;
private DisplayController mDisplayController;
@@ -166,10 +158,6 @@
// early bail out if the change was caused by keyguard showing up
return;
}
- if (!mEnablePipKeepClearAlgorithm) {
- // early bail out if the keep clear areas feature is disabled
- return;
- }
if (mPipBoundsState.isStashed()) {
// don't move when stashed
return;
@@ -187,10 +175,6 @@
}
private void updatePipPositionForKeepClearAreas() {
- if (!mEnablePipKeepClearAlgorithm) {
- // early bail out if the keep clear areas feature is disabled
- return;
- }
if (mIsKeyguardShowingOrAnimating) {
// early bail out if the change was caused by keyguard showing up
return;
@@ -343,19 +327,17 @@
public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
Set<Rect> unrestricted) {
if (mPipDisplayLayoutState.getDisplayId() == displayId) {
- if (mEnablePipKeepClearAlgorithm) {
- mPipBoundsState.setKeepClearAreas(restricted, unrestricted);
+ mPipBoundsState.setKeepClearAreas(restricted, unrestricted);
- mMainExecutor.removeCallbacks(
- mMovePipInResponseToKeepClearAreasChangeCallback);
- mMainExecutor.executeDelayed(
- mMovePipInResponseToKeepClearAreasChangeCallback,
- PIP_KEEP_CLEAR_AREAS_DELAY);
+ mMainExecutor.removeCallbacks(
+ mMovePipInResponseToKeepClearAreasChangeCallback);
+ mMainExecutor.executeDelayed(
+ mMovePipInResponseToKeepClearAreasChangeCallback,
+ PIP_KEEP_CLEAR_AREAS_DELAY);
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "onKeepClearAreasChanged: restricted=%s, unrestricted=%s",
- restricted, unrestricted);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "onKeepClearAreasChanged: restricted=%s, unrestricted=%s",
+ restricted, unrestricted);
}
}
};
@@ -660,25 +642,9 @@
// there's a keyguard present
return;
}
- int oldMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
onDisplayChangedUncheck(mDisplayController
.getDisplayLayout(mPipDisplayLayoutState.getDisplayId()),
false /* saveRestoreSnapFraction */);
- int newMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
- if (!mEnablePipKeepClearAlgorithm) {
- // offset PiP to adjust for bottom inset change
- int pipTop = mPipBoundsState.getBounds().top;
- int diff = newMaxMovementBound - oldMaxMovementBound;
- if (diff < 0 && pipTop > newMaxMovementBound) {
- // bottom inset has increased, move PiP up if it is too low
- mPipMotionHelper.animateToOffset(mPipBoundsState.getBounds(),
- newMaxMovementBound - pipTop);
- }
- if (diff > 0 && oldMaxMovementBound == pipTop) {
- // bottom inset has decreased, move PiP down if it was by the edge
- mPipMotionHelper.animateToOffset(mPipBoundsState.getBounds(), diff);
- }
- }
}
});
@@ -947,14 +913,8 @@
* Sets both shelf visibility and its height.
*/
private void setShelfHeight(boolean visible, int height) {
- if (mEnablePipKeepClearAlgorithm) {
- // turn this into Launcher keep clear area registration instead
- setLauncherKeepClearAreaHeight(visible, height);
- return;
- }
- if (!mIsKeyguardShowingOrAnimating) {
- setShelfHeightLocked(visible, height);
- }
+ // turn this into Launcher keep clear area registration instead
+ setLauncherKeepClearAreaHeight(visible, height);
}
private void setLauncherKeepClearAreaHeight(boolean visible, int height) {
@@ -1015,16 +975,10 @@
private Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams,
int launcherRotation, Rect hotseatKeepClearArea) {
-
- if (mEnablePipKeepClearAlgorithm) {
- // preemptively add the keep clear area for Hotseat, so that it is taken into account
- // when calculating the entry destination bounds of PiP window
- mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG,
- hotseatKeepClearArea);
- } else {
- int shelfHeight = hotseatKeepClearArea.height();
- setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight);
- }
+ // preemptively add the keep clear area for Hotseat, so that it is taken into account
+ // when calculating the entry destination bounds of PiP window
+ mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG,
+ hotseatKeepClearArea);
onDisplayRotationChangedNotInPip(mContext, launcherRotation);
final Rect entryBounds = mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
pictureInPictureParams);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index da455f8..4e75847 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -38,7 +38,7 @@
import com.android.wm.shell.common.bubbles.DismissCircleView;
import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
-import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import kotlin.Unit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 779c539..837426a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -66,7 +66,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index b251f6f..8f0a8e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -33,7 +33,6 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Debug;
-import android.os.SystemProperties;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
@@ -48,19 +47,16 @@
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import java.util.function.Consumer;
-
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
+import java.util.function.Consumer;
+
/**
* A helper to animate and manipulate the PiP.
*/
public class PipMotionHelper implements PipAppOpsListener.Callback,
FloatingContentCoordinator.FloatingContent {
-
- public static final boolean ENABLE_FLING_TO_DISMISS_PIP =
- SystemProperties.getBoolean("persist.wm.debug.fling_to_dismiss_pip", false);
private static final String TAG = "PipMotionHelper";
private static final boolean DEBUG = false;
@@ -707,7 +703,7 @@
loc[1] = animatedPipBounds.top;
}
};
- mMagnetizedPip.setFlingToTargetEnabled(ENABLE_FLING_TO_DISMISS_PIP);
+ mMagnetizedPip.setFlingToTargetEnabled(false);
}
return mMagnetizedPip;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index abe2db0..4e687dd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -46,11 +46,11 @@
import com.android.internal.policy.TaskResizingAlgorithm;
import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
-import com.android.wm.shell.pip.PipUiEventLogger;
import java.io.PrintWriter;
import java.util.function.Consumer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 3e95498a..6055fd9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -34,7 +34,6 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.os.SystemProperties;
import android.provider.DeviceConfig;
import android.util.Size;
import android.view.DisplayCutout;
@@ -51,13 +50,13 @@
import com.android.wm.shell.R;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellInit;
@@ -73,14 +72,6 @@
private static final String TAG = "PipTouchHandler";
private static final float DEFAULT_STASH_VELOCITY_THRESHOLD = 18000.f;
- private boolean mEnablePipKeepClearAlgorithm =
- SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", true);
-
- @VisibleForTesting
- void setEnablePipKeepClearAlgorithm(boolean value) {
- mEnablePipKeepClearAlgorithm = value;
- }
-
// Allow PIP to resize to a slightly bigger state upon touch
private boolean mEnableResize;
private final Context mContext;
@@ -430,48 +421,6 @@
mIsImeShowing ? mImeOffset : 0,
!mIsImeShowing && mIsShelfShowing ? mShelfHeight : 0);
- // If this is from an IME or shelf adjustment, then we should move the PiP so that it is not
- // occluded by the IME or shelf.
- if (fromImeAdjustment || fromShelfAdjustment) {
- if (mTouchState.isUserInteracting() && mTouchState.isDragging()) {
- // Defer the update of the current movement bounds until after the user finishes
- // touching the screen
- } else if (mEnablePipKeepClearAlgorithm) {
- // Ignore moving PiP if keep clear algorithm is enabled, since IME and shelf height
- // now are accounted for in the keep clear algorithm calculations
- } else {
- final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu();
- final Rect toMovementBounds = new Rect();
- mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds,
- toMovementBounds, mIsImeShowing ? mImeHeight : 0);
- final int prevBottom = mPipBoundsState.getMovementBounds().bottom
- - mMovementBoundsExtraOffsets;
- // This is to handle landscape fullscreen IMEs, don't apply the extra offset in this
- // case
- final int toBottom = toMovementBounds.bottom < toMovementBounds.top
- ? toMovementBounds.bottom
- : toMovementBounds.bottom - extraOffset;
-
- if (isExpanded) {
- curBounds.set(mPipBoundsState.getExpandedBounds());
- mPipBoundsAlgorithm.getSnapAlgorithm().applySnapFraction(curBounds,
- toMovementBounds, mSavedSnapFraction);
- }
-
- if (prevBottom < toBottom) {
- // The movement bounds are expanding
- if (curBounds.top > prevBottom - mBottomOffsetBufferPx) {
- mMotionHelper.animateToOffset(curBounds, toBottom - curBounds.top);
- }
- } else if (prevBottom > toBottom) {
- // The movement bounds are shrinking
- if (curBounds.top > toBottom - mBottomOffsetBufferPx) {
- mMotionHelper.animateToOffset(curBounds, toBottom - curBounds.top);
- }
- }
- }
- }
-
// Update the movement bounds after doing the calculations based on the old movement bounds
// above
mPipBoundsState.setNormalMovementBounds(normalMovementBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
index 11c2665..f1606f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
@@ -35,7 +35,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
-import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index e3544c6..5f5d8ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -48,11 +48,11 @@
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index f22ee59..39c7a4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -36,7 +36,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ImageUtils;
import com.android.wm.shell.R;
-import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index 4819f66..dbec607 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -25,6 +25,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
@@ -35,7 +36,6 @@
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.splitscreen.SplitScreenController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index f35eda6..ac142e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -203,6 +203,17 @@
}
}
+ @Nullable
+ public SplitBounds getSplitBoundsForTaskId(int taskId) {
+ if (taskId == INVALID_TASK_ID) {
+ return null;
+ }
+
+ // We could do extra verification of requiring both taskIds of a pair and verifying that
+ // the same split bounds object is returned... but meh. Seems unnecessary.
+ return mTaskSplitBoundsMap.get(taskId);
+ }
+
@Override
public Context getContext() {
return mContext;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index a11d952..dc6dc79 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.recents;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -23,6 +24,8 @@
import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
+
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -69,6 +72,8 @@
private final Transitions mTransitions;
private final ShellExecutor mExecutor;
+ @Nullable
+ private final RecentTasksController mRecentTasksController;
private IApplicationThread mAnimApp = null;
private final ArrayList<RecentsController> mControllers = new ArrayList<>();
@@ -82,6 +87,7 @@
@Nullable RecentTasksController recentTasksController) {
mTransitions = transitions;
mExecutor = transitions.getMainExecutor();
+ mRecentTasksController = recentTasksController;
if (!Transitions.ENABLE_SHELL_TRANSITIONS) return;
if (recentTasksController == null) return;
shellInit.addInitCallback(() -> {
@@ -417,6 +423,7 @@
mLeashMap = new ArrayMap<>();
mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0;
+ int closingSplitTaskId = INVALID_TASK_ID;
final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>();
TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter();
@@ -443,6 +450,7 @@
apps.add(target);
if (TransitionUtil.isClosingType(change.getMode())) {
mPausingTasks.add(new TaskState(change, target.leash));
+ closingSplitTaskId = change.getTaskInfo().taskId;
if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
" adding pausing leaf home taskId=%d", taskInfo.taskId);
@@ -500,13 +508,16 @@
}
}
t.apply();
+ Bundle b = new Bundle(1 /*capacity*/);
+ b.putParcelable(KEY_EXTRA_SPLIT_BOUNDS,
+ mRecentTasksController.getSplitBoundsForTaskId(closingSplitTaskId));
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"[%d] RecentsController.start: calling onAnimationStart", mInstanceId);
mListener.onAnimationStart(this,
apps.toArray(new RemoteAnimationTarget[apps.size()]),
wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
- new Rect(0, 0, 0, 0), new Rect());
+ new Rect(0, 0, 0, 0), new Rect(), b);
} catch (RemoteException e) {
Slog.e(TAG, "Error starting recents animation", e);
cancel("onAnimationStart() failed");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
index f209521..0edcff4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
@@ -26,6 +26,8 @@
* tasks/leashes/etc in Launcher
*/
public class SplitBounds implements Parcelable {
+ public static final String KEY_EXTRA_SPLIT_BOUNDS = "key_SplitBounds";
+
public final Rect leftTopBounds;
public final Rect rightBottomBounds;
/** This rect represents the actual gap between the two apps */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 2be7a49..29fff03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -71,6 +71,7 @@
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellController;
@@ -200,6 +201,19 @@
@Override
public void setSplitScreenController(SplitScreenController splitScreenController) {
mSplitScreenController = splitScreenController;
+ mSplitScreenController.registerSplitScreenListener(new SplitScreen.SplitScreenListener() {
+ @Override
+ public void onTaskStageChanged(int taskId, int stage, boolean visible) {
+ if (visible) {
+ DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId);
+ if (decor != null && DesktopModeStatus.isActive(mContext)
+ && decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false));
+ mDesktopTasksController.ifPresent(c -> c.moveToSplit(decor.mTaskInfo));
+ }
+ }
+ }
+ });
}
@Override
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index 42be818..43e7696 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.pip
import android.graphics.Rect
-import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -82,8 +81,8 @@
/**
* Checks that the visible region area of [pipApp] moves to closest edge during the animation.
*/
- @Presubmit
@Test
+ @FlakyTest(bugId = 294993100)
fun pipLayerMovesToClosestEdge() {
flicker.assertLayers {
val pipLayerList = layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
@@ -97,6 +96,90 @@
}
}
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun hasAtMostOnePipDismissOverlayWindow() {
+ super.hasAtMostOnePipDismissOverlayWindow()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun navBarLayerIsVisibleAtStartAndEnd() {
+ super.navBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun navBarLayerPositionAtStartAndEnd() {
+ super.navBarLayerPositionAtStartAndEnd()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun navBarWindowIsAlwaysVisible() {
+ super.navBarWindowIsAlwaysVisible()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun statusBarLayerPositionAtStartAndEnd() {
+ super.statusBarLayerPositionAtStartAndEnd()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun statusBarWindowIsAlwaysVisible() {
+ super.statusBarWindowIsAlwaysVisible()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ // Overridden to remove @Presubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun taskBarWindowIsAlwaysVisible() {
+ super.taskBarWindowIsAlwaysVisible()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index 096af39..2008d01 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -92,7 +92,7 @@
@Presubmit
@Test
- fun hasAtMostOnePipDismissOverlayWindow() {
+ open fun hasAtMostOnePipDismissOverlayWindow() {
val matcher = ComponentNameMatcher("", "pip-dismiss-overlay")
flicker.assertWm {
val overlaysPerState =
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt
index 610cede..fa723e3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt
@@ -23,6 +23,7 @@
import android.tools.common.NavBar
import android.tools.common.Rotation
import android.tools.device.apphelpers.MessagingAppHelper
+import android.tools.device.flicker.rules.ArtifactSaverRule
import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.flicker.rules.LaunchAppRule
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
@@ -33,9 +34,10 @@
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
fun testSetupRule(navigationMode: NavBar, rotation: Rotation): RuleChain {
- return RuleChain.outerRule(UnlockScreenRule())
+ return RuleChain.outerRule(ArtifactSaverRule())
+ .around(UnlockScreenRule())
.around(
- NavigationModeRule(navigationMode.value, /* changeNavigationModeAfterTest */ false)
+ NavigationModeRule(navigationMode.value, false)
)
.around(
LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 1e3fe42..248d665 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -53,6 +53,7 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.splitscreen.SplitScreenController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 0ae2908..911f5e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -56,12 +56,12 @@
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -76,7 +76,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.Optional;
@@ -329,21 +328,7 @@
}
@Test
- public void onKeepClearAreasChanged_featureDisabled_pipBoundsStateDoesntChange() {
- mPipController.setEnablePipKeepClearAlgorithm(false);
- final int displayId = 1;
- final Rect keepClearArea = new Rect(0, 0, 10, 10);
- when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId);
-
- mPipController.mDisplaysChangedListener.onKeepClearAreasChanged(
- displayId, Set.of(keepClearArea), Set.of());
-
- verify(mMockPipBoundsState, never()).setKeepClearAreas(Mockito.anySet(), Mockito.anySet());
- }
-
- @Test
- public void onKeepClearAreasChanged_featureEnabled_updatesPipBoundsState() {
- mPipController.setEnablePipKeepClearAlgorithm(true);
+ public void onKeepClearAreasChanged_updatesPipBoundsState() {
final int displayId = 1;
final Rect keepClearArea = new Rect(0, 0, 10, 10);
when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(displayId);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 689b5c5..12b4f3e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -37,6 +37,7 @@
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
@@ -45,7 +46,6 @@
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.PipUiEventLogger;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 852183c..314f195d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -18,7 +18,6 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -34,6 +33,7 @@
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
@@ -42,7 +42,6 @@
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
@@ -174,16 +173,4 @@
verify(mPipResizeGestureHandler, times(1))
.updateMaxSize(expectedMaxSize.getWidth(), expectedMaxSize.getHeight());
}
-
- @Test
- public void updateMovementBounds_withImeAdjustment_movesPip() {
- mPipTouchHandler.setEnablePipKeepClearAlgorithm(false);
- mFromImeAdjustment = true;
- mPipTouchHandler.onImeVisibilityChanged(true /* imeVisible */, mImeHeight);
-
- mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mPipBounds, mCurBounds,
- mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
-
- verify(mMotionHelper, times(1)).animateToOffset(any(), anyInt());
- }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
index ec84d7e..45f6c8c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipActionProviderTest.java
@@ -38,7 +38,7 @@
import android.testing.TestableLooper;
import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipMediaController;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 2c69522..9e9e1ca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.recents;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
@@ -367,6 +368,37 @@
verify(mRecentTasksController).notifyRecentTasksChanged();
}
+ @Test
+ public void getNullSplitBoundsNonSplitTask() {
+ SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(3);
+ assertNull("splitBounds should be null for non-split task", sb);
+ }
+
+ @Test
+ public void getNullSplitBoundsInvalidTask() {
+ SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(INVALID_TASK_ID);
+ assertNull("splitBounds should be null for invalid taskID", sb);
+ }
+
+ @Test
+ public void getSplitBoundsForSplitTask() {
+ SplitBounds pair1Bounds = mock(SplitBounds.class);
+ SplitBounds pair2Bounds = mock(SplitBounds.class);
+
+ mRecentTasksController.addSplitPair(1, 2, pair1Bounds);
+ mRecentTasksController.addSplitPair(4, 3, pair2Bounds);
+
+ SplitBounds splitBounds2 = mRecentTasksController.getSplitBoundsForTaskId(2);
+ SplitBounds splitBounds1 = mRecentTasksController.getSplitBoundsForTaskId(1);
+ assertEquals("Different splitBounds for same pair", splitBounds1, splitBounds2);
+ assertEquals(splitBounds1, pair1Bounds);
+
+ SplitBounds splitBounds3 = mRecentTasksController.getSplitBoundsForTaskId(3);
+ SplitBounds splitBounds4 = mRecentTasksController.getSplitBoundsForTaskId(4);
+ assertEquals("Different splitBounds for same pair", splitBounds3, splitBounds4);
+ assertEquals(splitBounds4, pair2Bounds);
+ }
+
/**
* Helper to create a task with a given task id.
*/
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index fb2b571..795bb3c 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -91,7 +91,7 @@
path.appendPath(kResourceCache);
char buf[256]; // 256 chars should be enough for anyone...
- strncpy(buf, pkgPath.string(), 255);
+ strncpy(buf, pkgPath.c_str(), 255);
buf[255] = '\0';
char* filename = buf;
while (*filename && *filename == '/') {
@@ -183,15 +183,15 @@
if (kAppZipName) {
realPath.appendPath(kAppZipName);
}
- ap.type = ::getFileType(realPath.string());
+ ap.type = ::getFileType(realPath.c_str());
if (ap.type == kFileTypeRegular) {
ap.path = realPath;
} else {
ap.path = path;
- ap.type = ::getFileType(path.string());
+ ap.type = ::getFileType(path.c_str());
if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) {
ALOGW("Asset path %s is neither a directory nor file (type=%d).",
- path.string(), (int)ap.type);
+ path.c_str(), (int)ap.type);
return false;
}
}
@@ -207,7 +207,7 @@
}
ALOGV("In %p Asset %s path: %s", this,
- ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string());
+ ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.c_str());
ap.isSystemAsset = isSystemAsset;
ssize_t apPos = mAssetPaths.add(ap);
@@ -248,7 +248,7 @@
Asset* idmap = NULL;
if ((idmap = openAssetFromFileLocked(idmapPath, Asset::ACCESS_BUFFER)) == NULL) {
- ALOGW("failed to open idmap file %s\n", idmapPath.string());
+ ALOGW("failed to open idmap file %s\n", idmapPath.c_str());
return false;
}
@@ -256,7 +256,7 @@
String8 overlayPath;
if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(),
NULL, NULL, NULL, &targetPath, &overlayPath)) {
- ALOGW("failed to read idmap file %s\n", idmapPath.string());
+ ALOGW("failed to read idmap file %s\n", idmapPath.c_str());
delete idmap;
return false;
}
@@ -264,29 +264,29 @@
if (overlayPath != packagePath) {
ALOGW("idmap file %s inconcistent: expected path %s does not match actual path %s\n",
- idmapPath.string(), packagePath.string(), overlayPath.string());
+ idmapPath.c_str(), packagePath.c_str(), overlayPath.c_str());
return false;
}
- if (access(targetPath.string(), R_OK) != 0) {
- ALOGW("failed to access file %s: %s\n", targetPath.string(), strerror(errno));
+ if (access(targetPath.c_str(), R_OK) != 0) {
+ ALOGW("failed to access file %s: %s\n", targetPath.c_str(), strerror(errno));
return false;
}
- if (access(idmapPath.string(), R_OK) != 0) {
- ALOGW("failed to access file %s: %s\n", idmapPath.string(), strerror(errno));
+ if (access(idmapPath.c_str(), R_OK) != 0) {
+ ALOGW("failed to access file %s: %s\n", idmapPath.c_str(), strerror(errno));
return false;
}
- if (access(overlayPath.string(), R_OK) != 0) {
- ALOGW("failed to access file %s: %s\n", overlayPath.string(), strerror(errno));
+ if (access(overlayPath.c_str(), R_OK) != 0) {
+ ALOGW("failed to access file %s: %s\n", overlayPath.c_str(), strerror(errno));
return false;
}
asset_path oap;
oap.path = overlayPath;
- oap.type = ::getFileType(overlayPath.string());
+ oap.type = ::getFileType(overlayPath.c_str());
oap.idmap = idmapPath;
#if 0
ALOGD("Overlay added: targetPath=%s overlayPath=%s idmapPath=%s\n",
- targetPath.string(), overlayPath.string(), idmapPath.string());
+ targetPath.c_str(), overlayPath.c_str(), idmapPath.c_str());
#endif
mAssetPaths.add(oap);
*cookie = static_cast<int32_t>(mAssetPaths.size());
@@ -310,7 +310,7 @@
ap.type = kFileTypeRegular;
ap.assumeOwnership = assume_ownership;
- ALOGV("In %p Asset fd %d name: %s", this, fd, ap.path.string());
+ ALOGV("In %p Asset fd %d name: %s", this, fd, ap.path.c_str());
ssize_t apPos = mAssetPaths.add(ap);
@@ -343,11 +343,11 @@
assets[i] = openNonAssetInPathLocked("resources.arsc",
Asset::ACCESS_BUFFER, ap);
if (assets[i] == NULL) {
- ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
+ ALOGW("failed to find resources.arsc in %s\n", ap.path.c_str());
goto exit;
}
if (tables[i].add(assets[i]) != NO_ERROR) {
- ALOGW("failed to add %s to resource table", paths[i].string());
+ ALOGW("failed to add %s to resource table", paths[i].c_str());
goto exit;
}
}
@@ -449,8 +449,8 @@
while (i > 0) {
i--;
ALOGV("Looking for asset '%s' in '%s'\n",
- assetName.string(), mAssetPaths.itemAt(i).path.string());
- Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode,
+ assetName.c_str(), mAssetPaths.itemAt(i).path.c_str());
+ Asset* pAsset = openNonAssetInPathLocked(assetName.c_str(), mode,
mAssetPaths.editItemAt(i));
if (pAsset != NULL) {
return pAsset != kExcludedAsset ? pAsset : NULL;
@@ -478,7 +478,7 @@
size_t i = mAssetPaths.size();
while (i > 0) {
i--;
- ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string());
+ ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.c_str());
Asset* pAsset = openNonAssetInPathLocked(
fileName, mode, mAssetPaths.editItemAt(i));
if (pAsset != NULL) {
@@ -500,7 +500,7 @@
if (which < mAssetPaths.size()) {
ALOGV("Looking for non-asset '%s' in '%s'\n", fileName,
- mAssetPaths.itemAt(which).path.string());
+ mAssetPaths.itemAt(which).path.c_str());
Asset* pAsset = openNonAssetInPathLocked(
fileName, mode, mAssetPaths.editItemAt(which));
if (pAsset != NULL) {
@@ -546,10 +546,10 @@
ResTable* sharedRes = NULL;
bool shared = true;
bool onlyEmptyResources = true;
- ATRACE_NAME(ap.path.string());
+ ATRACE_NAME(ap.path.c_str());
Asset* idmap = openIdmapLocked(ap);
size_t nextEntryIdx = mResources->getTableCount();
- ALOGV("Looking for resource asset in '%s'\n", ap.path.string());
+ ALOGV("Looking for resource asset in '%s'\n", ap.path.c_str());
if (ap.type != kFileTypeDirectory && ap.rawFd < 0) {
if (nextEntryIdx == 0) {
// The first item is typically the framework resources,
@@ -565,7 +565,7 @@
ass = const_cast<AssetManager*>(this)->
mZipSet.getZipResourceTableAsset(ap.path);
if (ass == NULL) {
- ALOGV("loading resource table %s\n", ap.path.string());
+ ALOGV("loading resource table %s\n", ap.path.c_str());
ass = const_cast<AssetManager*>(this)->
openNonAssetInPathLocked("resources.arsc",
Asset::ACCESS_BUFFER,
@@ -580,7 +580,7 @@
// If this is the first resource table in the asset
// manager, then we are going to cache it so that we
// can quickly copy it out for others.
- ALOGV("Creating shared resources for %s", ap.path.string());
+ ALOGV("Creating shared resources for %s", ap.path.c_str());
sharedRes = new ResTable();
sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
#ifdef __ANDROID__
@@ -589,14 +589,14 @@
String8 overlaysListPath(data);
overlaysListPath.appendPath(kResourceCache);
overlaysListPath.appendPath("overlays.list");
- addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx);
+ addSystemOverlays(overlaysListPath.c_str(), ap.path, sharedRes, nextEntryIdx);
#endif
sharedRes = const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTable(ap.path, sharedRes);
}
}
} else {
- ALOGV("loading resource table %s\n", ap.path.string());
+ ALOGV("loading resource table %s\n", ap.path.c_str());
ass = const_cast<AssetManager*>(this)->
openNonAssetInPathLocked("resources.arsc",
Asset::ACCESS_BUFFER,
@@ -607,10 +607,10 @@
if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
ALOGV("Installing resource asset %p in to table %p\n", ass, mResources);
if (sharedRes != NULL) {
- ALOGV("Copying existing resources for %s", ap.path.string());
+ ALOGV("Copying existing resources for %s", ap.path.c_str());
mResources->add(sharedRes, ap.isSystemAsset);
} else {
- ALOGV("Parsing resources for %s", ap.path.string());
+ ALOGV("Parsing resources for %s", ap.path.c_str());
mResources->add(ass, idmap, nextEntryIdx + 1, !shared, appAsLib, ap.isSystemAsset);
}
onlyEmptyResources = false;
@@ -692,9 +692,9 @@
ass = const_cast<AssetManager*>(this)->
openAssetFromFileLocked(ap.idmap, Asset::ACCESS_BUFFER);
if (ass) {
- ALOGV("loading idmap %s\n", ap.idmap.string());
+ ALOGV("loading idmap %s\n", ap.idmap.c_str());
} else {
- ALOGW("failed to load idmap %s\n", ap.idmap.string());
+ ALOGW("failed to load idmap %s\n", ap.idmap.c_str());
}
}
return ass;
@@ -812,7 +812,7 @@
ZipFileRO* pZip = getZipFileLocked(ap);
if (pZip != NULL) {
ALOGV("GOT zip, checking NA '%s'", (const char*) path);
- ZipEntryRO entry = pZip->findEntryByName(path.string());
+ ZipEntryRO entry = pZip->findEntryByName(path.c_str());
if (entry != NULL) {
ALOGV("FOUND NA in Zip file for %s", (const char*) path);
pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
@@ -823,7 +823,7 @@
if (pAsset != NULL) {
/* create a "source" name, for debug/display */
pAsset->setAssetSource(
- createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), String8(""),
+ createZipSourceNameLocked(ZipSet::getPathName(ap.path.c_str()), String8(""),
String8(fileName)));
}
}
@@ -870,7 +870,7 @@
}
if (ap.rawFd < 0) {
- ALOGV("getZipFileLocked: Creating new zip from path %s", ap.path.string());
+ ALOGV("getZipFileLocked: Creating new zip from path %s", ap.path.c_str());
ap.zip = mZipSet.getSharedZip(ap.path);
} else {
ALOGV("getZipFileLocked: Creating new zip from fd %d", ap.rawFd);
@@ -897,12 +897,12 @@
{
Asset* pAsset = NULL;
- if (strcasecmp(pathName.getPathExtension().string(), ".gz") == 0) {
+ if (strcasecmp(pathName.getPathExtension().c_str(), ".gz") == 0) {
//printf("TRYING '%s'\n", (const char*) pathName);
- pAsset = Asset::createFromCompressedFile(pathName.string(), mode);
+ pAsset = Asset::createFromCompressedFile(pathName.c_str(), mode);
} else {
//printf("TRYING '%s'\n", (const char*) pathName);
- pAsset = Asset::createFromFile(pathName.string(), mode);
+ pAsset = Asset::createFromFile(pathName.c_str(), mode);
}
return pAsset;
@@ -940,12 +940,12 @@
if (method == ZipFileRO::kCompressStored) {
pAsset = Asset::createFromUncompressedMap(std::move(*dataMap), mode);
- ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(),
+ ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.c_str(),
dataMap->file_name(), mode, pAsset.get());
} else {
pAsset = Asset::createFromCompressedMap(std::move(*dataMap),
static_cast<size_t>(uncompressedLen), mode);
- ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(),
+ ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.c_str(),
dataMap->file_name(), mode, pAsset.get());
}
if (pAsset == NULL) {
@@ -993,10 +993,10 @@
i--;
const asset_path& ap = mAssetPaths.itemAt(i);
if (ap.type == kFileTypeRegular) {
- ALOGV("Adding directory %s from zip %s", dirName, ap.path.string());
+ ALOGV("Adding directory %s from zip %s", dirName, ap.path.c_str());
scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName);
} else {
- ALOGV("Adding directory %s from dir %s", dirName, ap.path.string());
+ ALOGV("Adding directory %s from dir %s", dirName, ap.path.c_str());
scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName);
}
}
@@ -1042,10 +1042,10 @@
if (which < mAssetPaths.size()) {
const asset_path& ap = mAssetPaths.itemAt(which);
if (ap.type == kFileTypeRegular) {
- ALOGV("Adding directory %s from zip %s", dirName, ap.path.string());
+ ALOGV("Adding directory %s from zip %s", dirName, ap.path.c_str());
scanAndMergeZipLocked(pMergedInfo, ap, NULL, dirName);
} else {
- ALOGV("Adding directory %s from dir %s", dirName, ap.path.string());
+ ALOGV("Adding directory %s from dir %s", dirName, ap.path.c_str());
scanAndMergeDirLocked(pMergedInfo, ap, NULL, dirName);
}
}
@@ -1075,7 +1075,7 @@
{
assert(pMergedInfo != NULL);
- //printf("scanAndMergeDir: %s %s %s\n", ap.path.string(), rootDir, dirName);
+ //printf("scanAndMergeDir: %s %s %s\n", ap.path.c_str(), rootDir, dirName);
String8 path = createPathNameLocked(ap, rootDir);
if (dirName[0] != '\0')
@@ -1100,7 +1100,7 @@
const char* name;
int nameLen;
- name = pContents->itemAt(i).getFileName().string();
+ name = pContents->itemAt(i).getFileName().c_str();
nameLen = strlen(name);
if (nameLen > exclExtLen &&
strcmp(name + (nameLen - exclExtLen), kExcludeExtension) == 0)
@@ -1111,8 +1111,8 @@
matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match);
if (matchIdx > 0) {
ALOGV("Excluding '%s' [%s]\n",
- pMergedInfo->itemAt(matchIdx).getFileName().string(),
- pMergedInfo->itemAt(matchIdx).getSourceName().string());
+ pMergedInfo->itemAt(matchIdx).getFileName().c_str(),
+ pMergedInfo->itemAt(matchIdx).getSourceName().c_str());
pMergedInfo->removeAt(matchIdx);
} else {
//printf("+++ no match on '%s'\n", (const char*) match);
@@ -1150,9 +1150,9 @@
struct dirent* entry;
FileType fileType;
- ALOGV("Scanning dir '%s'\n", path.string());
+ ALOGV("Scanning dir '%s'\n", path.c_str());
- dir = opendir(path.string());
+ dir = opendir(path.c_str());
if (dir == NULL)
return NULL;
@@ -1176,7 +1176,7 @@
fileType = kFileTypeUnknown;
#else
// stat the file
- fileType = ::getFileType(path.appendPathCopy(entry->d_name).string());
+ fileType = ::getFileType(path.appendPathCopy(entry->d_name).c_str());
#endif
if (fileType != kFileTypeRegular && fileType != kFileTypeDirectory)
@@ -1184,7 +1184,7 @@
AssetDir::FileInfo info;
info.set(String8(entry->d_name), fileType);
- if (strcasecmp(info.getFileName().getPathExtension().string(), ".gz") == 0)
+ if (strcasecmp(info.getFileName().getPathExtension().c_str(), ".gz") == 0)
info.setFileName(info.getFileName().getBasePath());
info.setSourceName(path.appendPathCopy(info.getFileName()));
pContents->add(info);
@@ -1212,11 +1212,11 @@
pZip = mZipSet.getZip(ap.path);
if (pZip == NULL) {
- ALOGW("Failure opening zip %s\n", ap.path.string());
+ ALOGW("Failure opening zip %s\n", ap.path.c_str());
return false;
}
- zipName = ZipSet::getPathName(ap.path.string());
+ zipName = ZipSet::getPathName(ap.path.c_str());
/* convert "sounds" to "rootDir/sounds" */
if (rootDir != NULL) dirName = rootDir;
@@ -1240,7 +1240,7 @@
*/
int dirNameLen = dirName.length();
void *iterationCookie;
- if (!pZip->startIteration(&iterationCookie, dirName.string(), NULL)) {
+ if (!pZip->startIteration(&iterationCookie, dirName.c_str(), NULL)) {
ALOGW("ZipFileRO::startIteration returned false");
return false;
}
@@ -1254,7 +1254,7 @@
ALOGE("ARGH: name too long?\n");
continue;
}
- //printf("Comparing %s in %s?\n", nameBuf, dirName.string());
+ //printf("Comparing %s in %s?\n", nameBuf, dirName.c_str());
if (dirNameLen == 0 || nameBuf[dirNameLen] == '/')
{
const char* cp;
@@ -1275,7 +1275,7 @@
createZipSourceNameLocked(zipName, dirName, info.getFileName()));
contents.add(info);
- //printf("FOUND: file '%s'\n", info.getFileName().string());
+ //printf("FOUND: file '%s'\n", info.getFileName().c_str());
} else {
/* this is a subdir; add it if we don't already have it*/
String8 subdirName(cp, nextSlash - cp);
@@ -1291,7 +1291,7 @@
dirs.add(subdirName);
}
- //printf("FOUND: dir '%s'\n", subdirName.string());
+ //printf("FOUND: dir '%s'\n", subdirName.c_str());
}
}
}
@@ -1427,10 +1427,10 @@
if (kIsDebug) {
ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath);
}
- ALOGV("+++ opening zip '%s'\n", mPath.string());
- mZipFile = ZipFileRO::open(mPath.string());
+ ALOGV("+++ opening zip '%s'\n", mPath.c_str());
+ mZipFile = ZipFileRO::open(mPath.c_str());
if (mZipFile == NULL) {
- ALOGD("failed to open Zip archive '%s'\n", mPath.string());
+ ALOGD("failed to open Zip archive '%s'\n", mPath.c_str());
}
}
@@ -1441,11 +1441,11 @@
if (kIsDebug) {
ALOGI("Creating SharedZip %p fd=%d %s\n", this, fd, (const char*)mPath);
}
- ALOGV("+++ opening zip fd=%d '%s'\n", fd, mPath.string());
- mZipFile = ZipFileRO::openFd(fd, mPath.string());
+ ALOGV("+++ opening zip fd=%d '%s'\n", fd, mPath.c_str());
+ mZipFile = ZipFileRO::openFd(fd, mPath.c_str());
if (mZipFile == NULL) {
::close(fd);
- ALOGD("failed to open Zip archive fd=%d '%s'\n", fd, mPath.string());
+ ALOGD("failed to open Zip archive fd=%d '%s'\n", fd, mPath.c_str());
}
}
@@ -1520,7 +1520,7 @@
bool AssetManager::SharedZip::isUpToDate()
{
- time_t modWhen = getFileModDate(mPath.string());
+ time_t modWhen = getFileModDate(mPath.c_str());
return mModWhen == modWhen;
}
@@ -1551,7 +1551,7 @@
}
if (mZipFile != NULL) {
delete mZipFile;
- ALOGV("Closed '%s'\n", mPath.string());
+ ALOGV("Closed '%s'\n", mPath.c_str());
}
}
diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp
index 76a430e..fec0e77 100644
--- a/libs/androidfw/BackupData.cpp
+++ b/libs/androidfw/BackupData.cpp
@@ -106,8 +106,8 @@
k = key;
}
if (kIsDebug) {
- ALOGD("Writing header: prefix='%s' key='%s' dataSize=%zu", m_keyPrefix.string(),
- key.string(), dataSize);
+ ALOGD("Writing header: prefix='%s' key='%s' dataSize=%zu", m_keyPrefix.c_str(),
+ key.c_str(), dataSize);
}
entity_header_v1 header;
@@ -128,7 +128,7 @@
m_pos += amt;
if (kIsDebug) ALOGI("writing entity header key, %zd bytes", keyLen+1);
- amt = write(m_fd, k.string(), keyLen+1);
+ amt = write(m_fd, k.c_str(), keyLen+1);
if (amt != keyLen+1) {
m_status = errno;
return m_status;
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index e80e948..3582609 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -179,7 +179,7 @@
}
// filename is not NULL terminated, but it is padded
- amt = write(fd, name.string(), nameLen);
+ amt = write(fd, name.c_str(), nameLen);
if (amt != nameLen) {
ALOGW("write_snapshot_file error writing filename %s", strerror(errno));
return 1;
@@ -203,7 +203,7 @@
static int
write_delete_file(BackupDataWriter* dataStream, const String8& key)
{
- LOGP("write_delete_file %s\n", key.string());
+ LOGP("write_delete_file %s\n", key.c_str());
return dataStream->WriteEntityHeader(key, -1);
}
@@ -211,7 +211,7 @@
write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& key,
char const* realFilename)
{
- LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.string(), mode);
+ LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.c_str(), mode);
const int bufsize = 4*1024;
int err;
@@ -365,7 +365,7 @@
r.s.size = st.st_size;
if (newSnapshot.indexOfKey(key) >= 0) {
- LOGP("back_up_files key already in use '%s'", key.string());
+ LOGP("back_up_files key already in use '%s'", key.c_str());
return -1;
}
@@ -390,30 +390,30 @@
int cmp = p.compare(q);
if (cmp < 0) {
// file present in oldSnapshot, but not present in newSnapshot
- LOGP("file removed: %s", p.string());
+ LOGP("file removed: %s", p.c_str());
write_delete_file(dataStream, p);
n++;
} else if (cmp > 0) {
// file added
- LOGP("file added: %s crc=0x%08x", g.file.string(), g.s.crc32);
- write_update_file(dataStream, q, g.file.string());
+ LOGP("file added: %s crc=0x%08x", g.file.c_str(), g.s.crc32);
+ write_update_file(dataStream, q, g.file.c_str());
m++;
} else {
// same file exists in both old and new; check whether to update
const FileState& f = oldSnapshot.valueAt(n);
- LOGP("%s", q.string());
+ LOGP("%s", q.c_str());
LOGP(" old: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x",
f.modTime_sec, f.modTime_nsec, f.mode, f.size, f.crc32);
LOGP(" new: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x",
g.s.modTime_sec, g.s.modTime_nsec, g.s.mode, g.s.size, g.s.crc32);
if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec
|| f.mode != g.s.mode || f.size != g.s.size || f.crc32 != g.s.crc32) {
- int fd = open(g.file.string(), O_RDONLY);
+ int fd = open(g.file.c_str(), O_RDONLY);
if (fd < 0) {
- ALOGE("Unable to read file for backup: %s", g.file.string());
+ ALOGE("Unable to read file for backup: %s", g.file.c_str());
} else {
- write_update_file(dataStream, fd, g.s.mode, p, g.file.string());
+ write_update_file(dataStream, fd, g.s.mode, p, g.file.c_str());
close(fd);
}
}
@@ -432,7 +432,7 @@
while (m<M) {
const String8& q = newSnapshot.keyAt(m);
FileRec& g = newSnapshot.editValueAt(m);
- write_update_file(dataStream, q, g.file.string());
+ write_update_file(dataStream, q, g.file.c_str());
m++;
}
@@ -483,7 +483,7 @@
BackupDataWriter* writer)
{
// In the output stream everything is stored relative to the root
- const char* relstart = filepath.string() + rootpath.length();
+ const char* relstart = filepath.c_str() + rootpath.length();
if (*relstart == '/') relstart++; // won't be true when path == rootpath
String8 relpath(relstart);
@@ -514,9 +514,9 @@
int err = 0;
struct stat64 s;
- if (lstat64(filepath.string(), &s) != 0) {
+ if (lstat64(filepath.c_str(), &s) != 0) {
err = errno;
- ALOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string());
+ ALOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.c_str());
return err;
}
@@ -541,10 +541,10 @@
// !!! TODO: use mmap when possible to avoid churning the buffer cache
// !!! TODO: this will break with symlinks; need to use readlink(2)
- int fd = open(filepath.string(), O_RDONLY);
+ int fd = open(filepath.c_str(), O_RDONLY);
if (fd < 0) {
err = errno;
- ALOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string());
+ ALOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.c_str());
return err;
}
@@ -592,7 +592,7 @@
} else if (S_ISREG(s.st_mode)) {
type = '0'; // tar magic: '0' == normal file
} else {
- ALOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string());
+ ALOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.c_str());
goto cleanup;
}
buf[156] = type;
@@ -620,16 +620,16 @@
// [ 345 : 155 ] filename path prefix
// We only use the prefix area if fullname won't fit in the path
if (fullname.length() > 100) {
- strncpy(buf, relpath.string(), 100);
- strncpy(buf + 345, prefix.string(), 155);
+ strncpy(buf, relpath.c_str(), 100);
+ strncpy(buf + 345, prefix.c_str(), 155);
} else {
- strncpy(buf, fullname.string(), 100);
+ strncpy(buf, fullname.c_str(), 100);
}
}
// [ 329 : 8 ] and [ 337 : 8 ] devmajor/devminor, not used
- ALOGI(" Name: %s", fullname.string());
+ ALOGI(" Name: %s", fullname.c_str());
// If we're using a pax extended header, build & write that here; lengths are
// already preflighted
@@ -647,7 +647,7 @@
// fullname was generated above with the ustar paths
paxLen += write_pax_header_entry(paxData + paxLen, PAXDATA_SIZE - paxLen,
- "path", fullname.string());
+ "path", fullname.c_str());
// Now we know how big the pax data is
@@ -656,9 +656,9 @@
String8 leaf = fullname.getPathLeaf();
memset(paxHeader, 0, 100); // rewrite the name area
- snprintf(paxHeader, 100, "PaxHeader/%s", leaf.string());
+ snprintf(paxHeader, 100, "PaxHeader/%s", leaf.c_str());
memset(paxHeader + 345, 0, 155); // rewrite the prefix area
- strncpy(paxHeader + 345, prefix.string(), 155);
+ strncpy(paxHeader + 345, prefix.c_str(), 155);
paxHeader[156] = 'x'; // mark it as a pax extended header
@@ -691,12 +691,12 @@
ssize_t nRead = read(fd, buf, toRead);
if (nRead < 0) {
err = errno;
- ALOGE("Unable to read file [%s], err=%d (%s)", filepath.string(),
+ ALOGE("Unable to read file [%s], err=%d (%s)", filepath.c_str(),
err, strerror(err));
break;
} else if (nRead == 0) {
ALOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite,
- filepath.string());
+ filepath.c_str());
err = EIO;
break;
}
@@ -762,7 +762,7 @@
file_metadata_v1 metadata;
amt = in->ReadEntityData(&metadata, sizeof(metadata));
if (amt != sizeof(metadata)) {
- ALOGW("Could not read metadata for %s -- %ld / %s", filename.string(),
+ ALOGW("Could not read metadata for %s -- %ld / %s", filename.c_str(),
(long)amt, strerror(errno));
return EIO;
}
@@ -779,9 +779,9 @@
// Write the file and compute the crc
crc = crc32(0L, Z_NULL, 0);
- fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode);
+ fd = open(filename.c_str(), O_CREAT|O_RDWR|O_TRUNC, mode);
if (fd == -1) {
- ALOGW("Could not open file %s -- %s", filename.string(), strerror(errno));
+ ALOGW("Could not open file %s -- %s", filename.c_str(), strerror(errno));
return errno;
}
@@ -789,7 +789,7 @@
err = write(fd, buf, amt);
if (err != amt) {
close(fd);
- ALOGW("Error '%s' writing '%s'", strerror(errno), filename.string());
+ ALOGW("Error '%s' writing '%s'", strerror(errno), filename.c_str());
return errno;
}
crc = crc32(crc, (Bytef*)buf, amt);
@@ -798,9 +798,9 @@
close(fd);
// Record for the snapshot
- err = stat(filename.string(), &st);
+ err = stat(filename.c_str(), &st);
if (err != 0) {
- ALOGW("Error stating file that we just created %s", filename.string());
+ ALOGW("Error stating file that we just created %s", filename.c_str());
return errno;
}
@@ -1104,9 +1104,9 @@
fprintf(stderr, "state %zu expected={%d/%d, %04o, 0x%08x, 0x%08x, %3zu} '%s'\n"
" actual={%d/%d, %04o, 0x%08x, 0x%08x, %3d} '%s'\n", i,
states[i].modTime_sec, states[i].modTime_nsec, states[i].mode, states[i].size,
- states[i].crc32, name.length(), filenames[i].string(),
+ states[i].crc32, name.length(), filenames[i].c_str(),
state.modTime_sec, state.modTime_nsec, state.mode, state.size, state.crc32,
- state.nameLen, name.string());
+ state.nameLen, name.c_str());
matched = false;
}
}
@@ -1152,9 +1152,9 @@
return err;
}
- err = writer.WriteEntityData(text.string(), text.length()+1);
+ err = writer.WriteEntityData(text.c_str(), text.length()+1);
if (err != 0) {
- fprintf(stderr, "write failed for data '%s'\n", text.string());
+ fprintf(stderr, "write failed for data '%s'\n", text.c_str());
return errno;
}
@@ -1230,7 +1230,7 @@
goto finished;
}
if (string != str) {
- fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string());
+ fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.c_str());
err = EINVAL;
goto finished;
}
diff --git a/libs/androidfw/ConfigDescription.cpp b/libs/androidfw/ConfigDescription.cpp
index cf2fd6f..e08030c 100644
--- a/libs/androidfw/ConfigDescription.cpp
+++ b/libs/androidfw/ConfigDescription.cpp
@@ -905,7 +905,7 @@
std::string ConfigDescription::to_string() const {
const String8 str = toString();
- return std::string(str.string(), str.size());
+ return std::string(str.c_str(), str.size());
}
bool ConfigDescription::Dominates(const ConfigDescription& o) const {
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 2a6dc7b..5e645cc 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -84,7 +84,7 @@
String8 ashmemName("CursorWindow: ");
ashmemName.append(mName);
- ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize);
+ ashmemFd = ashmem_create_region(ashmemName.c_str(), mInflatedSize);
if (ashmemFd < 0) {
PLOG(ERROR) << "Failed ashmem_create_region";
goto fail_silent;
diff --git a/libs/androidfw/ObbFile.cpp b/libs/androidfw/ObbFile.cpp
index 95332a3..c6a9632 100644
--- a/libs/androidfw/ObbFile.cpp
+++ b/libs/androidfw/ObbFile.cpp
@@ -217,7 +217,7 @@
free(scanBuf);
#ifdef DEBUG
- ALOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion);
+ ALOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.c_str(), mVersion);
#endif
return true;
@@ -288,7 +288,7 @@
return false;
}
- if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) {
+ if (write(fd, mPackageName.c_str(), packageNameLen) != (ssize_t)packageNameLen) {
ALOGW("couldn't write package name: %s\n", strerror(errno));
return false;
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 5a63612..112058f 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1042,7 +1042,7 @@
if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) {
if (kDebugStringPoolNoisy) {
- ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string());
+ ALOGI("indexOfString UTF-8: %s", String8(str, strLen).c_str());
}
// The string pool contains UTF 8 strings; we don't want to cause
@@ -1103,7 +1103,7 @@
ALOGI("Looking at %s, i=%d\n", s->data(), i);
}
if (str8Len == s->size()
- && memcmp(s->data(), str8.string(), str8Len) == 0) {
+ && memcmp(s->data(), str8.c_str(), str8Len) == 0) {
if (kDebugStringPoolNoisy) {
ALOGI("MATCH!");
}
@@ -1115,7 +1115,7 @@
} else {
if (kDebugStringPoolNoisy) {
- ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string());
+ ALOGI("indexOfString UTF-16: %s", String8(str, strLen).c_str());
}
if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
@@ -1133,7 +1133,7 @@
int c = s.has_value() ? strzcmp16(s->data(), s->size(), str, strLen) : -1;
if (kDebugStringPoolNoisy) {
ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
- String8(s->data(), s->size()).string(), c, (int)l, (int)mid, (int)h);
+ String8(s->data(), s->size()).c_str(), c, (int)l, (int)mid, (int)h);
}
if (c == 0) {
if (kDebugStringPoolNoisy) {
@@ -1157,7 +1157,7 @@
return base::unexpected(s.error());
}
if (kDebugStringPoolNoisy) {
- ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).string(), i);
+ ALOGI("Looking 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) {
@@ -1525,8 +1525,8 @@
{
String16 nsStr(ns != NULL ? ns : "");
String16 attrStr(attr);
- return indexOfAttribute(ns ? nsStr.string() : NULL, ns ? nsStr.size() : 0,
- attrStr.string(), attrStr.size());
+ return indexOfAttribute(ns ? nsStr.c_str() : NULL, ns ? nsStr.size() : 0,
+ attrStr.c_str(), attrStr.size());
}
ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen,
@@ -1544,8 +1544,8 @@
}
attr8 = String8(attr, attrLen);
if (kDebugStringPoolNoisy) {
- ALOGI("indexOfAttribute UTF8 %s (%zu) / %s (%zu)", ns8.string(), nsLen,
- attr8.string(), attrLen);
+ ALOGI("indexOfAttribute UTF8 %s (%zu) / %s (%zu)", ns8.c_str(), nsLen,
+ attr8.c_str(), attrLen);
}
for (size_t i=0; i<N; i++) {
size_t curNsLen = 0, curAttrLen = 0;
@@ -1555,7 +1555,7 @@
ALOGI(" curNs=%s (%zu), curAttr=%s (%zu)", curNs, curNsLen, curAttr, curAttrLen);
}
if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen
- && memcmp(attr8.string(), curAttr, attrLen) == 0) {
+ && memcmp(attr8.c_str(), curAttr, attrLen) == 0) {
if (ns == NULL) {
if (curNs == NULL) {
if (kDebugStringPoolNoisy) {
@@ -1565,8 +1565,8 @@
}
} else if (curNs != NULL) {
//printf(" --> ns=%s, curNs=%s\n",
- // String8(ns).string(), String8(curNs).string());
- if (memcmp(ns8.string(), curNs, nsLen) == 0) {
+ // String8(ns).c_str(), String8(curNs).c_str());
+ if (memcmp(ns8.c_str(), curNs, nsLen) == 0) {
if (kDebugStringPoolNoisy) {
ALOGI(" FOUND!");
}
@@ -1578,8 +1578,8 @@
} else {
if (kDebugStringPoolNoisy) {
ALOGI("indexOfAttribute UTF16 %s (%zu) / %s (%zu)",
- String8(ns, nsLen).string(), nsLen,
- String8(attr, attrLen).string(), attrLen);
+ String8(ns, nsLen).c_str(), nsLen,
+ String8(attr, attrLen).c_str(), attrLen);
}
for (size_t i=0; i<N; i++) {
size_t curNsLen = 0, curAttrLen = 0;
@@ -1587,8 +1587,8 @@
const char16_t* curAttr = getAttributeName(i, &curAttrLen);
if (kDebugStringPoolNoisy) {
ALOGI(" curNs=%s (%zu), curAttr=%s (%zu)",
- String8(curNs, curNsLen).string(), curNsLen,
- String8(curAttr, curAttrLen).string(), curAttrLen);
+ String8(curNs, curNsLen).c_str(), curNsLen,
+ String8(curAttr, curAttrLen).c_str(), curAttrLen);
}
if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen
&& (memcmp(attr, curAttr, attrLen*sizeof(char16_t)) == 0)) {
@@ -1601,7 +1601,7 @@
}
} else if (curNs != NULL) {
//printf(" --> ns=%s, curNs=%s\n",
- // String8(ns).string(), String8(curNs).string());
+ // String8(ns).c_str(), String8(curNs).c_str());
if (memcmp(ns, curNs, nsLen*sizeof(char16_t)) == 0) {
if (kDebugStringPoolNoisy) {
ALOGI(" FOUND!");
@@ -4475,7 +4475,7 @@
return false;
}
- outName->package = grp->name.string();
+ outName->package = grp->name.c_str();
outName->packageLen = grp->name.size();
if (allowUtf8) {
outName->type8 = UnpackOptionalString(entry.typeStr.string8(), &outName->typeLen);
@@ -4575,7 +4575,7 @@
outValue->dataType,
outValue->dataType == Res_value::TYPE_STRING ?
String8(UnpackOptionalString(
- entry.package->header->values.stringAt(outValue->data), &len)).string() :
+ entry.package->header->values.stringAt(outValue->data), &len)).c_str() :
"",
outValue->data);
}
@@ -4944,7 +4944,7 @@
AutoMutex _lock2(mFilteredConfigLock);
if (kDebugTableGetEntry) {
- ALOGI("Setting parameters: %s\n", params->toString().string());
+ ALOGI("Setting parameters: %s\n", params->toString().c_str());
}
mParams = *params;
for (size_t p = 0; p < mPackageGroups.size(); p++) {
@@ -5055,7 +5055,7 @@
if (name[1] == 'i' && name[2] == 'n'
&& name[3] == 'd' && name[4] == 'e' && name[5] == 'x'
&& name[6] == '_') {
- int index = atoi(String8(name + 7, nameLen - 7).string());
+ int index = atoi(String8(name + 7, nameLen - 7).c_str());
if (Res_CHECKID(index)) {
ALOGW("Array resource index: %d is too large.",
index);
@@ -5121,9 +5121,9 @@
if (kDebugTableNoisy) {
printf("Looking for identifier: type=%s, name=%s, package=%s\n",
- String8(type, typeLen).string(),
- String8(name, nameLen).string(),
- String8(package, packageLen).string());
+ String8(type, typeLen).c_str(),
+ String8(name, nameLen).c_str(),
+ String8(package, packageLen).c_str());
}
const String16 attr("attr");
@@ -5134,9 +5134,9 @@
const PackageGroup* group = mPackageGroups[ig];
if (strzcmp16(package, packageLen,
- group->name.string(), group->name.size())) {
+ group->name.c_str(), group->name.size())) {
if (kDebugTableNoisy) {
- printf("Skipping package group: %s\n", String8(group->name).string());
+ printf("Skipping package group: %s\n", String8(group->name).c_str());
}
continue;
}
@@ -5161,8 +5161,8 @@
}
return identifier;
}
- } while (strzcmp16(attr.string(), attr.size(), targetType, targetTypeLen) == 0
- && (targetType = attrPrivate.string())
+ } while (strzcmp16(attr.c_str(), attr.size(), targetType, targetTypeLen) == 0
+ && (targetType = attrPrivate.c_str())
&& (targetTypeLen = attrPrivate.size())
);
}
@@ -5610,7 +5610,7 @@
}
}
- //printf("Value for: %s\n", String8(s, len).string());
+ //printf("Value for: %s\n", String8(s, len).c_str());
uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED;
uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff;
@@ -5665,7 +5665,7 @@
// be to any other type; we just need to count on the client making
// sure the referenced type is correct.
- //printf("Looking up ref: %s\n", String8(s, len).string());
+ //printf("Looking up ref: %s\n", String8(s, len).c_str());
// It's a reference!
if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') {
@@ -5705,8 +5705,8 @@
}
uint32_t specFlags = 0;
- uint32_t rid = identifierForName(name.string(), name.size(), type.string(),
- type.size(), package.string(), package.size(), &specFlags);
+ uint32_t rid = identifierForName(name.c_str(), name.size(), type.c_str(),
+ type.size(), package.c_str(), package.size(), &specFlags);
if (rid != 0) {
if (enforcePrivate) {
if (accessor == NULL || accessor->getAssetsPackage() != package) {
@@ -5725,8 +5725,8 @@
Res_GETTYPE(rid), Res_GETENTRY(rid));
if (kDebugTableNoisy) {
ALOGI("Incl %s:%s/%s: 0x%08x\n",
- String8(package).string(), String8(type).string(),
- String8(name).string(), rid);
+ String8(package).c_str(), String8(type).c_str(),
+ String8(name).c_str(), rid);
}
}
@@ -5744,8 +5744,8 @@
if (rid != 0) {
if (kDebugTableNoisy) {
ALOGI("Pckg %s:%s/%s: 0x%08x\n",
- String8(package).string(), String8(type).string(),
- String8(name).string(), rid);
+ String8(package).c_str(), String8(type).c_str(),
+ String8(name).c_str(), rid);
}
uint32_t packageId = Res_GETPACKAGE(rid) + 1;
if (packageId == 0x00) {
@@ -5834,7 +5834,7 @@
}
} else {
outValue->data = color;
- //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color);
+ //printf("Color input=%s, output=0x%x\n", String8(s, len).c_str(), color);
return true;
}
} else {
@@ -5846,8 +5846,8 @@
#if 0
fprintf(stderr, "%s: Color ID %s value %s is not valid\n",
"Resource File", //(const char*)in->getPrintableSource(),
- String8(*curTag).string(),
- String8(s, len).string());
+ String8(*curTag).c_str(),
+ String8(s, len).c_str());
#endif
return false;
}
@@ -5861,7 +5861,7 @@
// be to any other type; we just need to count on the client making
// sure the referenced type is correct.
- //printf("Looking up attr: %s\n", String8(s, len).string());
+ //printf("Looking up attr: %s\n", String8(s, len).c_str());
static const String16 attr16("attr");
String16 package, type, name;
@@ -5874,13 +5874,13 @@
}
//printf("Pkg: %s, Type: %s, Name: %s\n",
- // String8(package).string(), String8(type).string(),
- // String8(name).string());
+ // String8(package).c_str(), String8(type).c_str(),
+ // String8(name).c_str());
uint32_t specFlags = 0;
uint32_t rid =
- identifierForName(name.string(), name.size(),
- type.string(), type.size(),
- package.string(), package.size(), &specFlags);
+ identifierForName(name.c_str(), name.size(),
+ type.c_str(), type.size(),
+ package.c_str(), package.size(), &specFlags);
if (rid != 0) {
if (enforcePrivate) {
if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) {
@@ -6038,8 +6038,8 @@
if (getResourceName(bag->map.name.ident, false, &rname)) {
#if 0
printf("Matching %s against %s (0x%08x)\n",
- String8(s, len).string(),
- String8(rname.name, rname.nameLen).string(),
+ String8(s, len).c_str(),
+ String8(rname.name, rname.nameLen).c_str(),
bag->map.name.ident);
#endif
if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) {
@@ -6082,7 +6082,7 @@
while (pos < end && *pos != '|') {
pos++;
}
- //printf("Looking for: %s\n", String8(start, pos-start).string());
+ //printf("Looking for: %s\n", String8(start, pos-start).c_str());
const bag_entry* bagi = bag;
ssize_t i;
for (i=0; i<cnt; i++, bagi++) {
@@ -6091,8 +6091,8 @@
if (getResourceName(bagi->map.name.ident, false, &rname)) {
#if 0
printf("Matching %s against %s (0x%08x)\n",
- String8(start,pos-start).string(),
- String8(rname.name, rname.nameLen).string(),
+ String8(start,pos-start).c_str(),
+ String8(rname.name, rname.nameLen).c_str(),
bagi->map.name.ident);
#endif
if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) {
@@ -6419,7 +6419,7 @@
}
static bool compareString8AndCString(const String8& str, const char* cStr) {
- return strcmp(str.string(), cStr) < 0;
+ return strcmp(str.c_str(), cStr) < 0;
}
void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales,
@@ -6433,7 +6433,7 @@
const auto endIter = locales->end();
auto iter = std::lower_bound(beginIter, endIter, locale, compareString8AndCString);
- if (iter == endIter || strcmp(iter->string(), locale) != 0) {
+ if (iter == endIter || strcmp(iter->c_str(), locale) != 0) {
locales->insertAt(String8(locale), std::distance(beginIter, iter));
}
});
@@ -7030,7 +7030,7 @@
ResTable_config thisConfig;
thisConfig.copyFromDtoH(type->config);
ALOGI("Adding config to type %d: %s\n", type->id,
- thisConfig.toString().string());
+ thisConfig.toString().c_str());
}
}
} else {
@@ -7107,7 +7107,7 @@
char16_t tmpName[sizeof(entry->packageName) / sizeof(char16_t)];
strcpy16_dtoh(tmpName, entry->packageName, sizeof(entry->packageName) / sizeof(char16_t));
if (kDebugLibNoisy) {
- ALOGV("Found lib entry %s with id %d\n", String8(tmpName).string(),
+ ALOGV("Found lib entry %s with id %d\n", String8(tmpName).c_str(),
dtohl(entry->packageId));
}
if (packageId >= 256) {
@@ -7386,7 +7386,7 @@
current_res.nameLen,
current_res.type,
current_res.typeLen,
- targetPackageName.string(),
+ targetPackageName.c_str(),
targetPackageName.size(),
&typeSpecFlags);
@@ -7493,7 +7493,7 @@
}
-#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string())
+#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).c_str())
#define CHAR16_ARRAY_EQ(constant, var, len) \
(((len) == (sizeof(constant)/sizeof((constant)[0]))) && (0 == memcmp((var), (constant), (len))))
@@ -7588,13 +7588,13 @@
const char* str8 = UnpackOptionalString(pkg->header->values.string8At(
value.data), &len);
if (str8 != NULL) {
- printf("(string8) \"%s\"\n", normalizeForOutput(str8).string());
+ printf("(string8) \"%s\"\n", normalizeForOutput(str8).c_str());
} else {
const char16_t* str16 = UnpackOptionalString(pkg->header->values.stringAt(
value.data), &len);
if (str16 != NULL) {
printf("(string16) \"%s\"\n",
- normalizeForOutput(String8(str16, len).string()).string());
+ normalizeForOutput(String8(str16, len).c_str()).c_str());
} else {
printf("(string) null\n");
}
@@ -7635,7 +7635,7 @@
const PackageGroup* pg = mPackageGroups[pgIndex];
printf("Package Group %d id=0x%02x packageCount=%d name=%s\n",
(int)pgIndex, pg->id, (int)pg->packages.size(),
- String8(pg->name).string());
+ String8(pg->name).c_str());
const KeyedVector<String16, uint8_t>& refEntries = pg->dynamicRefTable.entries();
const size_t refEntryCount = refEntries.size();
@@ -7644,7 +7644,7 @@
for (size_t refIndex = 0; refIndex < refEntryCount; refIndex++) {
printf(" 0x%02x -> %s\n",
refEntries.valueAt(refIndex),
- String8(refEntries.keyAt(refIndex)).string());
+ String8(refEntries.keyAt(refIndex)).c_str());
}
printf("\n");
}
@@ -7670,7 +7670,7 @@
strcpy16_dtoh(tmpName, pkg->package->name,
sizeof(pkg->package->name)/sizeof(pkg->package->name[0]));
printf(" Package %d id=0x%02x name=%s\n", (int)pkgIndex,
- pkg->package->id, String8(tmpName).string());
+ pkg->package->id, String8(tmpName).c_str());
}
for (size_t typeIndex = 0; typeIndex < pg->types.size(); typeIndex++) {
@@ -7712,7 +7712,7 @@
printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
resID,
CHAR16_TO_CSTR(resName.package, resName.packageLen),
- type8.string(), name8.string(),
+ type8.c_str(), name8.c_str(),
dtohl(typeConfigs->typeSpecFlags[entryIndex]));
} else {
printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
@@ -7733,7 +7733,7 @@
String8 configStr = thisConfig.toString();
printf(" config %s", configStr.size() > 0
- ? configStr.string() : "(default)");
+ ? configStr.c_str() : "(default)");
if (type->flags != 0u) {
printf(" flags=0x%02x", type->flags);
if (type->flags & ResTable_type::FLAG_SPARSE) {
@@ -7807,7 +7807,7 @@
}
printf(" resource 0x%08x %s:%s/%s: ", resID,
CHAR16_TO_CSTR(resName.package, resName.packageLen),
- type8.string(), name8.string());
+ type8.c_str(), name8.c_str());
} else {
printf(" INVALID RESOURCE 0x%08x: ", resID);
}
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 19febcd..f3776b5 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -135,7 +135,7 @@
* This is NOT intended to be used for anything except debug output.
* DO NOT try to parse this or use it to open a file.
*/
- const char* getAssetSource(void) const { return mAssetSource.string(); }
+ const char* getAssetSource(void) const { return mAssetSource.c_str(); }
/*
* Create the asset from a file descriptor.
diff --git a/libs/androidfw/include/androidfw/ConfigDescription.h b/libs/androidfw/include/androidfw/ConfigDescription.h
index 7fbd7c0..83a80ce 100644
--- a/libs/androidfw/include/androidfw/ConfigDescription.h
+++ b/libs/androidfw/include/androidfw/ConfigDescription.h
@@ -213,7 +213,7 @@
inline ::std::ostream& operator<<(::std::ostream& out,
const ConfigDescription& o) {
- return out << o.toString().string();
+ return out << o.toString().c_str();
}
} // namespace android
diff --git a/libs/androidfw/tests/BackupData_test.cpp b/libs/androidfw/tests/BackupData_test.cpp
index e25b616..7d3a341 100644
--- a/libs/androidfw/tests/BackupData_test.cpp
+++ b/libs/androidfw/tests/BackupData_test.cpp
@@ -56,10 +56,10 @@
mFilename.append(m_external_storage);
mFilename.append(TEST_FILENAME);
- ::unlink(mFilename.string());
- int fd = ::open(mFilename.string(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ ::unlink(mFilename.c_str());
+ int fd = ::open(mFilename.c_str(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd < 0) {
- FAIL() << "Couldn't create " << mFilename.string() << " for writing";
+ FAIL() << "Couldn't create " << mFilename.c_str() << " for writing";
}
mKey1 = String8(KEY1);
mKey2 = String8(KEY2);
@@ -72,7 +72,7 @@
};
TEST_F(BackupDataTest, WriteAndReadSingle) {
- int fd = ::open(mFilename.string(), O_WRONLY);
+ int fd = ::open(mFilename.c_str(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
EXPECT_EQ(NO_ERROR, writer->WriteEntityHeader(mKey1, sizeof(DATA1)))
@@ -81,7 +81,7 @@
<< "WriteEntityData returned an error";
::close(fd);
- fd = ::open(mFilename.string(), O_RDONLY);
+ fd = ::open(mFilename.c_str(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
EXPECT_EQ(NO_ERROR, reader->Status())
<< "Reader ctor failed";
@@ -114,7 +114,7 @@
}
TEST_F(BackupDataTest, WriteAndReadMultiple) {
- int fd = ::open(mFilename.string(), O_WRONLY);
+ int fd = ::open(mFilename.c_str(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -122,7 +122,7 @@
writer->WriteEntityData(DATA2, sizeof(DATA2));
::close(fd);
- fd = ::open(mFilename.string(), O_RDONLY);
+ fd = ::open(mFilename.c_str(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -162,7 +162,7 @@
}
TEST_F(BackupDataTest, SkipEntity) {
- int fd = ::open(mFilename.string(), O_WRONLY);
+ int fd = ::open(mFilename.c_str(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -172,7 +172,7 @@
writer->WriteEntityData(DATA3, sizeof(DATA3));
::close(fd);
- fd = ::open(mFilename.string(), O_RDONLY);
+ fd = ::open(mFilename.c_str(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -217,14 +217,14 @@
}
TEST_F(BackupDataTest, DeleteEntity) {
- int fd = ::open(mFilename.string(), O_WRONLY);
+ int fd = ::open(mFilename.c_str(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
writer->WriteEntityHeader(mKey2, -1);
::close(fd);
- fd = ::open(mFilename.string(), O_RDONLY);
+ fd = ::open(mFilename.c_str(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -256,7 +256,7 @@
}
TEST_F(BackupDataTest, EneityAfterDelete) {
- int fd = ::open(mFilename.string(), O_WRONLY);
+ int fd = ::open(mFilename.c_str(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, sizeof(DATA1));
writer->WriteEntityData(DATA1, sizeof(DATA1));
@@ -265,7 +265,7 @@
writer->WriteEntityData(DATA3, sizeof(DATA3));
::close(fd);
- fd = ::open(mFilename.string(), O_RDONLY);
+ fd = ::open(mFilename.c_str(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -317,7 +317,7 @@
}
TEST_F(BackupDataTest, OnlyDeleteEntities) {
- int fd = ::open(mFilename.string(), O_WRONLY);
+ int fd = ::open(mFilename.c_str(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, -1);
writer->WriteEntityHeader(mKey2, -1);
@@ -325,7 +325,7 @@
writer->WriteEntityHeader(mKey4, -1);
::close(fd);
- fd = ::open(mFilename.string(), O_RDONLY);
+ fd = ::open(mFilename.c_str(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
@@ -385,13 +385,13 @@
}
TEST_F(BackupDataTest, ReadDeletedEntityData) {
- int fd = ::open(mFilename.string(), O_WRONLY);
+ int fd = ::open(mFilename.c_str(), O_WRONLY);
BackupDataWriter* writer = new BackupDataWriter(fd);
writer->WriteEntityHeader(mKey1, -1);
writer->WriteEntityHeader(mKey2, -1);
::close(fd);
- fd = ::open(mFilename.string(), O_RDONLY);
+ fd = ::open(mFilename.c_str(), O_RDONLY);
BackupDataReader* reader = new BackupDataReader(fd);
bool done;
diff --git a/libs/androidfw/tests/CommonHelpers.cpp b/libs/androidfw/tests/CommonHelpers.cpp
index 3396729..10138de 100644
--- a/libs/androidfw/tests/CommonHelpers.cpp
+++ b/libs/androidfw/tests/CommonHelpers.cpp
@@ -60,7 +60,7 @@
std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) {
auto str = pool->string8ObjectAt(idx);
CHECK(str.has_value()) << "failed to find string entry";
- return std::string(str->string(), str->length());
+ return std::string(str->c_str(), str->length());
}
} // namespace android
diff --git a/libs/androidfw/tests/ConfigDescription_test.cpp b/libs/androidfw/tests/ConfigDescription_test.cpp
index f5c01e5..ec478b0 100644
--- a/libs/androidfw/tests/ConfigDescription_test.cpp
+++ b/libs/androidfw/tests/ConfigDescription_test.cpp
@@ -50,10 +50,10 @@
TEST(ConfigDescriptionTest, ParseBasicQualifiers) {
ConfigDescription config;
EXPECT_TRUE(TestParse("", &config));
- EXPECT_EQ(std::string(""), config.toString().string());
+ EXPECT_EQ(std::string(""), config.toString().c_str());
EXPECT_TRUE(TestParse("fr-land", &config));
- EXPECT_EQ(std::string("fr-land"), config.toString().string());
+ EXPECT_EQ(std::string("fr-land"), config.toString().c_str());
EXPECT_TRUE(
TestParse("mcc310-pl-sw720dp-normal-long-port-night-"
@@ -61,22 +61,22 @@
&config));
EXPECT_EQ(std::string("mcc310-pl-sw720dp-normal-long-port-night-"
"xhdpi-keyssoft-qwerty-navexposed-nonav-v13"),
- config.toString().string());
+ config.toString().c_str());
}
TEST(ConfigDescriptionTest, ParseLocales) {
ConfigDescription config;
EXPECT_TRUE(TestParse("en-rUS", &config));
- EXPECT_EQ(std::string("en-rUS"), config.toString().string());
+ EXPECT_EQ(std::string("en-rUS"), config.toString().c_str());
}
TEST(ConfigDescriptionTest, ParseQualifierAddedInApi13) {
ConfigDescription config;
EXPECT_TRUE(TestParse("sw600dp", &config));
- EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
+ EXPECT_EQ(std::string("sw600dp-v13"), config.toString().c_str());
EXPECT_TRUE(TestParse("sw600dp-v8", &config));
- EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
+ EXPECT_EQ(std::string("sw600dp-v13"), config.toString().c_str());
}
TEST(ConfigDescriptionTest, ParseCarAttribute) {
@@ -91,13 +91,13 @@
EXPECT_EQ(android::ResTable_config::SCREENROUND_YES,
config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
- EXPECT_EQ(std::string("round-v23"), config.toString().string());
+ EXPECT_EQ(std::string("round-v23"), config.toString().c_str());
EXPECT_TRUE(TestParse("notround", &config));
EXPECT_EQ(android::ResTable_config::SCREENROUND_NO,
config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
- EXPECT_EQ(std::string("notround-v23"), config.toString().string());
+ EXPECT_EQ(std::string("notround-v23"), config.toString().c_str());
}
TEST(ConfigDescriptionTest, TestWideColorGamutQualifier) {
@@ -106,13 +106,13 @@
EXPECT_EQ(android::ResTable_config::WIDE_COLOR_GAMUT_YES,
config.colorMode & android::ResTable_config::MASK_WIDE_COLOR_GAMUT);
EXPECT_EQ(SDK_O, config.sdkVersion);
- EXPECT_EQ(std::string("widecg-v26"), config.toString().string());
+ EXPECT_EQ(std::string("widecg-v26"), config.toString().c_str());
EXPECT_TRUE(TestParse("nowidecg", &config));
EXPECT_EQ(android::ResTable_config::WIDE_COLOR_GAMUT_NO,
config.colorMode & android::ResTable_config::MASK_WIDE_COLOR_GAMUT);
EXPECT_EQ(SDK_O, config.sdkVersion);
- EXPECT_EQ(std::string("nowidecg-v26"), config.toString().string());
+ EXPECT_EQ(std::string("nowidecg-v26"), config.toString().c_str());
}
TEST(ConfigDescriptionTest, TestHdrQualifier) {
@@ -121,13 +121,13 @@
EXPECT_EQ(android::ResTable_config::HDR_YES,
config.colorMode & android::ResTable_config::MASK_HDR);
EXPECT_EQ(SDK_O, config.sdkVersion);
- EXPECT_EQ(std::string("highdr-v26"), config.toString().string());
+ EXPECT_EQ(std::string("highdr-v26"), config.toString().c_str());
EXPECT_TRUE(TestParse("lowdr", &config));
EXPECT_EQ(android::ResTable_config::HDR_NO,
config.colorMode & android::ResTable_config::MASK_HDR);
EXPECT_EQ(SDK_O, config.sdkVersion);
- EXPECT_EQ(std::string("lowdr-v26"), config.toString().string());
+ EXPECT_EQ(std::string("lowdr-v26"), config.toString().c_str());
}
TEST(ConfigDescriptionTest, ParseVrAttribute) {
@@ -135,7 +135,7 @@
EXPECT_TRUE(TestParse("vrheadset", &config));
EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_VR_HEADSET, config.uiMode);
EXPECT_EQ(SDK_O, config.sdkVersion);
- EXPECT_EQ(std::string("vrheadset-v26"), config.toString().string());
+ EXPECT_EQ(std::string("vrheadset-v26"), config.toString().c_str());
}
static inline ConfigDescription ParseConfigOrDie(android::StringPiece str) {
diff --git a/libs/androidfw/tests/ObbFile_test.cpp b/libs/androidfw/tests/ObbFile_test.cpp
index 1151121..ba818c4 100644
--- a/libs/androidfw/tests/ObbFile_test.cpp
+++ b/libs/androidfw/tests/ObbFile_test.cpp
@@ -43,9 +43,9 @@
mFileName.append(externalStorage);
mFileName.append(TEST_FILENAME);
- int fd = ::open(mFileName.string(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ int fd = ::open(mFileName.c_str(), O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd < 0) {
- FAIL() << "Couldn't create " << mFileName.string() << " for tests";
+ FAIL() << "Couldn't create " << mFileName.c_str() << " for tests";
}
}
@@ -69,17 +69,17 @@
EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE))
<< "Salt should be successfully set";
- EXPECT_TRUE(mObbFile->writeTo(mFileName.string()))
+ EXPECT_TRUE(mObbFile->writeTo(mFileName.c_str()))
<< "couldn't write to fake .obb file";
mObbFile = new ObbFile();
- EXPECT_TRUE(mObbFile->readFrom(mFileName.string()))
+ EXPECT_TRUE(mObbFile->readFrom(mFileName.c_str()))
<< "couldn't read from fake .obb file";
EXPECT_EQ(versionNum, mObbFile->getVersion())
<< "version didn't come out the same as it went in";
- const char* currentPackageName = mObbFile->getPackageName().string();
+ const char* currentPackageName = mObbFile->getPackageName().c_str();
EXPECT_STREQ(packageName, currentPackageName)
<< "package name didn't come out the same as it went in";
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index fbf7098..945981b 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -64,8 +64,8 @@
String16 defPackage("com.android.basic");
String16 testName("@string/test1");
uint32_t resID =
- table.identifierForName(testName.string(), testName.size(), 0, 0,
- defPackage.string(), defPackage.size());
+ table.identifierForName(testName.c_str(), testName.size(), 0, 0,
+ defPackage.c_str(), defPackage.size());
ASSERT_NE(uint32_t(0x00000000), resID);
ASSERT_EQ(basic::R::string::test1, resID);
}
diff --git a/libs/androidfw/tests/Split_test.cpp b/libs/androidfw/tests/Split_test.cpp
index 2c242db..3d88577 100644
--- a/libs/androidfw/tests/Split_test.cpp
+++ b/libs/androidfw/tests/Split_test.cpp
@@ -261,8 +261,8 @@
const String16 package("com.android.basic");
ASSERT_EQ(
R::string::test3,
- table.identifierForName(name.string(), name.size(), type.string(),
- type.size(), package.string(), package.size()));
+ table.identifierForName(name.c_str(), name.size(), type.c_str(),
+ type.size(), package.c_str(), package.size()));
}
} // namespace
diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp
index 10c0a4f..c6f657c 100644
--- a/libs/androidfw/tests/TestHelpers.cpp
+++ b/libs/androidfw/tests/TestHelpers.cpp
@@ -79,9 +79,9 @@
}
if (String8(expected_str) != *actual_str) {
- return AssertionFailure() << actual_str->string();
+ return AssertionFailure() << actual_str->c_str();
}
- return AssertionSuccess() << actual_str->string();
+ return AssertionSuccess() << actual_str->c_str();
}
} // namespace android
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 7f80dff..ce6b4b7 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -28,6 +28,24 @@
],
}
+aconfig_declarations {
+ name: "hwui_flags",
+ package: "com.android.graphics.hwui.flags",
+ srcs: [
+ "aconfig/hwui_flags.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "hwui_flags_java_lib",
+ aconfig_declarations: "hwui_flags",
+}
+
+cc_aconfig_library {
+ name: "hwui_flags_cc_lib",
+ aconfig_declarations: "hwui_flags",
+}
+
cc_defaults {
name: "hwui_defaults",
defaults: [
@@ -139,6 +157,7 @@
"libstatspull_lazy",
"libstatssocket_lazy",
"libtonemap",
+ "hwui_flags_cc_lib",
],
},
host: {
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index eb5878d..8c180da 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -54,6 +54,8 @@
mImpl->updateChildren(std::move(updateFn));
}
+ void visit(std::function<void(const RenderNode&)> func) const { mImpl->visit(std::move(func)); }
+
[[nodiscard]] explicit operator bool() const {
return mImpl.get() != nullptr;
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 1dd22cf..eee93c1 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -109,6 +109,13 @@
output << std::endl;
}
+void RenderNode::visit(std::function<void(const RenderNode&)> func) const {
+ func(*this);
+ if (mDisplayList) {
+ mDisplayList.visit(func);
+ }
+}
+
int RenderNode::getUsageSize() {
int size = sizeof(RenderNode);
size += mStagingDisplayList.getUsedSize();
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index d1e04ad..529a49e9 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -114,7 +114,7 @@
return mDisplayList.containsProjectionReceiver();
}
- const char* getName() const { return mName.string(); }
+ const char* getName() const { return mName.c_str(); }
void setName(const char* name) {
if (name) {
@@ -129,10 +129,6 @@
StretchMask& getStretchMask() { return mStretchMask; }
- VirtualLightRefBase* getUserContext() const { return mUserContext.get(); }
-
- void setUserContext(VirtualLightRefBase* context) { mUserContext = context; }
-
bool isPropertyFieldDirty(DirtyPropertyMask field) const {
return mDirtyPropertyFields & field;
}
@@ -215,6 +211,8 @@
void output(std::ostream& output, uint32_t level);
+ void visit(std::function<void(const RenderNode&)>) const;
+
void setUsageHint(UsageHint usageHint) { mUsageHint = usageHint; }
UsageHint usageHint() const { return mUsageHint; }
@@ -222,6 +220,7 @@
int64_t uniqueId() const { return mUniqueId; }
void setIsTextureView() { mIsTextureView = true; }
+ bool isTextureView() const { return mIsTextureView; }
void markDrawStart(SkCanvas& canvas);
void markDrawEnd(SkCanvas& canvas);
@@ -248,7 +247,6 @@
const int64_t mUniqueId;
String8 mName;
- sp<VirtualLightRefBase> mUserContext;
uint32_t mDirtyPropertyFields;
RenderProperties mProperties;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
new file mode 100644
index 0000000..d074a90
--- /dev/null
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.graphics.hwui.flags"
+
+flag {
+ name: "limited_hdr"
+ namespace: "core_graphics"
+ description: "API to enable apps to restrict the amount of HDR headroom that is used"
+ bug: "234181960"
+}
\ No newline at end of file
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index af2d3b3..5c8285a 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -66,6 +66,12 @@
}
}
+void SkiaDisplayList::visit(std::function<void(const RenderNode&)> func) const {
+ for (auto& child : mChildNodes) {
+ child.getRenderNode()->visit(func);
+ }
+}
+
static bool intersects(const SkISize screenSize, const Matrix4& mat, const SkRect& bounds) {
Vector3 points[] = { Vector3 {bounds.fLeft, bounds.fTop, 0},
Vector3 {bounds.fRight, bounds.fTop, 0},
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 7af31a4..e5bd5c9 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -145,6 +145,8 @@
*/
void updateChildren(std::function<void(RenderNode*)> updateFn);
+ void visit(std::function<void(const RenderNode&)> func) const;
+
/**
* Returns true if there is a child render node that is a projection receiver.
*/
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 3d77877..6679f8f 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -201,7 +201,7 @@
String8 cachesOutput;
mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
&mRenderThread.renderState());
- ALOGE("%s", cachesOutput.string());
+ ALOGE("%s", cachesOutput.c_str());
if (errorHandler) {
std::ostringstream err;
err << "Unable to create layer for " << node->getName();
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2e0de3f..4064bb9 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -1132,6 +1132,12 @@
return self->mColorMode != ColorMode::Default;
}
+void CanvasContext::visitAllRenderNodes(std::function<void(const RenderNode&)> func) const {
+ for (auto node : mRenderNodes) {
+ node->visit(func);
+ }
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 3f02674..241f8dd 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -238,6 +238,8 @@
static bool shouldDither();
+ void visitAllRenderNodes(std::function<void(const RenderNode&)>) const;
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline,
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index eb28c08..94ed06c 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -357,7 +357,15 @@
String8 cachesOutput;
mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState);
- dprintf(fd, "\nPipeline=%s\n%s\n", pipelineToString(), cachesOutput.string());
+ dprintf(fd, "\nPipeline=%s\n%s", pipelineToString(), cachesOutput.c_str());
+ for (auto&& context : mCacheManager->mCanvasContexts) {
+ context->visitAllRenderNodes([&](const RenderNode& node) {
+ if (node.isTextureView()) {
+ dprintf(fd, "TextureView: %dx%d\n", node.getWidth(), node.getHeight());
+ }
+ });
+ }
+ dprintf(fd, "\n");
}
void RenderThread::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index 8baa4b77..eab888e 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -31,15 +31,11 @@
#include <utils/Log.h>
#include <utils/Macros.h>
-// The ideal size of a page allocation (these need to be multiples of 8)
-#define INITIAL_PAGE_SIZE ((size_t)512) // 512b
-#define MAX_PAGE_SIZE ((size_t)131072) // 128kb
-
// The maximum amount of wasted space we can have per page
// Allocations exceeding this will have their own dedicated page
// If this is too low, we will malloc too much
// Too high, and we may waste too much space
-// Must be smaller than INITIAL_PAGE_SIZE
+// Must be smaller than kInitialPageSize
#define MAX_WASTE_RATIO (0.5f)
#if LOG_NDEBUG
@@ -75,6 +71,10 @@
namespace android {
namespace uirenderer {
+// The ideal size of a page allocation (these need to be multiples of 8)
+static constexpr size_t kInitialPageSize = 512; // 512b
+static constexpr size_t kMaxPageSize = 131072; // 128kb
+
class LinearAllocator::Page {
public:
Page* next() { return mNextPage; }
@@ -94,8 +94,8 @@
};
LinearAllocator::LinearAllocator()
- : mPageSize(INITIAL_PAGE_SIZE)
- , mMaxAllocSize(INITIAL_PAGE_SIZE * MAX_WASTE_RATIO)
+ : mPageSize(kInitialPageSize)
+ , mMaxAllocSize(kInitialPageSize * MAX_WASTE_RATIO)
, mNext(0)
, mCurrentPage(0)
, mPages(0)
@@ -135,8 +135,8 @@
void LinearAllocator::ensureNext(size_t size) {
if (fitsInCurrentPage(size)) return;
- if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) {
- mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2);
+ if (mCurrentPage && mPageSize < kMaxPageSize) {
+ mPageSize = min(kMaxPageSize, mPageSize * 2);
mMaxAllocSize = mPageSize * MAX_WASTE_RATIO;
mPageSize = ALIGN(mPageSize);
}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 6c0fd5f..5ce990f 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -23,6 +23,12 @@
cc_library_shared {
name: "libinputservice",
+ defaults: [
+ // Build using the same flags and configurations as inputflinger.
+ "inputflinger_defaults",
+ ],
+ host_supported: false,
+
srcs: [
"PointerController.cpp",
"PointerControllerContext.cpp",
@@ -50,12 +56,4 @@
],
include_dirs: ["frameworks/native/services"],
-
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-Wthread-safety",
- ],
-
}
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index c3ad767..6a46544 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -47,7 +47,7 @@
mLocked.pointerX = 0;
mLocked.pointerY = 0;
mLocked.pointerAlpha = 0.0f; // pointer is initially faded
- mLocked.pointerSprite = mContext.getSpriteController()->createSprite();
+ mLocked.pointerSprite = mContext.getSpriteController().createSprite();
mLocked.updatePointerIcon = false;
mLocked.requestedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED;
mLocked.resolvedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED;
@@ -325,8 +325,8 @@
}
if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
- sp<SpriteController> spriteController = mContext.getSpriteController();
- spriteController->openTransaction();
+ auto& spriteController = mContext.getSpriteController();
+ spriteController.openTransaction();
int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
mLocked.animationFrameIndex += incr;
@@ -336,7 +336,7 @@
}
mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
- spriteController->closeTransaction();
+ spriteController.closeTransaction();
}
// Keep animating.
return true;
@@ -346,8 +346,8 @@
if (!mLocked.viewport.isValid()) {
return;
}
- sp<SpriteController> spriteController = mContext.getSpriteController();
- spriteController->openTransaction();
+ auto& spriteController = mContext.getSpriteController();
+ spriteController.openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
@@ -392,7 +392,7 @@
mLocked.updatePointerIcon = false;
}
- spriteController->closeTransaction();
+ spriteController.closeTransaction();
}
void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index e21d6fb..435452c 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -63,7 +63,7 @@
std::shared_ptr<PointerController> PointerController::create(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- const sp<SpriteController>& spriteController) {
+ SpriteController& spriteController) {
// using 'new' to access non-public constructor
std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
new PointerController(policy, looper, spriteController));
@@ -85,8 +85,7 @@
}
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper,
- const sp<SpriteController>& spriteController)
+ const sp<Looper>& looper, SpriteController& spriteController)
: PointerController(
policy, looper, spriteController,
[](const sp<android::gui::WindowInfosListener>& listener) {
@@ -97,13 +96,12 @@
}) {}
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper,
- const sp<SpriteController>& spriteController,
+ const sp<Looper>& looper, SpriteController& spriteController,
WindowListenerConsumer registerListener,
WindowListenerConsumer unregisterListener)
: mContext(policy, looper, spriteController, *this),
mCursorController(mContext),
- mDisplayInfoListener(new DisplayInfoListener(this)),
+ mDisplayInfoListener(sp<DisplayInfoListener>::make(this)),
mUnregisterWindowInfosListener(std::move(unregisterListener)) {
std::scoped_lock lock(getLock());
mLocked.presentation = Presentation::SPOT;
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 62ee743..c7e772d 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -47,7 +47,7 @@
public:
static std::shared_ptr<PointerController> create(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- const sp<SpriteController>& spriteController);
+ SpriteController& spriteController);
~PointerController() override;
@@ -83,13 +83,12 @@
// Constructor used to test WindowInfosListener registration.
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- const sp<SpriteController>& spriteController,
- WindowListenerConsumer registerListener,
+ SpriteController& spriteController, WindowListenerConsumer registerListener,
WindowListenerConsumer unregisterListener);
private:
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- const sp<SpriteController>& spriteController);
+ SpriteController& spriteController);
friend PointerControllerContext::LooperCallback;
friend PointerControllerContext::MessageHandler;
diff --git a/libs/input/PointerControllerContext.cpp b/libs/input/PointerControllerContext.cpp
index f30e8d8..15c3517 100644
--- a/libs/input/PointerControllerContext.cpp
+++ b/libs/input/PointerControllerContext.cpp
@@ -32,12 +32,12 @@
PointerControllerContext::PointerControllerContext(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- const sp<SpriteController>& spriteController, PointerController& controller)
+ SpriteController& spriteController, PointerController& controller)
: mPolicy(policy),
mLooper(looper),
mSpriteController(spriteController),
- mHandler(new MessageHandler()),
- mCallback(new LooperCallback()),
+ mHandler(sp<MessageHandler>::make()),
+ mCallback(sp<LooperCallback>::make()),
mController(controller),
mAnimator(*this) {
std::scoped_lock lock(mLock);
@@ -93,7 +93,7 @@
return mPolicy;
}
-sp<SpriteController> PointerControllerContext::getSpriteController() {
+SpriteController& PointerControllerContext::getSpriteController() {
return mSpriteController;
}
diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h
index f6f5d3b..98c3988 100644
--- a/libs/input/PointerControllerContext.h
+++ b/libs/input/PointerControllerContext.h
@@ -92,7 +92,7 @@
class PointerControllerContext {
public:
PointerControllerContext(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper, const sp<SpriteController>& spriteController,
+ const sp<Looper>& looper, SpriteController& spriteController,
PointerController& controller);
~PointerControllerContext();
@@ -109,7 +109,7 @@
void setCallbackController(std::shared_ptr<PointerController> controller);
sp<PointerControllerPolicyInterface> getPolicy();
- sp<SpriteController> getSpriteController();
+ SpriteController& getSpriteController();
void handleDisplayEvents();
@@ -163,7 +163,7 @@
sp<PointerControllerPolicyInterface> mPolicy;
sp<Looper> mLooper;
- sp<SpriteController> mSpriteController;
+ SpriteController& mSpriteController;
sp<MessageHandler> mHandler;
sp<LooperCallback> mCallback;
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 130b204..6dc45a6 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -31,12 +31,19 @@
ParentSurfaceProvider parentSurfaceProvider)
: mLooper(looper),
mOverlayLayer(overlayLayer),
+ mHandler(sp<Handler>::make()),
mParentSurfaceProvider(std::move(parentSurfaceProvider)) {
- mHandler = new WeakMessageHandler(this);
mLocked.transactionNestingCount = 0;
mLocked.deferredSpriteUpdate = false;
}
+void SpriteController::setHandlerController(
+ const std::shared_ptr<android::SpriteController>& controller) {
+ // Initialize the weak message handler outside the constructor, because we cannot get a shared
+ // pointer to self in the constructor.
+ mHandler->spriteController = controller;
+}
+
SpriteController::~SpriteController() {
mLooper->removeMessages(mHandler);
@@ -47,7 +54,7 @@
}
sp<Sprite> SpriteController::createSprite() {
- return new SpriteImpl(this);
+ return sp<SpriteImpl>::make(*this);
}
void SpriteController::openTransaction() {
@@ -65,7 +72,7 @@
mLocked.transactionNestingCount -= 1;
if (mLocked.transactionNestingCount == 0 && mLocked.deferredSpriteUpdate) {
mLocked.deferredSpriteUpdate = false;
- mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
+ mLooper->sendMessage(mHandler, Message(Handler::MSG_UPDATE_SPRITES));
}
}
@@ -76,7 +83,7 @@
if (mLocked.transactionNestingCount != 0) {
mLocked.deferredSpriteUpdate = true;
} else {
- mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
+ mLooper->sendMessage(mHandler, Message(Handler::MSG_UPDATE_SPRITES));
}
}
}
@@ -85,18 +92,7 @@
bool wasEmpty = mLocked.disposedSurfaces.empty();
mLocked.disposedSurfaces.push_back(surfaceControl);
if (wasEmpty) {
- mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES));
- }
-}
-
-void SpriteController::handleMessage(const Message& message) {
- switch (message.what) {
- case MSG_UPDATE_SPRITES:
- doUpdateSprites();
- break;
- case MSG_DISPOSE_SURFACES:
- doDisposeSurfaces();
- break;
+ mLooper->sendMessage(mHandler, Message(Handler::MSG_DISPOSE_SURFACES));
}
}
@@ -327,7 +323,7 @@
void SpriteController::ensureSurfaceComposerClient() {
if (mSurfaceComposerClient == NULL) {
- mSurfaceComposerClient = new SurfaceComposerClient();
+ mSurfaceComposerClient = sp<SurfaceComposerClient>::make();
}
}
@@ -353,25 +349,41 @@
return surfaceControl;
}
-// --- SpriteController::SpriteImpl ---
+// --- SpriteController::Handler ---
-SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) :
- mController(controller) {
+void SpriteController::Handler::handleMessage(const android::Message& message) {
+ auto controller = spriteController.lock();
+ if (!controller) {
+ return;
+ }
+
+ switch (message.what) {
+ case MSG_UPDATE_SPRITES:
+ controller->doUpdateSprites();
+ break;
+ case MSG_DISPOSE_SURFACES:
+ controller->doDisposeSurfaces();
+ break;
+ }
}
+// --- SpriteController::SpriteImpl ---
+
+SpriteController::SpriteImpl::SpriteImpl(SpriteController& controller) : mController(controller) {}
+
SpriteController::SpriteImpl::~SpriteImpl() {
- AutoMutex _m(mController->mLock);
+ AutoMutex _m(mController.mLock);
// Let the controller take care of deleting the last reference to sprite
// surfaces so that we do not block the caller on an IPC here.
if (mLocked.state.surfaceControl != NULL) {
- mController->disposeSurfaceLocked(mLocked.state.surfaceControl);
+ mController.disposeSurfaceLocked(mLocked.state.surfaceControl);
mLocked.state.surfaceControl.clear();
}
}
void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
uint32_t dirty;
if (icon.isValid()) {
@@ -401,7 +413,7 @@
}
void SpriteController::SpriteImpl::setVisible(bool visible) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.visible != visible) {
mLocked.state.visible = visible;
@@ -410,7 +422,7 @@
}
void SpriteController::SpriteImpl::setPosition(float x, float y) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.positionX != x || mLocked.state.positionY != y) {
mLocked.state.positionX = x;
@@ -420,7 +432,7 @@
}
void SpriteController::SpriteImpl::setLayer(int32_t layer) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.layer != layer) {
mLocked.state.layer = layer;
@@ -429,7 +441,7 @@
}
void SpriteController::SpriteImpl::setAlpha(float alpha) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.alpha != alpha) {
mLocked.state.alpha = alpha;
@@ -439,7 +451,7 @@
void SpriteController::SpriteImpl::setTransformationMatrix(
const SpriteTransformationMatrix& matrix) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.transformationMatrix != matrix) {
mLocked.state.transformationMatrix = matrix;
@@ -448,7 +460,7 @@
}
void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
- AutoMutex _l(mController->mLock);
+ AutoMutex _l(mController.mLock);
if (mLocked.state.displayId != displayId) {
mLocked.state.displayId = displayId;
@@ -461,7 +473,7 @@
mLocked.state.dirty |= dirty;
if (!wasDirty) {
- mController->invalidateSpriteLocked(this);
+ mController.invalidateSpriteLocked(sp<SpriteImpl>::fromExisting(this));
}
}
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 1f113c0..04ecb38 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -109,15 +109,19 @@
*
* Clients are responsible for animating sprites by periodically updating their properties.
*/
-class SpriteController : public MessageHandler {
-protected:
- virtual ~SpriteController();
-
+class SpriteController {
public:
using ParentSurfaceProvider = std::function<sp<SurfaceControl>(int /*displayId*/)>;
SpriteController(const sp<Looper>& looper, int32_t overlayLayer, ParentSurfaceProvider parent);
+ SpriteController(const SpriteController&) = delete;
+ SpriteController& operator=(const SpriteController&) = delete;
+ virtual ~SpriteController();
- /* Creates a new sprite, initially invisible. */
+ /* Initialize the callback for the message handler. */
+ void setHandlerController(const std::shared_ptr<SpriteController>& controller);
+
+ /* Creates a new sprite, initially invisible. The lifecycle of the sprite must not extend beyond
+ * the lifecycle of this SpriteController. */
virtual sp<Sprite> createSprite();
/* Opens or closes a transaction to perform a batch of sprite updates as part of
@@ -129,9 +133,12 @@
virtual void closeTransaction();
private:
- enum {
- MSG_UPDATE_SPRITES,
- MSG_DISPOSE_SURFACES,
+ class Handler : public virtual android::MessageHandler {
+ public:
+ enum { MSG_UPDATE_SPRITES, MSG_DISPOSE_SURFACES };
+
+ void handleMessage(const Message& message) override;
+ std::weak_ptr<SpriteController> spriteController;
};
enum {
@@ -192,7 +199,7 @@
virtual ~SpriteImpl();
public:
- explicit SpriteImpl(const sp<SpriteController> controller);
+ explicit SpriteImpl(SpriteController& controller);
virtual void setIcon(const SpriteIcon& icon);
virtual void setVisible(bool visible);
@@ -220,7 +227,7 @@
}
private:
- sp<SpriteController> mController;
+ SpriteController& mController;
struct Locked {
SpriteState state;
@@ -245,7 +252,7 @@
sp<Looper> mLooper;
const int32_t mOverlayLayer;
- sp<WeakMessageHandler> mHandler;
+ sp<Handler> mHandler;
ParentSurfaceProvider mParentSurfaceProvider;
sp<SurfaceComposerClient> mSurfaceComposerClient;
@@ -260,7 +267,6 @@
void invalidateSpriteLocked(const sp<SpriteImpl>& sprite);
void disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl);
- void handleMessage(const Message& message);
void doUpdateSprites();
void doDisposeSurfaces();
diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp
index d9fe599..b8de919 100644
--- a/libs/input/TouchSpotController.cpp
+++ b/libs/input/TouchSpotController.cpp
@@ -39,15 +39,15 @@
// --- Spot ---
-void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
+void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float newX, float newY,
int32_t displayId) {
sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
sprite->setAlpha(alpha);
sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
- sprite->setPosition(x, y);
+ sprite->setPosition(newX, newY);
sprite->setDisplayId(displayId);
- this->x = x;
- this->y = y;
+ x = newX;
+ y = newY;
if (icon != mLastIcon) {
mLastIcon = icon;
@@ -98,8 +98,8 @@
#endif
std::scoped_lock lock(mLock);
- sp<SpriteController> spriteController = mContext.getSpriteController();
- spriteController->openTransaction();
+ auto& spriteController = mContext.getSpriteController();
+ spriteController.openTransaction();
// Add or move spots for fingers that are down.
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
@@ -125,7 +125,7 @@
}
}
- spriteController->closeTransaction();
+ spriteController.closeTransaction();
}
void TouchSpotController::clearSpots() {
@@ -167,7 +167,7 @@
sprite = mLocked.recycledSprites.back();
mLocked.recycledSprites.pop_back();
} else {
- sprite = mContext.getSpriteController()->createSprite();
+ sprite = mContext.getSpriteController().createSprite();
}
// Return the new spot.
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 8574751..3e2e43f 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -157,7 +157,7 @@
sp<MockSprite> mPointerSprite;
sp<MockPointerControllerPolicyInterface> mPolicy;
- sp<MockSpriteController> mSpriteController;
+ std::unique_ptr<MockSpriteController> mSpriteController;
std::shared_ptr<PointerController> mPointerController;
private:
@@ -175,14 +175,13 @@
PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
-
- mSpriteController = new NiceMock<MockSpriteController>(mLooper);
+ mSpriteController.reset(new NiceMock<MockSpriteController>(mLooper));
mPolicy = new MockPointerControllerPolicyInterface();
EXPECT_CALL(*mSpriteController, createSprite())
.WillOnce(Return(mPointerSprite));
- mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
+ mPointerController = PointerController::create(mPolicy, mLooper, *mSpriteController);
}
PointerControllerTest::~PointerControllerTest() {
@@ -319,10 +318,9 @@
class TestPointerController : public PointerController {
public:
TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
- const sp<Looper>& looper)
+ const sp<Looper>& looper, SpriteController& spriteController)
: PointerController(
- new MockPointerControllerPolicyInterface(), looper,
- new NiceMock<MockSpriteController>(looper),
+ new MockPointerControllerPolicyInterface(), looper, spriteController,
[®isteredListener](const sp<android::gui::WindowInfosListener>& listener) {
// Register listener
registeredListener = listener;
@@ -335,10 +333,12 @@
TEST_F(PointerControllerWindowInfoListenerTest,
doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
+ sp<Looper> looper = new Looper(false);
+ auto spriteController = NiceMock<MockSpriteController>(looper);
sp<android::gui::WindowInfosListener> registeredListener;
sp<android::gui::WindowInfosListener> localListenerCopy;
{
- TestPointerController pointerController(registeredListener, new Looper(false));
+ TestPointerController pointerController(registeredListener, looper, spriteController);
ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
localListenerCopy = registeredListener;
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index c3087bc..e2f4072 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4739,6 +4739,97 @@
/**
* @hide
+ * Test method to return the list of UIDs currently marked as ducked because of their
+ * audio focus status
+ * @return the list of UIDs, can be empty when no app is being ducked.
+ */
+ @TestApi
+ @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
+ public @NonNull List<Integer> getFocusDuckedUidsForTest() {
+ try {
+ return getService().getFocusDuckedUidsForTest();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Test method to return the duration of the fade out applied on the players of a focus loser
+ * @return the fade out duration in ms
+ */
+ @TestApi
+ @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
+ public long getFocusFadeOutDurationForTest() {
+ try {
+ return getService().getFocusFadeOutDurationForTest();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Test method to return the length of time after a fade-out before the focus loser is unmuted
+ * (and is faded back in).
+ * @return the time gap after a fade-out completion on focus loss, and fade-in start in ms.
+ */
+ @TestApi
+ @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
+ public long getFocusUnmuteDelayAfterFadeOutForTest() {
+ try {
+ return getService().getFocusUnmuteDelayAfterFadeOutForTest();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Test method to start preventing applications from requesting audio focus during a test,
+ * which could interfere with the functionality/behavior under test.
+ * Calling this method needs to be paired with a call to {@link #exitAudioFocusFreezeForTest}
+ * when the testing is done. If this is not the case (e.g. in case of a test crash),
+ * a death observer mechanism will ensure the system is not left in a bad state, but this should
+ * not be relied on when implementing tests.
+ * @param exemptedUids a list of UIDs that are exempt from the freeze. This would for instance
+ * be those of the test runner and other players used in the test, or the "fake" UIDs used
+ * for testing with {@link #requestAudioFocusForTest(AudioFocusRequest, String, int, int)}.
+ * @return true if the focus freeze mode is successfully entered, false if there was an issue,
+ * such as another freeze in place at the time of invocation.
+ * A false result should result in a test failure as this would indicate the system is not
+ * in a proper state with a predictable behavior for audio focus management.
+ */
+ @TestApi
+ @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ public boolean enterAudioFocusFreezeForTest(@NonNull List<Integer> exemptedUids) {
+ Objects.requireNonNull(exemptedUids);
+ try {
+ final int[] uids = exemptedUids.stream().mapToInt(Integer::intValue).toArray();
+ return getService().enterAudioFocusFreezeForTest(mICallBack, uids);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Test method to end preventing applications from requesting audio focus during a test.
+ * @return true if the focus freeze mode is successfully exited, false if there was an issue,
+ * such as the freeze already having ended, or not started.
+ */
+ @TestApi
+ @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ public boolean exitAudioFocusFreezeForTest() {
+ try {
+ return getService().exitAudioFocusFreezeForTest(mICallBack);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
* Request or lock audio focus.
* This method is to be used by system components that have registered an
* {@link android.media.audiopolicy.AudioPolicy} to request audio focus, but also to "lock" it
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 5cbb4e5..e45ef40 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -529,6 +529,23 @@
long getFadeOutDurationOnFocusLossMillis(in AudioAttributes aa);
+ @EnforcePermission("QUERY_AUDIO_STATE")
+ /* Returns a List<Integer> */
+ @SuppressWarnings(value = {"untyped-collection"})
+ List getFocusDuckedUidsForTest();
+
+ @EnforcePermission("QUERY_AUDIO_STATE")
+ long getFocusFadeOutDurationForTest();
+
+ @EnforcePermission("QUERY_AUDIO_STATE")
+ long getFocusUnmuteDelayAfterFadeOutForTest();
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ boolean enterAudioFocusFreezeForTest(IBinder cb, in int[] uids);
+
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ boolean exitAudioFocusFreezeForTest(IBinder cb);
+
void registerModeDispatcher(IAudioModeDispatcher dispatcher);
oneway void unregisterModeDispatcher(IAudioModeDispatcher dispatcher);
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 304eecb..d294601 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -108,6 +108,7 @@
+ ".permission.MANAGE_MEDIA_PROJECTION)")
void notifyActiveProjectionCapturedContentVisibilityChanged(boolean isVisible);
+ @EnforcePermission("MANAGE_MEDIA_PROJECTION")
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.MANAGE_MEDIA_PROJECTION)")
void addCallback(IMediaProjectionWatcherCallback callback);
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index ac8e4d4..8ed4bf2 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -920,7 +920,7 @@
CountDownLatch addedLatch = new CountDownLatch(1);
CountDownLatch preferenceLatch = new CountDownLatch(1);
- // A dummy callback is required to send route feature info.
+ // A placeholder callback is required to send route feature info.
RouteCallback routeCallback = new RouteCallback() {};
MediaRouter2Manager.Callback managerCallback =
new MediaRouter2Manager.Callback() {
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index c25df6e..c571b74 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -144,6 +144,8 @@
binder::Status ret =
mHintManager->createHintSession(mToken, tids, initialTargetWorkDurationNanos, &session);
if (!ret.isOk() || !session) {
+ ALOGE("%s: PerformanceHint cannot create hint session. %s", __FUNCTION__,
+ ret.exceptionMessage().c_str());
return nullptr;
}
return new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp
index d778feb..d6b7ecf 100644
--- a/packages/CtsShim/build/Android.bp
+++ b/packages/CtsShim/build/Android.bp
@@ -208,3 +208,22 @@
],
min_sdk_version: "24",
}
+
+//##########################################################
+// Variant: Add apk to an apex
+android_app {
+ name: "CtsShimAddApkToApex",
+ sdk_version: "current",
+ srcs: ["shim_add_apk_to_apex/src/android/addapktoapex/app/AddApkToApexDeviceActivity.java"],
+ optimize: {
+ enabled: false,
+ },
+ dex_preopt: {
+ enabled: false,
+ },
+ manifest: "shim_add_apk_to_apex/AndroidManifestAddApkToApex.xml",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.apex.cts.shim.v2_add_apk_to_apex",
+ ],
+}
diff --git a/packages/CtsShim/build/shim_add_apk_to_apex/AndroidManifestAddApkToApex.xml b/packages/CtsShim/build/shim_add_apk_to_apex/AndroidManifestAddApkToApex.xml
new file mode 100644
index 0000000..0e620b0
--- /dev/null
+++ b/packages/CtsShim/build/shim_add_apk_to_apex/AndroidManifestAddApkToApex.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.addapktoapex.app">
+
+ <application>
+ <activity android:name=".AddApkToApexDeviceActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/packages/CtsShim/build/shim_add_apk_to_apex/src/android/addapktoapex/app/AddApkToApexDeviceActivity.java b/packages/CtsShim/build/shim_add_apk_to_apex/src/android/addapktoapex/app/AddApkToApexDeviceActivity.java
new file mode 100644
index 0000000..c68904b
--- /dev/null
+++ b/packages/CtsShim/build/shim_add_apk_to_apex/src/android/addapktoapex/app/AddApkToApexDeviceActivity.java
@@ -0,0 +1,42 @@
+/*
+ * 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 android.addapktoapex.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * A simple activity which logs to Logcat.
+ */
+public class AddApkToApexDeviceActivity extends Activity {
+
+ private static final String TAG = AddApkToApexDeviceActivity.class.getSimpleName();
+
+ /**
+ * The test string to log.
+ */
+ private static final String TEST_STRING = "AddApkToApexTestString";
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ // Log the test string to Logcat.
+ Log.i(TAG, TEST_STRING);
+ }
+
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index f2f019d9..736e0ef 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -34,7 +34,6 @@
import android.os.Bundle;
import android.os.Process;
import android.os.UserManager;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -51,12 +50,10 @@
public class InstallStart extends Activity {
private static final String TAG = InstallStart.class.getSimpleName();
-
- private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = 1;
- private static final int DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = 2;
private PackageManager mPackageManager;
private UserManager mUserManager;
private boolean mAbortInstall = false;
+ private boolean mShouldFinish = true;
private final boolean mLocalLOGV = false;
@@ -131,7 +128,7 @@
mAbortInstall = true;
}
- checkDevicePolicyRestriction();
+ checkDevicePolicyRestrictions();
final String installerPackageNameFromIntent = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
@@ -150,7 +147,9 @@
if (mAbortInstall) {
setResult(RESULT_CANCELED);
- finish();
+ if (mShouldFinish) {
+ finish();
+ }
return;
}
@@ -281,58 +280,52 @@
return originatingUid == installerUid;
}
- private void checkDevicePolicyRestriction() {
- // Check for install apps user restriction first.
- final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
- UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
- if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
- if (mLocalLOGV) Log.i(TAG, "install not allowed: " + UserManager.DISALLOW_INSTALL_APPS);
- mAbortInstall = true;
- showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
- return;
- } else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
- if (mLocalLOGV) {
- Log.i(TAG, "install not allowed by admin; showing "
- + Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
- }
- mAbortInstall = true;
- startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
- return;
- }
+ private void checkDevicePolicyRestrictions() {
+ final String[] restrictions = new String[] {
+ UserManager.DISALLOW_INSTALL_APPS,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
+ };
- final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
- final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());
- final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
- & (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
- if (systemRestriction != 0) {
- if (mLocalLOGV) Log.i(TAG, "Showing DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER");
+ final DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
+ for (String restriction : restrictions) {
+ if (!mUserManager.hasUserRestrictionForUser(restriction, Process.myUserHandle())) {
+ continue;
+ }
+
mAbortInstall = true;
- showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
- } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
- mAbortInstall = true;
- startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
- } else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
- mAbortInstall = true;
- startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
+
+ // If the given restriction is set by an admin, display information about the
+ // admin enforcing the restriction for the affected user. If not enforced by the admin,
+ // show the system dialog.
+ final Intent showAdminSupportDetailsIntent = dpm.createAdminSupportIntent(restriction);
+ if (showAdminSupportDetailsIntent != null) {
+ if (mLocalLOGV) Log.i(TAG, "starting " + showAdminSupportDetailsIntent);
+ startActivity(showAdminSupportDetailsIntent);
+ } else {
+ if (mLocalLOGV) Log.i(TAG, "Restriction set by system: " + restriction);
+ mShouldFinish = false;
+ showDialogInner(restriction);
+ }
+ break;
}
}
/**
- * Replace any dialog shown by the dialog with the one for the given {@link #createDialog(int)}.
+ * Replace any dialog shown by the dialog with the one for the given
+ * {@link #createDialog(String)}.
*
- * @param id The dialog type to add
+ * @param restriction The restriction to create the dialog for
*/
- private void showDialogInner(int id) {
- if (mLocalLOGV) Log.i(TAG, "showDialogInner(" + id + ")");
+ private void showDialogInner(String restriction) {
+ if (mLocalLOGV) Log.i(TAG, "showDialogInner(" + restriction + ")");
DialogFragment currentDialog =
(DialogFragment) getFragmentManager().findFragmentByTag("dialog");
if (currentDialog != null) {
currentDialog.dismissAllowingStateLoss();
}
- DialogFragment newDialog = createDialog(id);
+ DialogFragment newDialog = createDialog(restriction);
if (newDialog != null) {
getFragmentManager().beginTransaction()
.add(newDialog, "dialog").commitAllowingStateLoss();
@@ -342,34 +335,20 @@
/**
* Create a new dialog.
*
- * @param id The id of the dialog (determines dialog type)
+ * @param restriction The restriction to create the dialog for
* @return The dialog
*/
- private DialogFragment createDialog(int id) {
- if (mLocalLOGV) Log.i(TAG, "createDialog(" + id + ")");
- switch (id) {
- case DLG_INSTALL_APPS_RESTRICTED_FOR_USER:
+ private DialogFragment createDialog(String restriction) {
+ if (mLocalLOGV) Log.i(TAG, "createDialog(" + restriction + ")");
+ switch (restriction) {
+ case UserManager.DISALLOW_INSTALL_APPS:
return PackageUtil.SimpleErrorDialog.newInstance(
R.string.install_apps_user_restriction_dlg_text);
- case DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER:
+ case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES:
+ case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY:
return PackageUtil.SimpleErrorDialog.newInstance(
R.string.unknown_apps_user_restriction_dlg_text);
}
return null;
}
-
- private void startAdminSupportDetailsActivity(String restriction) {
- if (mLocalLOGV) Log.i(TAG, "startAdminSupportDetailsActivity(): " + restriction);
-
- // If the given restriction is set by an admin, display information about the
- // admin enforcing the restriction for the affected user.
- final DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
- final Intent showAdminSupportDetailsIntent = dpm.createAdminSupportIntent(restriction);
- if (showAdminSupportDetailsIntent != null) {
- if (mLocalLOGV) Log.i(TAG, "starting " + showAdminSupportDetailsIntent);
- startActivity(showAdminSupportDetailsIntent);
- } else {
- if (mLocalLOGV) Log.w(TAG, "not intent for " + restriction);
- }
- }
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index df91d98..80e8761 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -443,7 +443,7 @@
if (mLocalLOGV) Log.i(TAG, "onResume(): mAppSnippet=" + mAppSnippet);
if (mAppSnippet != null) {
- // load dummy layout with OK button disabled until we override this layout in
+ // load placeholder layout with OK button disabled until we override this layout in
// startInstallConfirm
bindUi();
checkIfAllowedAndInitiateInstall();
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/Keyboards.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/Keyboards.kt
index 3f7cc19..b650034 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/Keyboards.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/Keyboards.kt
@@ -21,7 +21,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.snapshotFlow
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
@@ -29,7 +28,6 @@
/**
* An action when run, hides the keyboard if it's open.
*/
-@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun hideKeyboardAction(): () -> Unit {
val keyboardController = LocalSoftwareKeyboardController.current
@@ -41,7 +39,6 @@
*
* And when user scrolling the lazy list, hides the keyboard if it's open.
*/
-@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun rememberLazyListStateAndHideKeyboardWhenStartScroll(): LazyListState {
val listState = rememberLazyListState()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/KeyboardsTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/KeyboardsTest.kt
index 944ef7f..e9b3109 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/KeyboardsTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/KeyboardsTest.kt
@@ -21,7 +21,6 @@
import androidx.compose.material3.Text
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.SoftwareKeyboardController
@@ -32,12 +31,11 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
-@OptIn(ExperimentalComposeUiApi::class)
@RunWith(AndroidJUnit4::class)
class KeyboardsTest {
@get:Rule
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/theme/SettingsThemeTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/theme/SettingsThemeTest.kt
index 2ff3039..bd8a54b 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/theme/SettingsThemeTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/theme/SettingsThemeTest.kt
@@ -31,10 +31,10 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.anyInt
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
-import org.mockito.Mockito.`when` as whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class SettingsThemeTest {
@@ -55,7 +55,7 @@
@Before
fun setUp() {
whenever(context.resources).thenReturn(resources)
- whenever(resources.getString(anyInt())).thenReturn("")
+ whenever(resources.getString(any())).thenReturn("")
}
private fun mockAndroidConfig(configName: String, configValue: String) {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedTextTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedTextTest.kt
index 2c218e3..5e59620 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedTextTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedTextTest.kt
@@ -32,9 +32,9 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class AnnotatedTextTest {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffoldTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffoldTest.kt
index 872d957..3e8fdec 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffoldTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffoldTest.kt
@@ -33,9 +33,9 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class SettingsScaffoldTest {
diff --git a/packages/SettingsLib/Spa/testutils/Android.bp b/packages/SettingsLib/Spa/testutils/Android.bp
index 65f5d34..4031cd7 100644
--- a/packages/SettingsLib/Spa/testutils/Android.bp
+++ b/packages/SettingsLib/Spa/testutils/Android.bp
@@ -30,7 +30,7 @@
"androidx.compose.ui_ui-test-junit4",
"androidx.compose.ui_ui-test-manifest",
"androidx.lifecycle_lifecycle-runtime-testing",
- "mockito",
+ "mockito-kotlin2",
"truth-prebuilt",
],
kotlincflags: [
diff --git a/packages/SettingsLib/Spa/testutils/build.gradle.kts b/packages/SettingsLib/Spa/testutils/build.gradle.kts
index f5a22c9..50243dc 100644
--- a/packages/SettingsLib/Spa/testutils/build.gradle.kts
+++ b/packages/SettingsLib/Spa/testutils/build.gradle.kts
@@ -41,7 +41,12 @@
api("androidx.arch.core:core-testing:2.2.0-alpha01")
api("androidx.compose.ui:ui-test-junit4:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-runtime-testing")
+ api("org.mockito.kotlin:mockito-kotlin:5.1.0")
+ api("org.mockito:mockito-core") {
+ version {
+ strictly("2.28.2")
+ }
+ }
api(libs.truth)
- api("org.mockito:mockito-core:2.21.0")
debugApi("androidx.compose.ui:ui-test-manifest:$jetpackComposeVersion")
}
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/MockitoHelper.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/MockitoHelper.kt
deleted file mode 100644
index 5ba54c1..0000000
--- a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/MockitoHelper.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2022 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.testutils
-
-import org.mockito.Mockito
-
-/**
- * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when null is
- * returned.
- *
- * Generic T is nullable because implicitly bounded by Any?.
- */
-fun <T> any(type: Class<T>): T = Mockito.any(type)
-
-inline fun <reified T> any(): T = any(T::class.java)
diff --git a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
index 0b74fa8..d1bcb57 100644
--- a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
@@ -2,7 +2,7 @@
flag {
name: "use_media_router2_for_info_media_manager"
- namespace: "placeholder_namespace"
+ namespace: "media_solutions"
description: "Gates whether to use a MediaRouter2-based implementation of InfoMediaManager, instead of the legacy MediaRouter2Manager-based implementation."
bug: "192657812"
}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
index 7455912..4fcdc8b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
@@ -27,6 +27,7 @@
import com.android.settingslib.R;
import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.utils.ThreadUtils;
/**
* Preference controller for bluetooth address
@@ -74,13 +75,18 @@
protected void updateConnectivity() {
BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
if (bluetooth != null && mBtAddress != null) {
- String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null;
- if (!TextUtils.isEmpty(address)) {
- // Convert the address to lowercase for consistency with the wifi MAC address.
- mBtAddress.setSummary(address.toLowerCase());
- } else {
- mBtAddress.setSummary(R.string.status_unavailable);
- }
+ ThreadUtils.postOnBackgroundThread(() -> {
+ String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null;
+ ThreadUtils.postOnMainThread(() -> {
+ if (!TextUtils.isEmpty(address)) {
+ // Convert the address to lowercase for consistency with the wifi MAC
+ // address.
+ mBtAddress.setSummary(address.toLowerCase());
+ } else {
+ mBtAddress.setSummary(R.string.status_unavailable);
+ }
+ });
+ });
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
index 9ab84d2..f90a17a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
@@ -45,6 +45,7 @@
private static final int EXTREME_LOW_BATTERY_THRESHOLD = 3;
private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
+ public static final int BATTERY_LEVEL_UNKNOWN = -1;
public static final int CHARGING_UNKNOWN = -1;
public static final int CHARGING_SLOWLY = 0;
public static final int CHARGING_REGULAR = 1;
@@ -186,12 +187,13 @@
/** Gets the battery level from the intent. */
public static int getBatteryLevel(Intent batteryChangedIntent) {
if (batteryChangedIntent == null) {
- return -1; /*invalid battery level*/
+ return BATTERY_LEVEL_UNKNOWN;
}
- final int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ final int level =
+ batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, BATTERY_LEVEL_UNKNOWN);
final int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
return scale == 0
- ? -1 /*invalid battery level*/
+ ? BATTERY_LEVEL_UNKNOWN
: Math.round((level / (float) scale) * 100f);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelper.java b/packages/SettingsLib/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelper.java
index 1134d13..bc8c560 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/dpp/WifiDppIntentHelper.java
@@ -28,6 +28,11 @@
* Wifi dpp intent helper functions to share between the Settings App and SystemUI.
*/
public class WifiDppIntentHelper {
+ /**
+ * Action added to the intent when app wants to launch the QR code generator with lock screen.
+ */
+ public static final String ACTION_CONFIGURATOR_AUTH_QR_CODE_GENERATOR =
+ "android.settings.WIFI_DPP_CONFIGURATOR_AUTH_QR_CODE_GENERATOR";
static final String EXTRA_WIFI_SECURITY = "security";
/** The data corresponding to {@code WifiConfiguration} SSID */
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index 5c55a43..c037c40 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -42,7 +42,10 @@
name: "SettingsLibRoboTests",
srcs: ["src/**/*.java"],
static_libs: [
+ "Settings_robolectric_meta_service_file",
+ "Robolectric_shadows_androidx_fragment_upstream",
"SettingsLib-robo-testutils",
+ "androidx.fragment_fragment",
"androidx.test.core",
"androidx.core_core",
"testng", // TODO: remove once JUnit on Android provides assertThrows
@@ -53,6 +56,20 @@
test_options: {
timeout: 36000,
},
+ upstream: true,
+}
+
+java_genrule {
+ name: "Settings_robolectric_meta_service_file",
+ out: ["robolectric_meta_service_file.jar"],
+ tools: ["soong_zip"],
+ cmd: "mkdir -p $(genDir)/META-INF/services/ && touch $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider &&" +
+ "echo -e 'org.robolectric.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
+ "echo -e 'org.robolectric.shadows.multidex.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
+ "echo -e 'org.robolectric.shadows.httpclient.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
+ //"echo -e 'com.android.settings.testutils.shadow.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
+ "echo -e 'com.android.settingslib.testutils.shadow.Shadows' >> $(genDir)/META-INF/services/org.robolectric.internal.ShadowProvider && " +
+ "$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)/META-INF/services/",
}
java_library {
@@ -60,9 +77,23 @@
srcs: [
"testutils/com/android/settingslib/testutils/**/*.java",
],
-
+ javacflags: [
+ "-Aorg.robolectric.annotation.processing.shadowPackage=com.android.settingslib.testutils.shadow",
+ "-Aorg.robolectric.annotation.processing.sdkCheckMode=ERROR",
+ // Uncomment the below to debug annotation processors not firing.
+ //"-verbose",
+ //"-XprintRounds",
+ //"-XprintProcessorInfo",
+ //"-Xlint",
+ //"-J-verbose",
+ ],
+ plugins: [
+ "auto_value_plugin_1.9",
+ "auto_value_builder_plugin_1.9",
+ "Robolectric_processor_upstream",
+ ],
libs: [
- "Robolectric_all-target",
+ "Robolectric_all-target_upstream",
"mockito-robolectric-prebuilt",
"truth-prebuilt",
],
diff --git a/packages/SettingsLib/tests/robotests/config/robolectric.properties b/packages/SettingsLib/tests/robotests/config/robolectric.properties
index fab7251..2a9e50d 100644
--- a/packages/SettingsLib/tests/robotests/config/robolectric.properties
+++ b/packages/SettingsLib/tests/robotests/config/robolectric.properties
@@ -1 +1,2 @@
sdk=NEWEST_SDK
+instrumentedPackages=androidx.preference
diff --git a/packages/SettingsLib/tests/robotests/fragment/Android.bp b/packages/SettingsLib/tests/robotests/fragment/Android.bp
new file mode 100644
index 0000000..3e67156
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/fragment/Android.bp
@@ -0,0 +1,40 @@
+//#############################################
+// Compile Robolectric shadows framework misapplied to androidx
+//#############################################
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+ name: "Robolectric_shadows_androidx_fragment_upstream",
+ srcs: [
+ "src/main/java/**/*.java",
+ "src/main/java/**/*.kt",
+ ],
+ javacflags: [
+ "-Aorg.robolectric.annotation.processing.shadowPackage=org.robolectric.shadows.androidx.fragment",
+ "-Aorg.robolectric.annotation.processing.sdkCheckMode=ERROR",
+ // Uncomment the below to debug annotation processors not firing.
+ //"-verbose",
+ //"-XprintRounds",
+ //"-XprintProcessorInfo",
+ //"-Xlint",
+ //"-J-verbose",
+ ],
+ libs: [
+ "Robolectric_all-target_upstream",
+ "androidx.fragment_fragment",
+ ],
+ plugins: [
+ "auto_value_plugin_1.9",
+ "auto_value_builder_plugin_1.9",
+ "Robolectric_processor_upstream",
+ ],
+
+}
diff --git a/packages/SettingsLib/tests/robotests/fragment/BUILD b/packages/SettingsLib/tests/robotests/fragment/BUILD
new file mode 100644
index 0000000..393a02e
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/fragment/BUILD
@@ -0,0 +1,69 @@
+load("//third_party/java/android/android_sdk_linux/extras/android/compatibility/jetify:jetify.bzl", "jetify_android_library", "jetify_android_local_test")
+
+package(
+ default_applicable_licenses = ["//third_party/java_src/robolectric:license"],
+ default_visibility = ["//third_party/java_src/robolectric:__subpackages__"],
+)
+
+licenses(["notice"])
+
+#==============================================================================
+# Test resources library
+#==============================================================================
+jetify_android_library(
+ name = "test_resources",
+ custom_package = "org.robolectric.shadows.androidx.fragment",
+ manifest = "src/test/AndroidManifest.xml",
+ resource_files = glob(
+ ["src/test/resources/**/*"],
+ ),
+)
+
+#==============================================================================
+# AndroidX fragment module library
+#==============================================================================
+jetify_android_library(
+ name = "androidx_fragment",
+ testonly = 1,
+ srcs = glob(
+ ["src/main/java/**"],
+ ),
+ custom_package = "org.robolectric.shadows.androidx.fragment",
+ javacopts = [
+ "-Aorg.robolectric.annotation.processing.shadowPackage=org.robolectric.shadows.androidx.fragment",
+ ],
+ jetify_sources = True,
+ plugins = [
+ "//java/com/google/thirdparty/robolectric/processor",
+ ],
+ deps = [
+ "//third_party/java/androidx/core",
+ "//third_party/java/androidx/fragment",
+ "//third_party/java/androidx/lifecycle",
+ "//third_party/java_src/robolectric/shadowapi",
+ "//third_party/java_src/robolectric/shadows/framework",
+ ],
+)
+
+[
+ jetify_android_local_test(
+ name = "test_" + src.rstrip(".java"),
+ size = "small",
+ srcs = glob(
+ ["src/test/java/**/*.java"],
+ ),
+ jetify_sources = True,
+ deps = [
+ ":androidx_fragment",
+ ":test_resources",
+ "//third_party/java/androidx/fragment",
+ "//third_party/java/androidx/loader",
+ "//third_party/java/mockito",
+ "//third_party/java/robolectric",
+ "//third_party/java/truth",
+ ],
+ )
+ for src in glob(
+ ["src/test/java/**/*Test.java"],
+ )
+]
diff --git a/packages/SettingsLib/tests/robotests/fragment/build.gradle b/packages/SettingsLib/tests/robotests/fragment/build.gradle
new file mode 100644
index 0000000..d9dcd84
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/fragment/build.gradle
@@ -0,0 +1,48 @@
+plugins {
+ id "net.ltgt.errorprone" version "0.0.13"
+}
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 28
+
+ android {
+ sourceSets {
+ main {
+ res.srcDirs = ['src/test/resources/res']
+ }
+ }
+ testOptions {
+ unitTests {
+ includeAndroidResources = true
+ }
+ }
+ }
+}
+
+dependencies {
+ // Project dependencies
+ compileOnly project(":robolectric")
+
+ // Compile dependencies
+ compileOnly AndroidSdk.MAX_SDK.coordinates
+ compileOnly "androidx.core:core:1.0.0-rc02"
+ compileOnly 'androidx.fragment:fragment:1.0.0-rc02'
+ compileOnly "androidx.lifecycle:lifecycle-viewmodel:2.0.0-rc01"
+ compileOnly "androidx.lifecycle:lifecycle-common:2.0.0-beta01"
+
+ // Testing dependencies
+ testImplementation "com.google.truth:truth:0.44"
+ testImplementation "org.mockito:mockito-core:2.5.4"
+ testImplementation "androidx.arch.core:core-common:2.0.0-beta01"
+ testImplementation "androidx.arch.core:core-runtime:2.0.0-rc01"
+ testImplementation "androidx.collection:collection:1.0.0-rc01"
+ testImplementation "androidx.core:core:1.0.0-rc02"
+ testImplementation 'androidx.fragment:fragment:1.0.0-rc02'
+ testImplementation "androidx.lifecycle:lifecycle-viewmodel:2.0.0-rc01"
+ testImplementation "androidx.lifecycle:lifecycle-common:2.0.0-beta01"
+ testImplementation "androidx.lifecycle:lifecycle-runtime:2.0.0-rc01"
+ testImplementation "androidx.lifecycle:lifecycle-livedata-core:2.0.0-rc01"
+ testImplementation "androidx.loader:loader:1.0.0-rc02"
+}
diff --git a/packages/SettingsLib/tests/robotests/fragment/src/main/java/org/robolectric/shadows/androidx/fragment/FragmentController.java b/packages/SettingsLib/tests/robotests/fragment/src/main/java/org/robolectric/shadows/androidx/fragment/FragmentController.java
new file mode 100644
index 0000000..c688683
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/fragment/src/main/java/org/robolectric/shadows/androidx/fragment/FragmentController.java
@@ -0,0 +1,348 @@
+/*
+ * 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 org.robolectric.shadows.androidx.fragment;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import org.robolectric.android.controller.ActivityController;
+import org.robolectric.android.controller.ComponentController;
+import org.robolectric.util.ReflectionHelpers;
+
+/** A Controller that can be used to drive the lifecycle of a {@link Fragment} */
+public class FragmentController<F extends Fragment>
+ extends ComponentController<FragmentController<F>, F> {
+
+ private final F mFragment;
+ private final ActivityController<? extends FragmentActivity> mActivityController;
+
+ private FragmentController(F fragment, Class<? extends FragmentActivity> activityClass) {
+ this(fragment, activityClass, null /*intent*/, null /*arguments*/);
+ }
+
+ private FragmentController(
+ F fragment, Class<? extends FragmentActivity> activityClass, Intent intent) {
+ this(fragment, activityClass, intent, null /*arguments*/);
+ }
+
+ private FragmentController(
+ F fragment, Class<? extends FragmentActivity> activityClass, Bundle arguments) {
+ this(fragment, activityClass, null /*intent*/, arguments);
+ }
+
+ private FragmentController(
+ F fragment,
+ Class<? extends FragmentActivity> activityClass,
+ Intent intent,
+ Bundle arguments) {
+ super(fragment, intent);
+ this.mFragment = fragment;
+ if (arguments != null) {
+ this.mFragment.setArguments(arguments);
+ }
+ this.mActivityController =
+ ActivityController.of(ReflectionHelpers.callConstructor(activityClass), intent);
+ }
+
+ /**
+ * Generate the {@link FragmentController} for specific fragment.
+ *
+ * @param fragment the fragment which you'd like to drive lifecycle
+ * @return {@link FragmentController}
+ */
+ public static <F extends Fragment> FragmentController<F> of(F fragment) {
+ return new FragmentController<>(fragment, FragmentControllerActivity.class);
+ }
+
+ /**
+ * Generate the {@link FragmentController} for specific fragment and intent.
+ *
+ * @param fragment the fragment which you'd like to drive lifecycle
+ * @param intent the intent which will be retained by activity
+ * @return {@link FragmentController}
+ */
+ public static <F extends Fragment> FragmentController<F> of(F fragment, Intent intent) {
+ return new FragmentController<>(fragment, FragmentControllerActivity.class, intent);
+ }
+
+ /**
+ * Generate the {@link FragmentController} for specific fragment and arguments.
+ *
+ * @param fragment the fragment which you'd like to drive lifecycle
+ * @param arguments the arguments which will be retained by fragment
+ * @return {@link FragmentController}
+ */
+ public static <F extends Fragment> FragmentController<F> of(F fragment, Bundle arguments) {
+ return new FragmentController<>(fragment, FragmentControllerActivity.class, arguments);
+ }
+
+ /**
+ * Generate the {@link FragmentController} for specific fragment and activity class.
+ *
+ * @param fragment the fragment which you'd like to drive lifecycle
+ * @param activityClass the activity which will be attached by fragment
+ * @return {@link FragmentController}
+ */
+ public static <F extends Fragment> FragmentController<F> of(
+ F fragment, Class<? extends FragmentActivity> activityClass) {
+ return new FragmentController<>(fragment, activityClass);
+ }
+
+ /**
+ * Generate the {@link FragmentController} for specific fragment, intent and arguments.
+ *
+ * @param fragment the fragment which you'd like to drive lifecycle
+ * @param intent the intent which will be retained by activity
+ * @param arguments the arguments which will be retained by fragment
+ * @return {@link FragmentController}
+ */
+ public static <F extends Fragment> FragmentController<F> of(
+ F fragment, Intent intent, Bundle arguments) {
+ return new FragmentController<>(fragment, FragmentControllerActivity.class, intent,
+ arguments);
+ }
+
+ /**
+ * Generate the {@link FragmentController} for specific fragment, activity class and intent.
+ *
+ * @param fragment the fragment which you'd like to drive lifecycle
+ * @param activityClass the activity which will be attached by fragment
+ * @param intent the intent which will be retained by activity
+ * @return {@link FragmentController}
+ */
+ public static <F extends Fragment> FragmentController<F> of(
+ F fragment, Class<? extends FragmentActivity> activityClass, Intent intent) {
+ return new FragmentController<>(fragment, activityClass, intent);
+ }
+
+ /**
+ * Generate the {@link FragmentController} for specific fragment, activity class and arguments.
+ *
+ * @param fragment the fragment which you'd like to drive lifecycle
+ * @param activityClass the activity which will be attached by fragment
+ * @param arguments the arguments which will be retained by fragment
+ * @return {@link FragmentController}
+ */
+ public static <F extends Fragment> FragmentController<F> of(
+ F fragment, Class<? extends FragmentActivity> activityClass, Bundle arguments) {
+ return new FragmentController<>(fragment, activityClass, arguments);
+ }
+
+ /**
+ * Generate the {@link FragmentController} for specific fragment, activity class, intent and
+ * arguments.
+ *
+ * @param fragment the fragment which you'd like to drive lifecycle
+ * @param activityClass the activity which will be attached by fragment
+ * @param intent the intent which will be retained by activity
+ * @param arguments the arguments which will be retained by fragment
+ * @return {@link FragmentController}
+ */
+ public static <F extends Fragment> FragmentController<F> of(
+ F fragment,
+ Class<? extends FragmentActivity> activityClass,
+ Intent intent,
+ Bundle arguments) {
+ return new FragmentController<>(fragment, activityClass, intent, arguments);
+ }
+
+ /**
+ * Sets up the given fragment by attaching it to an activity, calling its onCreate() through
+ * onResume() lifecycle methods, and then making it visible. Note that the fragment will be
+ * added
+ * to the view with ID 1.
+ */
+ public static <F extends Fragment> F setupFragment(F fragment) {
+ return FragmentController.of(fragment).create().start().resume().visible().get();
+ }
+
+ /**
+ * Sets up the given fragment by attaching it to an activity, calling its onCreate() through
+ * onResume() lifecycle methods, and then making it visible. Note that the fragment will be
+ * added
+ * to the view with ID 1.
+ */
+ public static <F extends Fragment> F setupFragment(
+ F fragment, Class<? extends FragmentActivity> fragmentActivityClass) {
+ return FragmentController.of(fragment, fragmentActivityClass)
+ .create()
+ .start()
+ .resume()
+ .visible()
+ .get();
+ }
+
+ /**
+ * Sets up the given fragment by attaching it to an activity created with the given bundle,
+ * calling its onCreate() through onResume() lifecycle methods, and then making it visible. Note
+ * that the fragment will be added to the view with ID 1.
+ */
+ public static <F extends Fragment> F setupFragment(
+ F fragment, Class<? extends FragmentActivity> fragmentActivityClass, Bundle bundle) {
+ return FragmentController.of(fragment, fragmentActivityClass)
+ .create(bundle)
+ .start()
+ .resume()
+ .visible()
+ .get();
+ }
+
+ /**
+ * Sets up the given fragment by attaching it to an activity created with the given bundle and
+ * container id, calling its onCreate() through onResume() lifecycle methods, and then making it
+ * visible.
+ */
+ public static <F extends Fragment> F setupFragment(
+ F fragment,
+ Class<? extends FragmentActivity> fragmentActivityClass,
+ int containerViewId,
+ Bundle bundle) {
+ return FragmentController.of(fragment, fragmentActivityClass)
+ .create(containerViewId, bundle)
+ .start()
+ .resume()
+ .visible()
+ .get();
+ }
+
+ /**
+ * Creates the activity with {@link Bundle} and adds the fragment to the view with ID {@code
+ * contentViewId}.
+ */
+ public FragmentController<F> create(final int contentViewId, final Bundle bundle) {
+ shadowMainLooper.runPaused(
+ new Runnable() {
+ @Override
+ public void run() {
+ mActivityController
+ .create(bundle)
+ .get()
+ .getSupportFragmentManager()
+ .beginTransaction()
+ .add(contentViewId, mFragment)
+ .commit();
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Creates the activity with {@link Bundle} and adds the fragment to it. Note that the fragment
+ * will be added to the view with ID 1.
+ */
+ public FragmentController<F> create(final Bundle bundle) {
+ return create(1, bundle);
+ }
+
+ /**
+ * Creates the {@link Fragment} in a newly initialized state and hence will receive a null
+ * savedInstanceState {@link Bundle parameter}
+ */
+ @Override
+ public FragmentController<F> create() {
+ return create(null);
+ }
+
+ /** Drive lifecycle of activity to Start lifetime */
+ public FragmentController<F> start() {
+ shadowMainLooper.runPaused(
+ new Runnable() {
+ @Override
+ public void run() {
+ mActivityController.start();
+ }
+ });
+ return this;
+ }
+
+ /** Drive lifecycle of activity to Resume lifetime */
+ public FragmentController<F> resume() {
+ shadowMainLooper.runPaused(
+ new Runnable() {
+ @Override
+ public void run() {
+ mActivityController.resume();
+ }
+ });
+ return this;
+ }
+
+ /** Drive lifecycle of activity to Pause lifetime */
+ public FragmentController<F> pause() {
+ shadowMainLooper.runPaused(
+ new Runnable() {
+ @Override
+ public void run() {
+ mActivityController.pause();
+ }
+ });
+ return this;
+ }
+
+ /** Drive lifecycle of activity to Stop lifetime */
+ public FragmentController<F> stop() {
+ shadowMainLooper.runPaused(
+ new Runnable() {
+ @Override
+ public void run() {
+ mActivityController.stop();
+ }
+ });
+ return this;
+ }
+
+ /** Drive lifecycle of activity to Destroy lifetime */
+ @Override
+ public FragmentController<F> destroy() {
+ shadowMainLooper.runPaused(
+ new Runnable() {
+ @Override
+ public void run() {
+ mActivityController.destroy();
+ }
+ });
+ return this;
+ }
+
+ /** Let activity can be visible lifetime */
+ public FragmentController<F> visible() {
+ shadowMainLooper.runPaused(
+ new Runnable() {
+ @Override
+ public void run() {
+ mActivityController.visible();
+ }
+ });
+ return this;
+ }
+
+ private static class FragmentControllerActivity extends FragmentActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ LinearLayout view = new LinearLayout(this);
+ view.setId(1);
+
+ setContentView(view);
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/fragment/src/main/java/org/robolectric/shadows/androidx/fragment/package-info.java b/packages/SettingsLib/tests/robotests/fragment/src/main/java/org/robolectric/shadows/androidx/fragment/package-info.java
new file mode 100644
index 0000000..dd89441
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/fragment/src/main/java/org/robolectric/shadows/androidx/fragment/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Testing infrastructure for androidx.fragment library.
+ *
+ * <p>To use this in your project, add the artifact {@code
+ * org.robolectric:shadows-androidx-fragment} to your project.
+ */
+package org.robolectric.shadows.androidx.fragment;
diff --git a/packages/SettingsLib/tests/robotests/fragment/src/test/AndroidManifest.xml b/packages/SettingsLib/tests/robotests/fragment/src/test/AndroidManifest.xml
new file mode 100644
index 0000000..8493c02
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/fragment/src/test/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.robolectric.shadows.androidx.fragment">
+
+ <uses-sdk android:targetSdkVersion="28"/>
+</manifest>
diff --git a/packages/SettingsLib/tests/robotests/fragment/src/test/java/org/robolectric/shadows/androidx/fragment/FragmentControllerTest.java b/packages/SettingsLib/tests/robotests/fragment/src/test/java/org/robolectric/shadows/androidx/fragment/FragmentControllerTest.java
new file mode 100644
index 0000000..ef63058
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/fragment/src/test/java/org/robolectric/shadows/androidx/fragment/FragmentControllerTest.java
@@ -0,0 +1,360 @@
+/*
+ * 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 org.robolectric.shadows.androidx.fragment;
+
+import static android.os.Looper.getMainLooper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Tests for {@link FragmentController} */
+@RunWith(RobolectricTestRunner.class)
+public class FragmentControllerTest {
+
+ @After
+ public void tearDown() {
+ TranscriptFragment.clearLifecycleEvents();
+ }
+
+ @Test
+ public void initialNotAttached() {
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment());
+
+ assertThat(controller.get().getView()).isNull();
+ assertThat(controller.get().getActivity()).isNull();
+ assertThat(controller.get().isAdded()).isFalse();
+ }
+
+ @Test
+ public void initialNotAttached_customActivity() {
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment(), TestActivity.class);
+
+ assertThat(controller.get().getView()).isNull();
+ assertThat(controller.get().getActivity()).isNull();
+ assertThat(controller.get().isAdded()).isFalse();
+ }
+
+ @Test
+ public void attachedAfterCreate() {
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment());
+
+ controller.create();
+ shadowOf(getMainLooper()).idle();
+
+ assertThat(controller.get().getActivity()).isNotNull();
+ assertThat(controller.get().isAdded()).isTrue();
+ assertThat(controller.get().isResumed()).isFalse();
+ }
+
+ @Test
+ public void attachedAfterCreate_customActivity() {
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment(), TestActivity.class);
+
+ controller.create();
+ shadowOf(getMainLooper()).idle();
+
+ assertThat(controller.get().getActivity()).isNotNull();
+ assertThat(controller.get().getActivity()).isInstanceOf(TestActivity.class);
+ assertThat(controller.get().isAdded()).isTrue();
+ assertThat(controller.get().isResumed()).isFalse();
+ }
+
+ @Test
+ public void attachedAfterCreate_customizedViewId() {
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment(), CustomizedViewIdTestActivity.class);
+
+ controller.create(R.id.custom_activity_view, null).start();
+
+ assertThat(controller.get().getView()).isNotNull();
+ assertThat(controller.get().getActivity()).isNotNull();
+ assertThat(controller.get().isAdded()).isTrue();
+ assertThat(controller.get().isResumed()).isFalse();
+ assertThat((TextView) controller.get().getView().findViewById(R.id.tacos)).isNotNull();
+ }
+
+ @Test
+ public void hasViewAfterStart() {
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment());
+
+ controller.create().start();
+
+ assertThat(controller.get().getView()).isNotNull();
+ }
+
+ @Test
+ public void isResumed() {
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment(), TestActivity.class);
+
+ controller.create().start().resume();
+
+ assertThat(controller.get().getView()).isNotNull();
+ assertThat(controller.get().getActivity()).isNotNull();
+ assertThat(controller.get().isAdded()).isTrue();
+ assertThat(controller.get().isResumed()).isTrue();
+ assertThat((TextView) controller.get().getView().findViewById(R.id.tacos)).isNotNull();
+ }
+
+ @Test
+ public void isPaused() {
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment(), TestActivity.class);
+
+ controller.create().start().resume().pause();
+
+ assertThat(controller.get().getView()).isNotNull();
+ assertThat(controller.get().getActivity()).isNotNull();
+ assertThat(controller.get().isAdded()).isTrue();
+ assertThat(controller.get().isResumed()).isFalse();
+ assertThat(controller.get().getLifecycleEvents())
+ .containsExactly("onCreate", "onStart", "onResume", "onPause")
+ .inOrder();
+ }
+
+ @Test
+ public void isStopped() {
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment(), TestActivity.class);
+
+ controller.create().start().resume().pause().stop();
+
+ assertThat(controller.get().getView()).isNotNull();
+ assertThat(controller.get().getActivity()).isNotNull();
+ assertThat(controller.get().isAdded()).isTrue();
+ assertThat(controller.get().isResumed()).isFalse();
+ assertThat(controller.get().getLifecycleEvents())
+ .containsExactly("onCreate", "onStart", "onResume", "onPause", "onStop")
+ .inOrder();
+ }
+
+ @Test
+ public void withIntent() {
+ final Intent intent = generateTestIntent();
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment(), TestActivity.class, intent);
+
+ controller.create();
+ shadowOf(getMainLooper()).idle();
+ final Intent intentInFragment = controller.get().getActivity().getIntent();
+
+ assertThat(intentInFragment.getAction()).isEqualTo("test_action");
+ assertThat(intentInFragment.getExtras().getString("test_key")).isEqualTo("test_value");
+ }
+
+ @Test
+ public void withArguments() {
+ final Bundle bundle = generateTestBundle();
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment(), TestActivity.class, bundle);
+
+ controller.create();
+ final Bundle args = controller.get().getArguments();
+
+ assertThat(args.getString("test_key")).isEqualTo("test_value");
+ }
+
+ @Test
+ public void withIntentAndArguments() {
+ final Bundle bundle = generateTestBundle();
+ final Intent intent = generateTestIntent();
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment(), TestActivity.class, intent, bundle);
+
+ controller.create();
+ shadowOf(getMainLooper()).idle();
+ final Intent intentInFragment = controller.get().getActivity().getIntent();
+ final Bundle args = controller.get().getArguments();
+
+ assertThat(intentInFragment.getAction()).isEqualTo("test_action");
+ assertThat(intentInFragment.getExtras().getString("test_key")).isEqualTo("test_value");
+ assertThat(args.getString("test_key")).isEqualTo("test_value");
+ }
+
+ @Test
+ public void visible() {
+ final FragmentController<TranscriptFragment> controller =
+ FragmentController.of(new TranscriptFragment(), TestActivity.class);
+
+ controller.create().start().resume();
+
+ assertThat(controller.get().isVisible()).isFalse();
+
+ controller.visible();
+
+ assertThat(controller.get().isVisible()).isTrue();
+ }
+
+ @Test
+ public void setupFragmentWithFragment_fragmentHasCorrectLifecycle() {
+ TranscriptFragment fragment = FragmentController.setupFragment(new TranscriptFragment());
+
+ assertThat(fragment.getLifecycleEvents())
+ .containsExactly("onCreate", "onStart", "onResume")
+ .inOrder();
+ assertThat(fragment.isVisible()).isTrue();
+ }
+
+ @Test
+ public void setupFragmentWithFragmentAndActivity_fragmentHasCorrectLifecycle() {
+ TranscriptFragment fragment =
+ FragmentController.setupFragment(new TranscriptFragment(), TestActivity.class);
+
+ assertThat(fragment.getLifecycleEvents())
+ .containsExactly("onCreate", "onStart", "onResume")
+ .inOrder();
+ assertThat(fragment.isVisible()).isTrue();
+ }
+
+ @Test
+ public void setupFragmentWithFragmentAndActivityAndBundle_HasCorrectLifecycle() {
+ Bundle testBundle = generateTestBundle();
+ TranscriptFragment fragment =
+ FragmentController.setupFragment(new TranscriptFragment(), TestActivity.class,
+ testBundle);
+
+ assertThat(fragment.getLifecycleEvents())
+ .containsExactly("onCreate", "onStart", "onResume")
+ .inOrder();
+ assertThat(fragment.isVisible()).isTrue();
+ }
+
+ @Test
+ public void
+ setupFragmentWithFragment_Activity_ContainViewIdAndBundle_HasCorrectLifecycle() {
+ Bundle testBundle = generateTestBundle();
+ TranscriptFragment fragment =
+ FragmentController.setupFragment(
+ new TranscriptFragment(),
+ CustomizedViewIdTestActivity.class,
+ R.id.custom_activity_view,
+ testBundle);
+
+ assertThat(fragment.getLifecycleEvents())
+ .containsExactly("onCreate", "onStart", "onResume")
+ .inOrder();
+ assertThat(fragment.isVisible()).isTrue();
+ }
+
+ private Intent generateTestIntent() {
+ final Intent testIntent = new Intent("test_action").putExtra("test_key", "test_value");
+ return testIntent;
+ }
+
+ private Bundle generateTestBundle() {
+ final Bundle testBundle = new Bundle();
+ testBundle.putString("test_key", "test_value");
+
+ return testBundle;
+ }
+
+ /** A Fragment which can record lifecycle status for test. */
+ public static class TranscriptFragment extends Fragment {
+
+ public static final List<String> sLifecycleEvents = new ArrayList<>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ sLifecycleEvents.add("onCreate");
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ sLifecycleEvents.add("onStart");
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ sLifecycleEvents.add("onResume");
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ sLifecycleEvents.add("onPause");
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ sLifecycleEvents.add("onStop");
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_contents, container, false);
+ }
+
+ public List<String> getLifecycleEvents() {
+ return sLifecycleEvents;
+ }
+
+ public static void clearLifecycleEvents() {
+ sLifecycleEvents.clear();
+ }
+ }
+
+ /** A Activity which set a default view for test. */
+ public static class TestActivity extends FragmentActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ LinearLayout view = new LinearLayout(this);
+ view.setId(1);
+
+ setContentView(view);
+ }
+ }
+
+ /** A Activity which has a custom view for test. */
+ public static class CustomizedViewIdTestActivity extends FragmentActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.custom_activity_view);
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/fragment/src/test/resources/res/layout/custom_activity_view.xml b/packages/SettingsLib/tests/robotests/fragment/src/test/resources/res/layout/custom_activity_view.xml
new file mode 100644
index 0000000..c074f30
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/fragment/src/test/resources/res/layout/custom_activity_view.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/custom_activity_view"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+</LinearLayout>
diff --git a/packages/SettingsLib/tests/robotests/fragment/src/test/resources/res/layout/fragment_contents.xml b/packages/SettingsLib/tests/robotests/fragment/src/test/resources/res/layout/fragment_contents.xml
new file mode 100644
index 0000000..425b2bb
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/fragment/src/test/resources/res/layout/fragment_contents.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/tacos"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="TACOS"/>
+
+ <TextView
+ android:id="@+id/burritos"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="BURRITOS"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 4a913c8..bb72375 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -25,7 +25,6 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -58,12 +57,10 @@
import org.robolectric.shadows.ShadowSettings;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {UtilsTest.ShadowSecure.class, UtilsTest.ShadowLocationManager.class})
+@Config(shadows = {UtilsTest.ShadowLocationManager.class})
public class UtilsTest {
private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
private static final String TAG = "UtilsTest";
@@ -94,7 +91,7 @@
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
when(mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager);
- ShadowSecure.reset();
+ ShadowSettings.ShadowSecure.reset();
mAudioManager = mContext.getSystemService(AudioManager.class);
}
@@ -111,15 +108,16 @@
Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.LOCATION_CHANGER, Settings.Secure.LOCATION_CHANGER_UNKNOWN))
- .isEqualTo(Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
+ Settings.Secure.LOCATION_CHANGER,
+ Settings.Secure.LOCATION_CHANGER_UNKNOWN)).isEqualTo(
+ Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
}
@Test
public void testFormatPercentage_RoundTrue_RoundUpIfPossible() {
- final String[] expectedPercentages = {PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_1,
- PERCENTAGE_1, PERCENTAGE_49, PERCENTAGE_49, PERCENTAGE_50, PERCENTAGE_50,
- PERCENTAGE_100};
+ final String[] expectedPercentages =
+ {PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_1, PERCENTAGE_1, PERCENTAGE_49,
+ PERCENTAGE_49, PERCENTAGE_50, PERCENTAGE_50, PERCENTAGE_100};
for (int i = 0, size = TEST_PERCENTAGES.length; i < size; i++) {
final String percentage = Utils.formatPercentage(TEST_PERCENTAGES[i], true);
@@ -129,9 +127,9 @@
@Test
public void testFormatPercentage_RoundFalse_NoRound() {
- final String[] expectedPercentages = {PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_0,
- PERCENTAGE_0, PERCENTAGE_49, PERCENTAGE_49, PERCENTAGE_49, PERCENTAGE_50,
- PERCENTAGE_100};
+ final String[] expectedPercentages =
+ {PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_49,
+ PERCENTAGE_49, PERCENTAGE_49, PERCENTAGE_50, PERCENTAGE_100};
for (int i = 0, size = TEST_PERCENTAGES.length; i < size; i++) {
final String percentage = Utils.formatPercentage(TEST_PERCENTAGES[i], false);
@@ -143,12 +141,7 @@
public void testGetDefaultStorageManagerDaysToRetain_storageManagerDaysToRetainUsesResources() {
Resources resources = mock(Resources.class);
when(resources.getInteger(
- eq(
- com.android
- .internal
- .R
- .integer
- .config_storageManagerDaystoRetainDefault)))
+ eq(com.android.internal.R.integer.config_storageManagerDaystoRetainDefault)))
.thenReturn(60);
assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60);
}
@@ -163,31 +156,6 @@
return intent -> TextUtils.equals(expected, intent.getAction());
}
- @Implements(value = Settings.Secure.class)
- public static class ShadowSecure extends ShadowSettings.ShadowSecure {
- private static Map<String, Integer> map = new HashMap<>();
-
- @Implementation
- public static boolean putIntForUser(ContentResolver cr, String name, int value,
- int userHandle) {
- map.put(name, value);
- return true;
- }
-
- @Implementation
- public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
- if (map.containsKey(name)) {
- return map.get(name);
- } else {
- return def;
- }
- }
-
- public static void reset() {
- map.clear();
- }
- }
-
@Implements(value = LocationManager.class)
public static class ShadowLocationManager {
@@ -337,9 +305,8 @@
@Test
public void getBatteryStatus_statusIsFull_returnFullString() {
- final Intent intent = new Intent()
- .putExtra(BatteryManager.EXTRA_LEVEL, 100)
- .putExtra(BatteryManager.EXTRA_SCALE, 100);
+ final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_LEVEL, 100).putExtra(
+ BatteryManager.EXTRA_SCALE, 100);
final Resources resources = mContext.getResources();
assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo(
@@ -348,9 +315,8 @@
@Test
public void getBatteryStatus_statusIsFullAndUseCompactStatus_returnFullyChargedString() {
- final Intent intent = new Intent()
- .putExtra(BatteryManager.EXTRA_LEVEL, 100)
- .putExtra(BatteryManager.EXTRA_SCALE, 100);
+ final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_LEVEL, 100).putExtra(
+ BatteryManager.EXTRA_SCALE, 100);
final Resources resources = mContext.getResources();
assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ true)).isEqualTo(
@@ -516,7 +482,6 @@
when(mUsbPort.getStatus()).thenReturn(mUsbPortStatus);
when(mUsbPort.supportsComplianceWarnings()).thenReturn(true);
when(mUsbPortStatus.isConnected()).thenReturn(true);
- when(mUsbPortStatus.getComplianceWarnings())
- .thenReturn(new int[]{complianceWarningType});
+ when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[]{complianceWarningType});
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
index 44fdaec..3de8446 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
@@ -23,13 +23,17 @@
import android.os.UserHandle;
import android.provider.Settings;
+import com.android.settingslib.testutils.shadow.ShadowSecure;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowSecure.class})
public class AccessibilityUtilsTest {
private Context mContext;
@@ -46,7 +50,7 @@
@Test
public void getEnabledServicesFromSettings_badFormat_emptyResult() {
- Settings.Secure.putStringForUser(
+ ShadowSecure.putStringForUser(
mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
":",
UserHandle.myUserId());
@@ -57,7 +61,7 @@
@Test
public void getEnabledServicesFromSettings_1Service_1result() {
final ComponentName cn = new ComponentName("pkg", "serv");
- Settings.Secure.putStringForUser(
+ ShadowSecure.putStringForUser(
mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
cn.flattenToString() + ":",
UserHandle.myUserId());
@@ -70,7 +74,7 @@
public void getEnabledServicesFromSettings_2Services_2results() {
final ComponentName cn1 = new ComponentName("pkg", "serv");
final ComponentName cn2 = new ComponentName("pkg", "serv2");
- Settings.Secure.putStringForUser(
+ ShadowSecure.putStringForUser(
mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
cn1.flattenToString() + ":" + cn2.flattenToString(),
UserHandle.myUserId());
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java
index cb62a73..f9505dd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java
@@ -37,6 +37,8 @@
import android.os.UserManager;
import android.util.LongSparseArray;
+import com.android.settingslib.testutils.shadow.ShadowPermissionChecker;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,7 +47,6 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowPermissionChecker;
import java.time.Clock;
import java.util.ArrayList;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index dd8d54a..a2e8c59 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
@@ -38,6 +38,7 @@
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.LooperMode;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
@@ -167,6 +168,7 @@
}
@Test
+ @LooperMode(LooperMode.Mode.PAUSED)
public void getAttribution_notSet_shouldReturnUnknown() {
final Activity activity = Robolectric.setupActivity(Activity.class);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
index d67d44b..25833b3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
@@ -36,6 +36,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.CujType;
+import com.android.settingslib.testutils.OverpoweredReflectionHelper;
import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
import org.junit.Before;
@@ -51,7 +52,6 @@
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
-import org.robolectric.util.ReflectionHelpers;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -83,8 +83,10 @@
public void setUp() {
ShadowInteractionJankMonitor.reset();
when(ShadowInteractionJankMonitor.MOCK_INSTANCE.begin(any())).thenReturn(true);
- ReflectionHelpers.setStaticField(SettingsJankMonitor.class, "scheduledExecutorService",
- mScheduledExecutorService);
+ OverpoweredReflectionHelper
+ .setStaticField(SettingsJankMonitor.class,
+ "scheduledExecutorService",
+ mScheduledExecutorService);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/HideNonSystemOverlayMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/HideNonSystemOverlayMixinTest.java
index cf702b53..471dac0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/HideNonSystemOverlayMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/HideNonSystemOverlayMixinTest.java
@@ -37,8 +37,10 @@
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
+import org.robolectric.annotation.LooperMode;
@RunWith(RobolectricTestRunner.class)
+@LooperMode(LooperMode.Mode.PAUSED)
public class HideNonSystemOverlayMixinTest {
private ActivityController<TestActivity> mActivityController;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
index 3475ff7..b009abd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
@@ -22,13 +22,14 @@
import android.os.UserManager;
import android.provider.Settings;
+import com.android.settingslib.testutils.shadow.ShadowUserManager;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowUserManager;
@RunWith(RobolectricTestRunner.class)
public class DevelopmentSettingsEnablerTest {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index 8e33ca3..0cabab2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -21,6 +21,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.LooperMode;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
@@ -269,6 +270,7 @@
}
@Test
+ @LooperMode(LooperMode.Mode.PAUSED)
public void testGenerateHtmlWithCustomHeading() throws Exception {
List<File> xmlFiles = new ArrayList<>();
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
@@ -292,6 +294,7 @@
}
@Test
+ @LooperMode(LooperMode.Mode.PAUSED)
public void testGenerateNewHtmlWithCustomHeading() throws Exception {
List<File> xmlFiles = new ArrayList<>();
Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/package-info.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/package-info.java
new file mode 100644
index 0000000..9e9725f
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+@LooperMode(LooperMode.Mode.LEGACY)
+package com.android.settingslib;
+
+import org.robolectric.annotation.LooperMode;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
index d41d511..faec02f7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
@@ -27,6 +27,7 @@
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.LooperMode;
@RunWith(RobolectricTestRunner.class)
public class AnimatedImageViewTest {
@@ -40,6 +41,7 @@
}
@Test
+ @LooperMode(LooperMode.Mode.PAUSED)
public void testAnimation_ViewVisible_AnimationRunning() {
mAnimatedImageView.setVisibility(View.VISIBLE);
mAnimatedImageView.setAnimating(true);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
index 0a48f19..0d88913 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
@@ -41,6 +41,8 @@
import androidx.preference.PreferenceViewHolder;
import androidx.preference.R;
+import com.android.settingslib.testutils.OverpoweredReflectionHelper;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -502,14 +504,18 @@
private void assumeAndroidR() {
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 30);
ReflectionHelpers.setStaticField(Build.VERSION.class, "CODENAME", "R");
- ReflectionHelpers.setStaticField(BannerMessagePreference.class, "IS_AT_LEAST_S", false);
+ OverpoweredReflectionHelper
+ .setStaticField(BannerMessagePreference.class, "IS_AT_LEAST_S", false);
// Reset view holder to use correct layout.
}
+
+
private void assumeAndroidS() {
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 31);
ReflectionHelpers.setStaticField(Build.VERSION.class, "CODENAME", "S");
- ReflectionHelpers.setStaticField(BannerMessagePreference.class, "IS_AT_LEAST_S", true);
+ OverpoweredReflectionHelper
+ .setStaticField(BannerMessagePreference.class, "IS_AT_LEAST_S", true);
// Re-inflate view to update layout.
setUpViewHolder();
}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/OverpoweredReflectionHelper.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/OverpoweredReflectionHelper.java
new file mode 100644
index 0000000..4fcc5a1
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/OverpoweredReflectionHelper.java
@@ -0,0 +1,99 @@
+/*
+ * 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.settingslib.testutils;
+
+import org.robolectric.util.ReflectionHelpers;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+public class OverpoweredReflectionHelper extends ReflectionHelpers {
+
+ /**
+ * Robolectric upstream does not rely on or encourage this behaviour.
+ *
+ * @param field
+ */
+ private static void makeFieldVeryAccessible(Field field) {
+ field.setAccessible(true);
+ // remove 'final' modifier if present
+ if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) {
+ Field modifiersField = getModifiersField();
+ modifiersField.setAccessible(true);
+ try {
+ modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
+ } catch (IllegalAccessException e) {
+
+ throw new AssertionError(e);
+ }
+ }
+ }
+
+ private static Field getModifiersField() {
+ try {
+ return Field.class.getDeclaredField("modifiers");
+ } catch (NoSuchFieldException e) {
+ try {
+ Method getFieldsMethod =
+ Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
+ getFieldsMethod.setAccessible(true);
+ Field[] fields = (Field[]) getFieldsMethod.invoke(Field.class, false);
+ for (Field modifiersField : fields) {
+ if ("modifiers".equals(modifiersField.getName())) {
+ return modifiersField;
+ }
+ }
+ } catch (ReflectiveOperationException innerE) {
+ throw new AssertionError(innerE);
+ }
+ }
+ throw new AssertionError();
+ }
+
+ /**
+ * Reflectively set the value of a static field.
+ *
+ * @param field Field object.
+ * @param fieldNewValue The new value.
+ */
+ public static void setStaticField(Field field, Object fieldNewValue) {
+ try {
+ makeFieldVeryAccessible(field);
+ field.setAccessible(true);
+ field.set(null, fieldNewValue);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Reflectively set the value of a static field.
+ *
+ * @param clazz Target class.
+ * @param fieldName The field name.
+ * @param fieldNewValue The new value.
+ */
+ public static void setStaticField(Class<?> clazz, String fieldName, Object fieldNewValue) {
+ try {
+ setStaticField(clazz.getDeclaredField(fieldName), fieldNewValue);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowActivityManager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
index 924eb04..0b9ba8d 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
@@ -16,23 +16,27 @@
package com.android.settingslib.testutils.shadow;
+import static android.os.Build.VERSION_CODES.O;
+
import android.app.ActivityManager;
+import android.app.IActivityManager;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import org.robolectric.shadow.api.Shadow;
+import org.robolectric.util.ReflectionHelpers;
@Implements(ActivityManager.class)
public class ShadowActivityManager {
private static int sCurrentUserId = 0;
- private int mUserSwitchedTo = -1;
+ private static int sUserSwitchedTo = -1;
@Resetter
- public void reset() {
+ public static void reset() {
sCurrentUserId = 0;
- mUserSwitchedTo = 0;
+ sUserSwitchedTo = 0;
}
@Implementation
@@ -42,16 +46,21 @@
@Implementation
protected boolean switchUser(int userId) {
- mUserSwitchedTo = userId;
+ sUserSwitchedTo = userId;
return true;
}
+ @Implementation(minSdk = O)
+ protected static IActivityManager getService() {
+ return ReflectionHelpers.createNullProxy(IActivityManager.class);
+ }
+
public boolean getSwitchUserCalled() {
- return mUserSwitchedTo != -1;
+ return sUserSwitchedTo != -1;
}
public int getUserSwitchedTo() {
- return mUserSwitchedTo;
+ return sUserSwitchedTo;
}
public static void setCurrentUser(int userId) {
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
index 2c0792f..bbfdb7f 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
@@ -29,7 +29,7 @@
private static String sDefaultDialer;
@Resetter
- public void reset() {
+ public static void reset() {
sDefaultDialer = null;
}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowPermissionChecker.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowPermissionChecker.java
new file mode 100644
index 0000000..fae3aea
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowPermissionChecker.java
@@ -0,0 +1,86 @@
+/*
+ * 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.settingslib.testutils.shadow;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.AttributionSource;
+import android.content.Context;
+import android.content.PermissionChecker;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.util.HashMap;
+import java.util.Map;
+/** Shadow class of {@link PermissionChecker}. */
+@Implements(PermissionChecker.class)
+public class ShadowPermissionChecker {
+ private static final Map<String, Map<String, Integer>> RESULTS = new HashMap<>();
+ /** Set the result of permission check for a specific permission. */
+ public static void setResult(String packageName, String permission, int result) {
+ if (!RESULTS.containsKey(packageName)) {
+ RESULTS.put(packageName, new HashMap<>());
+ }
+ RESULTS.get(packageName).put(permission, result);
+ }
+ /** Check the permission of calling package. */
+ @Implementation
+ public static int checkCallingPermissionForDataDelivery(
+ Context context,
+ String permission,
+ String packageName,
+ String attributionTag,
+ String message) {
+ return RESULTS.containsKey(packageName) && RESULTS.get(packageName).containsKey(permission)
+ ? RESULTS.get(packageName).get(permission)
+ : PermissionChecker.checkCallingPermissionForDataDelivery(
+ context, permission, packageName, attributionTag, message);
+ }
+ /** Check general permission. */
+ @Implementation
+ public static int checkPermissionForDataDelivery(
+ Context context,
+ String permission,
+ int pid,
+ int uid,
+ String packageName,
+ String attributionTag,
+ String message) {
+ return RESULTS.containsKey(packageName) && RESULTS.get(packageName).containsKey(permission)
+ ? RESULTS.get(packageName).get(permission)
+ : PermissionChecker.checkPermissionForDataDelivery(
+ context, permission, pid, uid, packageName, attributionTag, message);
+ }
+ /** Check general permission. */
+ @Implementation
+ public static int checkPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission, int pid, int uid, @Nullable String packageName) {
+ return checkPermissionForPreflight(context, permission, new AttributionSource(
+ uid, packageName, null /*attributionTag*/));
+ }
+ /** Check general permission. */
+ @Implementation
+ public static int checkPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission, @NonNull AttributionSource attributionSource) {
+ final String packageName = attributionSource.getPackageName();
+ return RESULTS.containsKey(packageName) && RESULTS.get(packageName).containsKey(permission)
+ ? RESULTS.get(packageName).get(permission)
+ : PermissionChecker.checkPermissionForPreflight(
+ context, permission, attributionSource);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowSecure.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowSecure.java
new file mode 100644
index 0000000..70ebc67
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowSecure.java
@@ -0,0 +1,35 @@
+/*
+ * 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.settingslib.testutils.shadow;
+
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowSettings;
+
+@Implements(value = Settings.Secure.class)
+public class ShadowSecure extends ShadowSettings.ShadowSecure {
+ @Implementation(minSdk = JELLY_BEAN_MR1)
+ public static boolean putStringForUser(ContentResolver cr, String name, String value,
+ int userHandle) {
+ return putString(cr, name, value);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java
index 381d072..5ac0a87 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowSmsApplication.java
@@ -31,7 +31,7 @@
private static ComponentName sDefaultSmsApplication;
@Resetter
- public void reset() {
+ public static void reset() {
sDefaultSmsApplication = null;
}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowUserManager.java
index ca1eefc..60d7721 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowUserManager.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -16,20 +16,28 @@
package com.android.settingslib.testutils.shadow;
+import static android.os.Build.VERSION_CODES.N_MR1;
+
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
+import android.os.UserHandle;
import android.os.UserManager;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowBuild;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
@Implements(value = UserManager.class)
public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
private List<UserInfo> mUserInfos = addProfile(0, "Owner");
+ private final Map<Integer, UserProperties> mUserPropertiesMap = new HashMap<>();
@Implementation
protected static UserManager get(Context context) {
@@ -62,4 +70,37 @@
protected List<UserInfo> getProfiles(@UserIdInt int userHandle) {
return getProfiles();
}
+
+ /**
+ * @return {@code false} by default, or the value specified via {@link #setIsAdminUser(boolean)}
+ */
+ @Implementation(minSdk = N_MR1)
+ public boolean isAdminUser() {
+ return getUserInfo(UserHandle.myUserId()).isAdmin();
+ }
+
+ /**
+ * Sets that the current user is an admin user; controls the return value of
+ * {@link UserManager#isAdminUser}.
+ */
+ public void setIsAdminUser(boolean isAdminUser) {
+ UserInfo userInfo = getUserInfo(UserHandle.myUserId());
+ if (isAdminUser) {
+ userInfo.flags |= UserInfo.FLAG_ADMIN;
+ } else {
+ userInfo.flags &= ~UserInfo.FLAG_ADMIN;
+ }
+ }
+
+ public void setupUserProperty(int userId, int showInSettings) {
+ UserProperties userProperties = new UserProperties(new UserProperties.Builder()
+ .setShowInSettings(showInSettings).build());
+ mUserPropertiesMap.putIfAbsent(userId, userProperties);
+ }
+
+ @Implementation(minSdk = ShadowBuild.UPSIDE_DOWN_CAKE)
+ protected UserProperties getUserProperties(UserHandle user) {
+ return mUserPropertiesMap.getOrDefault(user.getIdentifier(),
+ new UserProperties(new UserProperties.Builder().build()));
+ }
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index d1f7f2f..fa2d677 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -24,7 +24,7 @@
/**
* These keys may be mentioned in the SETTINGS_TO_BACKUP arrays in SystemSettings
* and SecureSettings as well. This is because those tables drive both backup and
- * restore, and restore needs to properly whitelist keys that used to live
+ * restore, and restore needs to properly allowlist keys that used to live
* in those namespaces.
*
* NOTE: Settings are backed up and restored in the order they appear
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 423c8a3..2d62e2a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -242,6 +242,7 @@
Settings.Secure.HEARING_AID_CALL_ROUTING,
Settings.Secure.HEARING_AID_MEDIA_ROUTING,
Settings.Secure.HEARING_AID_SYSTEM_SOUNDS_ROUTING,
- Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED
+ Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
+ Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 6c9da97..4494765 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -200,6 +200,7 @@
VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_TOUCH_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_LONG_PRESS_HOME_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.VR_DISPLAY_MODE, new DiscreteValueValidator(new String[] {"0", "1"}));
VALIDATORS.put(Secure.NOTIFICATION_BADGING, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NOTIFICATION_DISMISS_RTL, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index c830d6b..7b49608 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -927,7 +927,7 @@
@VisibleForTesting
SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
// Figure out the white list and redirects to the global table. We restore anything
- // in either the backup whitelist or the legacy-restore whitelist for this table.
+ // in either the backup allowlist or the legacy-restore allowlist for this table.
String[] whitelist;
Map<String, Validator> validators = null;
if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
@@ -1474,7 +1474,7 @@
}
/**
- * Store the whitelist of settings to be backed up and validators for them.
+ * Store the allowlist of settings to be backed up and validators for them.
*/
@VisibleForTesting
static class SettingsBackupWhitelist {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1192e00..d2b444b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1926,6 +1926,9 @@
dumpSetting(s, p,
Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED,
SecureSettingsProto.Assist.LONG_PRESS_HOME_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED,
+ SecureSettingsProto.Assist.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED);
p.end(assistToken);
final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7e97956..5f4f317 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -251,7 +251,7 @@
public static final int WRITE_FALLBACK_SETTINGS_FILES_JOB_ID = 1;
public static final long ONE_DAY_INTERVAL_MILLIS = 24 * 60 * 60 * 1000L;
- // Overlay specified settings whitelisted for Instant Apps
+ // Overlay specified settings allowlisted for Instant Apps
private static final Set<String> OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS = new ArraySet<>();
private static final Set<String> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS = new ArraySet<>();
private static final Set<String> OVERLAY_ALLOWED_SECURE_INSTANT_APP_SETTINGS = new ArraySet<>();
@@ -1157,8 +1157,6 @@
Slog.v(LOG_TAG, "getConfigSetting(" + name + ")");
}
- Settings.Config.enforceReadPermission(/*namespace=*/name.split("/")[0]);
-
// Get the value.
synchronized (mLock) {
return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_CONFIG,
@@ -1338,9 +1336,6 @@
Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix);
}
- Settings.Config.enforceReadPermission(
- prefix != null ? prefix.split("/")[0] : null);
-
synchronized (mLock) {
// Get the settings.
SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
@@ -2155,7 +2150,7 @@
@GuardedBy("mLock")
private List<String> getSettingsNamesLocked(int settingsType, int userId) {
- // Don't enforce the instant app whitelist for now -- its too prone to unintended breakage
+ // Don't enforce the instant app allowlist for now -- its too prone to unintended breakage
// in the current form.
return mSettingsRegistry.getSettingsNamesLocked(settingsType, userId);
}
@@ -2196,7 +2191,7 @@
}
if (!getInstantAppAccessibleSettings(settingsType).contains(settingName)
&& !getOverlayInstantAppAccessibleSettings(settingsType).contains(settingName)) {
- // Don't enforce the instant app whitelist for now -- its too prone to unintended
+ // Don't enforce the instant app allowlist for now -- its too prone to unintended
// breakage in the current form.
Slog.w(LOG_TAG, "Instant App " + ai.packageName
+ " trying to access unexposed setting, this will be an error in the future.");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 73ead3c..5552745 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -1110,7 +1110,10 @@
} catch (FileNotFoundException fnfe) {
final String message = "No fallback file found for: " + mStatePersistFile;
Slog.wtf(LOG_TAG, message);
- throw new IllegalStateException(message);
+ if (!isConfigSettingsKey(mKey)) {
+ // Allow partially deserialized config settings because they can be updated later
+ throw new IllegalStateException(message);
+ }
}
if (parseStateFromXmlStreamLocked(in)) {
// Parsed state from fallback file. Restore original file with fallback file
@@ -1122,7 +1125,10 @@
} else {
final String message = "Failed parsing settings file: " + mStatePersistFile;
Slog.wtf(LOG_TAG, message);
- throw new IllegalStateException(message);
+ if (!isConfigSettingsKey(mKey)) {
+ // Allow partially deserialized config settings because they can be updated later
+ throw new IllegalStateException(message);
+ }
}
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index ef062df..4b10b56 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -23,14 +23,16 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.ContentProvider;
-import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
@@ -44,8 +46,8 @@
import android.telephony.TelephonyManager;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
+import android.util.ArrayMap;
-import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
@@ -77,13 +79,14 @@
@Mock private Context mContext;
@Mock private Resources mResources;
- @Mock private ContentResolver mContentResolver;
@Mock private AudioManager mAudioManager;
@Mock private TelephonyManager mTelephonyManager;
+ @Mock private MockContentResolver mContentResolver;
+ private MockSettingsProvider mSettingsProvider;
+
@Before
public void setUp() {
- clearLongPressPowerValues();
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(eq(Context.AUDIO_SERVICE))).thenReturn(mAudioManager);
when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE))).thenReturn(
@@ -91,14 +94,20 @@
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getApplicationContext()).thenReturn(mContext);
when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
- when(mContext.getContentResolver()).thenReturn(getContentResolver());
mSettingsHelper = spy(new SettingsHelper(mContext));
+ mContentResolver = spy(new MockContentResolver());
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mSettingsProvider = new MockSettingsProvider(mContext);
+ mContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
}
@After
public void tearDown() {
- clearLongPressPowerValues();
+ Settings.Global.putString(mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS,
+ null);
+ Settings.Global.putString(mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
+ null);
}
@Test
@@ -123,33 +132,30 @@
mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
SETTING_KEY, SETTING_VALUE, /* restoredFromSdkInt */ 0);
- verifyZeroInteractions(mContentResolver);
+ // The only time of interaction happened during setUp()
+ verify(mContentResolver, times(1))
+ .addProvider(Settings.AUTHORITY, mSettingsProvider);
+
+ verifyNoMoreInteractions(mContentResolver);
}
@Test
public void testRestoreValue_lppForAssistantEnabled_updatesValue() {
- ContentResolver cr =
- InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
when(mResources.getBoolean(
R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
true);
- mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
Settings.Global.POWER_BUTTON_LONG_PRESS, "5", 0);
- assertThat(
- Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))
- .isEqualTo(5);
- assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
- -1)).isEqualTo(2);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(5);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(2);
}
@Test
public void testRestoreValue_lppForAssistantNotEnabled_updatesValueToDefaultConfig() {
- ContentResolver cr =
- InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
when(mResources.getBoolean(
R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
true);
@@ -161,21 +167,17 @@
R.integer.config_keyChordPowerVolumeUp)).thenReturn(
1);
- mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
Settings.Global.POWER_BUTTON_LONG_PRESS, "2", 0);
- assertThat(
- Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))
- .isEqualTo(1);
- assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
- -1)).isEqualTo(1);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(1);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(1);
}
@Test
public void testRestoreValue_lppForAssistantNotEnabledDefaultConfig_updatesValue() {
- ContentResolver cr =
- InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
when(mResources.getBoolean(
R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
true);
@@ -187,47 +189,39 @@
R.integer.config_keyChordPowerVolumeUp)).thenReturn(
1);
- mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
Settings.Global.POWER_BUTTON_LONG_PRESS, "2", 0);
- assertThat(
- Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))
- .isEqualTo(1);
- assertThat(Settings.Global.getInt(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
- -1)).isEqualTo(1);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(1);
+ assertThat(Settings.Global.getInt(
+ mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(1);
}
@Test
public void testRestoreValue_lppForAssistantNotAvailable_doesNotRestore() {
- ContentResolver cr =
- InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
- when(mResources.getBoolean(
- R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
- false);
+ when(mResources.getBoolean(R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(false);
- mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
- Settings.Global.POWER_BUTTON_LONG_PRESS, "5", 0);
+ mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
+ Settings.Global.POWER_BUTTON_LONG_PRESS, "500", 0);
- assertThat((Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS,
- -1))).isEqualTo(-1);
+ assertThat((Settings.Global.getInt(
+ mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))).isEqualTo(-1);
}
@Test
public void testRestoreValue_lppForAssistantInvalid_doesNotRestore() {
- ContentResolver cr =
- InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
when(mResources.getBoolean(
R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
false);
- mSettingsHelper.restoreValue(mContext, cr, new ContentValues(), Uri.EMPTY,
+ mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
Settings.Global.POWER_BUTTON_LONG_PRESS, "trees", 0);
- assertThat((Settings.Global.getInt(cr, Settings.Global.POWER_BUTTON_LONG_PRESS,
- -1))).isEqualTo(-1);
+ assertThat((Settings.Global.getInt(
+ mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))).isEqualTo(-1);
}
@Test
@@ -363,9 +357,6 @@
final String newRingtoneValueCanonicalized =
"content://media/internal/audio/media/100?title=Song&canonical=1";
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
ContentProvider mockMediaContentProvider =
new MockContentProvider(mContext) {
@Override
@@ -386,25 +377,22 @@
}
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE))
+ resetRingtoneSettingsToDefault();
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(DEFAULT_RINGTONE_VALUE);
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.RINGTONE,
sourceRingtoneValue,
0);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(newRingtoneValueCanonicalized);
}
@@ -417,9 +405,6 @@
final String newRingtoneValueCanonicalized =
"content://0@media/external/audio/media/100?title=Song&canonical=1";
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
cursor.addRow(new Object[] {100L});
@@ -458,24 +443,21 @@
}
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
+ mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
+ resetRingtoneSettingsToDefault();
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.RINGTONE,
sourceRingtoneValue,
0);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(newRingtoneValueCanonicalized);
}
@@ -488,9 +470,6 @@
final String newRingtoneValueCanonicalized =
"content://0@media/external/audio/media/200?title=notificationPing&canonicalize=1";
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
cursor.addRow(new Object[] {200L});
@@ -529,17 +508,14 @@
}
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
+ mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
+ resetRingtoneSettingsToDefault();
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.NOTIFICATION_SOUND,
@@ -548,7 +524,7 @@
assertThat(
Settings.System.getString(
- mMockContentResolver, Settings.System.NOTIFICATION_SOUND))
+ mContentResolver, Settings.System.NOTIFICATION_SOUND))
.isEqualTo(newRingtoneValueCanonicalized);
}
@@ -561,9 +537,6 @@
final String newRingtoneValueCanonicalized =
"content://0@media/external/audio/media/300?title=alarmSound&canonical=1";
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
cursor.addRow(new Object[] {300L});
@@ -600,26 +573,29 @@
assertThat(selectionArgs).isEqualTo(new String[] {"alarmSound"});
return cursor;
}
+
+ @Override
+ public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType,
+ Bundle opts) {
+ return null;
+ }
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
+ mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
+ resetRingtoneSettingsToDefault();
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.ALARM_ALERT,
sourceRingtoneValue,
0);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.ALARM_ALERT))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.ALARM_ALERT))
.isEqualTo(newRingtoneValueCanonicalized);
}
@@ -628,9 +604,6 @@
final String sourceRingtoneValue =
"content://0@media/external/audio/media/1?title=Song&canonical=1";
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
// This is to mock the case that there are multiple results by querying title +
// ringtone_type.
MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
@@ -651,32 +624,26 @@
}
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
+ mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
+ resetRingtoneSettingsToDefault();
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.RINGTONE,
sourceRingtoneValue,
0);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(DEFAULT_RINGTONE_VALUE);
}
@Test
public void testRestoreValue_customRingtone_restoreSilentValue() {
- MockContentResolver mMockContentResolver = new MockContentResolver();
- when(mContext.getContentResolver()).thenReturn(mMockContentResolver);
-
ContentProvider mockMediaContentProvider =
new MockContentProvider(mContext) {
@Override
@@ -691,37 +658,46 @@
}
};
- ContentProvider mockSettingsContentProvider =
- new MockSettingsProvider(mContext, getContentResolver());
- mMockContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- mMockContentResolver.addProvider(Settings.AUTHORITY, mockSettingsContentProvider);
+ mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
- resetRingtoneSettingsToDefault(mMockContentResolver);
+ resetRingtoneSettingsToDefault();
mSettingsHelper.restoreValue(
mContext,
- mMockContentResolver,
+ mContentResolver,
new ContentValues(),
Uri.EMPTY,
Settings.System.RINGTONE,
"_silent",
0);
- assertThat(Settings.System.getString(mMockContentResolver, Settings.System.RINGTONE))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(null);
}
- public static class MockSettingsProvider extends MockContentProvider {
- ContentResolver mBaseContentResolver;
-
- public MockSettingsProvider(Context context, ContentResolver baseContentResolver) {
+ private static class MockSettingsProvider extends MockContentProvider {
+ private final ArrayMap<String, String> mKeyValueStore = new ArrayMap<>();
+ MockSettingsProvider(Context context) {
super(context);
- this.mBaseContentResolver = baseContentResolver;
}
@Override
public Bundle call(String method, String request, Bundle args) {
- return mBaseContentResolver.call(Settings.AUTHORITY, method, request, args);
+ if (method.startsWith("PUT_")) {
+ mKeyValueStore.put(request, args.getString("value"));
+ return null;
+ } else if (method.startsWith("GET_")) {
+ return Bundle.forPair("value", mKeyValueStore.getOrDefault(request, ""));
+ }
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ String name = values.getAsString("name");
+ String value = values.getAsString("value");
+ mKeyValueStore.put(name, value);
+ return null;
}
}
@@ -752,15 +728,13 @@
}
private int getAutoRotationSettingValue() {
- return Settings.System.getInt(
- getContentResolver(),
+ return Settings.System.getInt(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION,
/* default= */ -1);
}
private void setAutoRotationSettingValue(int value) {
- Settings.System.putInt(
- getContentResolver(),
+ Settings.System.putInt(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION,
value
);
@@ -769,7 +743,7 @@
private void restoreAutoRotationSetting(int newValue) {
mSettingsHelper.restoreValue(
mContext,
- getContentResolver(),
+ mContentResolver,
new ContentValues(),
/* destination= */ Settings.System.CONTENT_URI,
/* name= */ Settings.System.ACCELEROMETER_ROTATION,
@@ -777,31 +751,19 @@
/* restoredFromSdkInt= */ 0);
}
- private ContentResolver getContentResolver() {
- return InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
- }
-
- private void clearLongPressPowerValues() {
- ContentResolver cr = InstrumentationRegistry.getInstrumentation().getTargetContext()
- .getContentResolver();
- Settings.Global.putString(cr, Settings.Global.POWER_BUTTON_LONG_PRESS, null);
- Settings.Global.putString(cr, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, null);
- }
-
- private void resetRingtoneSettingsToDefault(ContentResolver contentResolver) {
+ private void resetRingtoneSettingsToDefault() {
Settings.System.putString(
- contentResolver, Settings.System.RINGTONE, DEFAULT_RINGTONE_VALUE);
+ mContentResolver, Settings.System.RINGTONE, DEFAULT_RINGTONE_VALUE);
Settings.System.putString(
- contentResolver, Settings.System.NOTIFICATION_SOUND, DEFAULT_NOTIFICATION_VALUE);
+ mContentResolver, Settings.System.NOTIFICATION_SOUND, DEFAULT_NOTIFICATION_VALUE);
Settings.System.putString(
- contentResolver, Settings.System.ALARM_ALERT, DEFAULT_ALARM_VALUE);
+ mContentResolver, Settings.System.ALARM_ALERT, DEFAULT_ALARM_VALUE);
- assertThat(Settings.System.getString(contentResolver, Settings.System.RINGTONE))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
.isEqualTo(DEFAULT_RINGTONE_VALUE);
- assertThat(Settings.System.getString(contentResolver, Settings.System.NOTIFICATION_SOUND))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.NOTIFICATION_SOUND))
.isEqualTo(DEFAULT_NOTIFICATION_VALUE);
- assertThat(Settings.System.getString(contentResolver, Settings.System.ALARM_ALERT))
+ assertThat(Settings.System.getString(mContentResolver, Settings.System.ALARM_ALERT))
.isEqualTo(DEFAULT_ALARM_VALUE);
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 798bdec4..ab0225d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -409,6 +409,18 @@
scope.launch(bgDispatcher) { mutateSetting { it.copy(seedColor = value) } }
}
+ // Returns currentClockId if clock is connected, otherwise DEFAULT_CLOCK_ID. Since this
+ // is dependent on which clocks are connected, it may change when a clock is installed or
+ // removed from the device (unlike currentClockId).
+ // TODO: Merge w/ CurrentClockId when we convert to a flow. We shouldn't need both behaviors.
+ val activeClockId: String
+ get() {
+ if (!availableClocks.containsKey(currentClockId)) {
+ return DEFAULT_CLOCK_ID
+ }
+ return currentClockId
+ }
+
init {
// Register default clock designs
for (clock in defaultClockProvider.getClocks()) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index e539c95..b28920c 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -62,9 +62,10 @@
private val burmeseLineSpacing =
resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
+ protected var onSecondaryDisplay: Boolean = false
override val events: DefaultClockEvents
- override val config = ClockConfig()
+ override val config = ClockConfig(DEFAULT_CLOCK_ID)
init {
val parent = FrameLayout(ctx)
@@ -142,6 +143,11 @@
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSizePx)
recomputePadding(targetRegion)
}
+
+ override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {
+ this@DefaultClockController.onSecondaryDisplay = onSecondaryDisplay
+ recomputePadding(null)
+ }
}
open fun recomputePadding(targetRegion: Rect?) {}
@@ -182,13 +188,19 @@
override fun recomputePadding(targetRegion: Rect?) {
// We center the view within the targetRegion instead of within the parent
// view by computing the difference and adding that to the padding.
- val parent = view.parent
- val yDiff =
- if (targetRegion != null && parent is View && parent.isLaidOut())
- targetRegion.centerY() - parent.height / 2f
- else 0f
val lp = view.getLayoutParams() as FrameLayout.LayoutParams
- lp.topMargin = (-0.5f * view.bottom + yDiff).toInt()
+ lp.topMargin =
+ if (onSecondaryDisplay) {
+ // On the secondary display we don't want any additional top/bottom margin.
+ 0
+ } else {
+ val parent = view.parent
+ val yDiff =
+ if (targetRegion != null && parent is View && parent.isLaidOut())
+ targetRegion.centerY() - parent.height / 2f
+ else 0f
+ (-0.5f * view.bottom + yDiff).toInt()
+ }
view.setLayoutParams(lp)
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index d962732..e2f4793 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -177,6 +177,9 @@
* targetRegion is relative to the parent view.
*/
fun onTargetRegionChanged(targetRegion: Rect?)
+
+ /** Called to notify the clock about its display. */
+ fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean)
}
/** Tick rates for clocks */
@@ -196,6 +199,8 @@
/** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
data class ClockConfig(
+ val id: String,
+
/** Transition to AOD should move smartspace like large clock instead of small clock */
val useAlternateSmartspaceAODTransition: Boolean = false,
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml
new file mode 100644
index 0000000..593f507f
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+**
+** Copyright 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.
+*/
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/presentation"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <com.android.keyguard.KeyguardStatusView
+ android:id="@+id/clock"
+ android:layout_width="410dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical">
+
+ <include
+ android:id="@+id/keyguard_clock_container"
+ layout="@layout/keyguard_clock_switch"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </com.android.keyguard.KeyguardStatusView>
+
+</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml
new file mode 100644
index 0000000..952f056
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/carrier_combo"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="horizontal" >
+
+ <com.android.systemui.util.AutoMarqueeTextView
+ android:id="@+id/mobile_carrier_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginEnd="@dimen/qs_carrier_margin_width"
+ android:visibility="gone"
+ android:textDirection="locale"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:singleLine="true"
+ android:maxEms="7"/>
+
+ <include layout="@layout/status_bar_mobile_signal_group_new" />
+
+</com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView>
+
diff --git a/packages/SystemUI/res/drawable/auth_credential_emergency_button_background.xml b/packages/SystemUI/res/drawable/auth_credential_emergency_button_background.xml
new file mode 100644
index 0000000..85450b4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/auth_credential_emergency_button_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ <shape android:shape="rectangle">
+ <corners android:radius="25dp"/>
+ <solid android:color="@android:color/system_accent3_100" />
+ </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml b/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml
index 4a9d41f..b83f15a 100644
--- a/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml
@@ -14,6 +14,4 @@
limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetLeft="3dp"
- android:insetRight="3dp"
android:drawable="@drawable/ic_speaker_mute" />
diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
index e2ce34f..e439f77 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
@@ -61,29 +61,46 @@
</RelativeLayout>
- <LinearLayout
+ <FrameLayout
android:id="@+id/auth_credential_input"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- style="?passwordTextAppearance"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- android:inputType="textPassword"
- android:minHeight="48dp" />
-
- <TextView
- android:id="@+id/error"
- style="?errorTextAppearance"
- android:layout_gravity="center"
+ <LinearLayout
android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|top"
+ android:orientation="vertical">
- </LinearLayout>
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ style="?passwordTextAppearance"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ android:inputType="textPassword"
+ android:minHeight="48dp"/>
+
+ <TextView
+ android:id="@+id/error"
+ style="?errorTextAppearance"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/emergencyCallButton"
+ style="@style/AuthCredentialEmergencyButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:text="@string/work_challenge_emergency_button_text"/>
+ </FrameLayout>
</com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
index 88f138f..d5af377 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
@@ -60,27 +60,44 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/error"
+ style="?errorTextAppearanceLand"
+ android:layout_below="@id/description"
+ android:layout_alignParentLeft="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
</RelativeLayout>
- <FrameLayout
+ <RelativeLayout
android:layout_weight="1"
- style="?containerStyle"
android:layout_width="0dp"
android:layout_height="match_parent">
- <com.android.internal.widget.LockPatternView
- android:id="@+id/lockPattern"
- android:layout_gravity="center"
- android:layout_width="@dimen/biometric_auth_pattern_view_size"
- android:layout_height="@dimen/biometric_auth_pattern_view_size"/>
-
- <TextView
- android:id="@+id/error"
- style="?errorTextAppearance"
+ <FrameLayout
+ style="?containerStyle"
+ android:layout_above="@id/emergencyCallButton"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|bottom"/>
+ android:layout_height="match_parent">
- </FrameLayout>
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPattern"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/biometric_auth_pattern_view_size"
+ android:layout_height="@dimen/biometric_auth_pattern_view_size"/>
+ </FrameLayout>
+
+ <Button
+ android:id="@+id/emergencyCallButton"
+ style="@style/AuthCredentialEmergencyButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="35dp"
+ android:visibility="gone"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:text="@string/work_challenge_emergency_button_text"/>
+ </RelativeLayout>
</com.android.systemui.biometrics.ui.CredentialPatternView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 33f1b10..9336845 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -65,29 +65,46 @@
</ScrollView>
- <LinearLayout
+ <FrameLayout
android:id="@+id/auth_credential_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- style="?passwordTextAppearance"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- android:inputType="textPassword"
- android:minHeight="48dp" />
-
- <TextView
- android:id="@+id/error"
- style="?errorTextAppearance"
- android:layout_gravity="center_horizontal"
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|top"
+ android:orientation="vertical">
- </LinearLayout>
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ style="?passwordTextAppearance"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ android:inputType="textPassword"
+ android:minHeight="48dp"/>
+
+ <TextView
+ android:id="@+id/error"
+ style="?errorTextAppearance"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/emergencyCallButton"
+ style="@style/AuthCredentialEmergencyButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:text="@string/work_challenge_emergency_button_text"/>
+ </FrameLayout>
</com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
index 81ca3718..59828fd 100644
--- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -58,24 +58,42 @@
android:layout_height="wrap_content"/>
</RelativeLayout>
- <FrameLayout
+ <RelativeLayout
android:id="@+id/auth_credential_container"
- style="?containerStyle"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.internal.widget.LockPatternView
- android:id="@+id/lockPattern"
- android:layout_gravity="center"
- android:layout_width="@dimen/biometric_auth_pattern_view_size"
- android:layout_height="@dimen/biometric_auth_pattern_view_size"/>
+ <FrameLayout
+ android:layout_centerInParent="true"
+ android:layout_above="@id/emergencyCallButton"
+ style="?containerStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
- <TextView
- android:id="@+id/error"
- style="?errorTextAppearance"
- android:layout_width="match_parent"
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPattern"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/biometric_auth_pattern_view_size"
+ android:layout_height="@dimen/biometric_auth_pattern_view_size"/>
+
+ <TextView
+ android:id="@+id/error"
+ style="?errorTextAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"/>
+ </FrameLayout>
+
+ <Button
+ android:id="@+id/emergencyCallButton"
+ style="@style/AuthCredentialEmergencyButtonStyle"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|bottom"/>
- </FrameLayout>
+ android:layout_alignParentBottom="true"
+ android:visibility="gone"
+ android:layout_marginBottom="35dp"
+ android:layout_centerHorizontal="true"
+ android:text="@string/work_challenge_emergency_button_text"/>
+ </RelativeLayout>
</com.android.systemui.biometrics.ui.CredentialPatternView>
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index 12f13e9..3a15ae4 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -127,6 +127,8 @@
android:gravity="center_vertical"
android:paddingStart="@dimen/shade_header_system_icons_padding_start"
android:paddingEnd="@dimen/shade_header_system_icons_padding_end"
+ android:paddingTop="@dimen/shade_header_system_icons_padding_top"
+ android:paddingBottom="@dimen/shade_header_system_icons_padding_bottom"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/privacy_container"
app:layout_constraintTop_toTopOf="@id/clock">
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index f14be41..ec006c5 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -416,11 +416,22 @@
android:focusable="true"/>
</LinearLayout>
- <LinearLayout
+ <RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_gravity="end|center_vertical">
+ android:gravity="center_vertical">
+ <Button
+ android:id="@+id/share_wifi_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/share_wifi_button_text"
+ style="?android:attr/buttonBarNeutralButtonStyle"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:clickable="true"
+ android:focusable="true"
+ android:layout_alignParentLeft="true"
+ android:visibility="gone"/>
<Button
android:id="@+id/done_button"
android:layout_width="wrap_content"
@@ -430,8 +441,9 @@
android:maxLines="1"
android:ellipsize="end"
android:clickable="true"
- android:focusable="true"/>
- </LinearLayout>
+ android:focusable="true"
+ android:layout_alignParentRight="true"/>
+ </RelativeLayout>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index 9af46c5..3796415 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -64,8 +64,7 @@
android:layout_height="wrap_content"
android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
style="@style/TextAppearance.Dialog.Body.Message"
- android:gravity="start"
- android:lineHeight="@dimen/screenrecord_warning_line_height"/>
+ android:gravity="start"/>
<!-- Buttons -->
<LinearLayout
diff --git a/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
index 78cd718..39ec09b 100644
--- a/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
+++ b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
@@ -34,8 +34,8 @@
android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
android:contentDescription="@string/screenshot_dismiss_work_profile">
<ImageView
- android:layout_width="16dp"
- android:layout_height="16dp"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
android:layout_gravity="center"
android:background="@drawable/circular_background"
android:backgroundTint="?androidprv:attr/materialColorSurfaceContainerHigh"
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index d85e012..915dcdb 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -82,6 +82,8 @@
<!-- start padding is smaller to account for status icon margins coming from drawable itself -->
<dimen name="shade_header_system_icons_padding_start">3dp</dimen>
<dimen name="shade_header_system_icons_padding_end">4dp</dimen>
+ <dimen name="shade_header_system_icons_padding_top">2dp</dimen>
+ <dimen name="shade_header_system_icons_padding_bottom">2dp</dimen>
<!-- Lockscreen shade transition values -->
<dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index d693631..8bc3eed 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -221,6 +221,7 @@
<attr name="descriptionTextAppearance" format="reference" />
<attr name="passwordTextAppearance" format="reference" />
<attr name="errorTextAppearance" format="reference"/>
+ <attr name="errorTextAppearanceLand" format="reference"/>
</declare-styleable>
<declare-styleable name="LogAccessPermissionGrantDialog">
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8310b95..2dc1b45 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -496,9 +496,10 @@
<dimen name="large_screen_shade_header_min_height">@dimen/qs_header_row_min_height</dimen>
<dimen name="large_screen_shade_header_left_padding">@dimen/qs_horizontal_margin</dimen>
<dimen name="shade_header_system_icons_height">@dimen/large_screen_shade_header_min_height</dimen>
- <dimen name="shade_header_system_icons_height_large_screen">32dp</dimen>
<dimen name="shade_header_system_icons_padding_start">0dp</dimen>
<dimen name="shade_header_system_icons_padding_end">0dp</dimen>
+ <dimen name="shade_header_system_icons_padding_top">0dp</dimen>
+ <dimen name="shade_header_system_icons_padding_bottom">0dp</dimen>
<!-- The top margin of the panel that holds the list of notifications.
On phones it's always 0dp but it's overridden in Car UI
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cddfda2..77ffe19 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -156,6 +156,9 @@
<!-- Button label for declining language change [CHAR LIMIT=25] -->
<string name="hdmi_cec_set_menu_language_decline">Keep current language</string>
+ <!-- Button label to share wifi [CHAR_LIMIT=20] -->
+ <string name="share_wifi_button_text">Share Wi\u2011Fi</string>
+
<!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] -->
<string name="wifi_debugging_title">Allow wireless debugging on this network?</string>
@@ -386,6 +389,8 @@
<string name="biometric_dialog_wrong_password">Wrong password</string>
<!-- Error string shown when the user enters too many incorrect attempts [CHAR LIMIT=120]-->
<string name="biometric_dialog_credential_too_many_attempts">Too many incorrect attempts.\nTry again in <xliff:g id="number">%d</xliff:g> seconds.</string>
+ <!-- Button text shown on an authentication screen giving the user the option to make an emergency call without unlocking their device [CHAR LIMIT=20] -->
+ <string name="work_challenge_emergency_button_text">Emergency</string>
<!-- Error string shown when the user enters an incorrect PIN/pattern/password and it counts towards the max attempts before the data on the device is wiped. [CHAR LIMIT=NONE]-->
<string name="biometric_dialog_credential_attempts_before_wipe">Try again. Attempt <xliff:g id="attempts" example="1">%1$d</xliff:g> of <xliff:g id="max_attempts" example="3">%2$d</xliff:g>.</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 10340c6..6991b96 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -236,6 +236,13 @@
<item name="android:gravity">center</item>
</style>
+ <style name="TextAppearance.AuthNonBioCredential.ErrorLand">
+ <item name="android:layout_marginTop">20dp</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">?android:attr/colorError</item>
+ <item name="android:gravity">start</item>
+ </style>
+
<style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault">
<item name="android:gravity">center</item>
<item name="android:paddingTop">28dp</item>
@@ -276,6 +283,17 @@
<item name="android:minWidth">200dp</item>
</style>
+ <style name="AuthCredentialEmergencyButtonStyle">
+ <item name="android:background">@drawable/auth_credential_emergency_button_background</item>
+ <item name="android:textColor">@android:color/system_accent3_900</item>
+ <item name="android:outlineProvider">none</item>
+ <item name="android:paddingTop">15dp</item>
+ <item name="android:paddingBottom">15dp</item>
+ <item name="android:paddingLeft">30dp</item>
+ <item name="android:paddingRight">30dp</item>
+ <item name="android:textSize">16sp</item>
+ </style>
+
<style name="DeviceManagementDialogTitle">
<item name="android:gravity">center</item>
<item name="android:textAppearance">@style/TextAppearance.Dialog.Title</item>
@@ -353,6 +371,7 @@
<item name="descriptionTextAppearance">@style/TextAppearance.AuthNonBioCredential.Description</item>
<item name="passwordTextAppearance">@style/TextAppearance.AuthCredential.PasswordEntry</item>
<item name="errorTextAppearance">@style/TextAppearance.AuthNonBioCredential.Error</item>
+ <item name="errorTextAppearanceLand">@style/TextAppearance.AuthNonBioCredential.ErrorLand</item>
</style>
<style name="LockPatternViewStyle" >
diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml
index 2ec6180..fe61c46 100644
--- a/packages/SystemUI/res/xml/large_screen_shade_header.xml
+++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml
@@ -56,7 +56,7 @@
<Constraint android:id="@+id/shade_header_system_icons">
<Layout
android:layout_width="wrap_content"
- android:layout_height="@dimen/shade_header_system_icons_height_large_screen"
+ android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/privacy_container"
app:layout_constraintTop_toTopOf="parent"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 45a5ce3..80040a3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -200,11 +200,12 @@
@Override
public void onAnimationStart(IRecentsAnimationController controller,
RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
- Rect homeContentInsets, Rect minimizedHomeBounds) {
+ Rect homeContentInsets, Rect minimizedHomeBounds,
+ Bundle extras) {
final RecentsAnimationControllerCompat controllerCompat =
new RecentsAnimationControllerCompat(controller);
animationHandler.onAnimationStart(controllerCompat, apps,
- wallpapers, homeContentInsets, minimizedHomeBounds);
+ wallpapers, homeContentInsets, minimizedHomeBounds, extras);
}
@Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index 8bddf21..2407350 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -17,6 +17,7 @@
package com.android.systemui.shared.system;
import android.graphics.Rect;
+import android.os.Bundle;
import android.view.RemoteAnimationTarget;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -29,7 +30,7 @@
*/
void onAnimationStart(RecentsAnimationControllerCompat controller,
RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
- Rect homeContentInsets, Rect minimizedHomeBounds);
+ Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras);
/**
* Called when the animation into Recents was canceled. This call is made on the binder thread.
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
index f005bab..fae9fec 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
@@ -33,8 +33,7 @@
SettingsUtilModule::class,
])
abstract class FlagsModule {
- @Binds
- abstract fun bindsFeatureFlagDebug(impl: FeatureFlagsDebug): FeatureFlags
+ @Binds abstract fun bindsFeatureFlagDebug(impl: FeatureFlagsClassicDebug): FeatureFlagsClassic
@Binds
@IntoSet
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
index 927d4604b..7aacb4e 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
@@ -29,7 +29,7 @@
])
abstract class FlagsModule {
@Binds
- abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags
+ abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsClassicRelease): FeatureFlagsClassic
@Binds
@IntoSet
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
new file mode 100644
index 0000000..899cad89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.keyguard
+
+import android.app.Presentation
+import android.content.Context
+import android.graphics.Color
+import android.os.Bundle
+import android.view.Display
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import com.android.keyguard.dagger.KeyguardStatusViewComponent
+import com.android.systemui.R
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** [Presentation] shown in connected displays while on keyguard. */
+class ConnectedDisplayKeyguardPresentation
+@AssistedInject
+constructor(
+ @Assisted display: Display,
+ context: Context,
+ private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
+) :
+ Presentation(
+ context,
+ display,
+ R.style.Theme_SystemUI_KeyguardPresentation,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
+ ) {
+
+ private lateinit var keyguardStatusViewController: KeyguardStatusViewController
+ private lateinit var clock: KeyguardStatusView
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(
+ LayoutInflater.from(context)
+ .inflate(R.layout.keyguard_clock_presentation, /* root= */ null)
+ )
+ val window = window ?: error("no window available.")
+
+ // Logic to make the lock screen fullscreen
+ window.decorView.systemUiVisibility =
+ (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
+ window.attributes.fitInsetsTypes = 0
+ window.isNavigationBarContrastEnforced = false
+ window.navigationBarColor = Color.TRANSPARENT
+
+ clock = findViewById(R.id.clock)
+ keyguardStatusViewController =
+ keyguardStatusViewComponentFactory.build(clock).keyguardStatusViewController.apply {
+ setDisplayedOnSecondaryDisplay()
+ init()
+ }
+ }
+
+ /** [ConnectedDisplayKeyguardPresentation] factory. */
+ @AssistedFactory
+ interface Factory {
+ /** Creates a new [Presentation] for the given [display]. */
+ fun create(
+ display: Display,
+ ): ConnectedDisplayKeyguardPresentation
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 30b8ed0..06b6692 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,10 +1,5 @@
package com.android.keyguard;
-import static android.view.View.ALPHA;
-import static android.view.View.SCALE_X;
-import static android.view.View.SCALE_Y;
-import static android.view.View.TRANSLATION_Y;
-
import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_X_CLOCK_DESIGN;
import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_DESIGN;
import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_SIZE;
@@ -44,6 +39,7 @@
public class KeyguardClockSwitch extends RelativeLayout {
private static final String TAG = "KeyguardClockSwitch";
+ public static final String MISSING_CLOCK_ID = "CLOCK_MISSING";
private static final long CLOCK_OUT_MILLIS = 133;
private static final long CLOCK_IN_MILLIS = 167;
@@ -149,6 +145,13 @@
updateStatusArea(/* animate= */false);
}
+ /** Sets whether the large clock is being shown on a connected display. */
+ public void setLargeClockOnSecondaryDisplay(boolean onSecondaryDisplay) {
+ if (mClock != null) {
+ mClock.getLargeClock().getEvents().onSecondaryDisplayChanged(onSecondaryDisplay);
+ }
+ }
+
/**
* Enable or disable split shade specific positioning
*/
@@ -194,6 +197,14 @@
return mLogBuffer;
}
+ /** Returns the id of the currently rendering clock */
+ public String getClockId() {
+ if (mClock == null) {
+ return MISSING_CLOCK_ID;
+ }
+ return mClock.getConfig().getId();
+ }
+
void setClock(ClockController clock, int statusBarState) {
mClock = clock;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 7a5ad5e..05ace74 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -104,6 +104,7 @@
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+ private boolean mShownOnSecondaryDisplay = false;
private boolean mOnlyClock = false;
private boolean mIsActiveDreamLockscreenHosted = false;
private FeatureFlags mFeatureFlags;
@@ -185,8 +186,18 @@
}
/**
- * Mostly used for alternate displays, limit the information shown
+ * When set, limits the information shown in an external display.
*/
+ public void setShownOnSecondaryDisplay(boolean shownOnSecondaryDisplay) {
+ mShownOnSecondaryDisplay = shownOnSecondaryDisplay;
+ }
+
+ /**
+ * Mostly used for alternate displays, limit the information shown
+ *
+ * @deprecated use {@link KeyguardClockSwitchController#setShownOnSecondaryDisplay}
+ */
+ @Deprecated
public void setOnlyClock(boolean onlyClock) {
mOnlyClock = onlyClock;
}
@@ -211,8 +222,10 @@
mSmallClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
- mDumpManager.unregisterDumpable(getClass().getSimpleName()); // unregister previous clocks
- mDumpManager.registerDumpable(getClass().getSimpleName(), this);
+ if (!mOnlyClock) {
+ mDumpManager.unregisterDumpable(getClass().getSimpleName()); // unregister previous
+ mDumpManager.registerDumpable(getClass().getSimpleName(), this);
+ }
if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
mStatusArea = mView.findViewById(R.id.keyguard_status_area);
@@ -221,6 +234,15 @@
}
}
+ private void hideSliceViewAndNotificationIconContainer() {
+ View ksv = mView.findViewById(R.id.keyguard_slice_view);
+ ksv.setVisibility(View.GONE);
+
+ View nic = mView.findViewById(
+ R.id.left_aligned_notification_icon_container);
+ nic.setVisibility(View.GONE);
+ }
+
@Override
protected void onViewAttached() {
mClockRegistry.registerClockChangeListener(mClockChangedListener);
@@ -234,13 +256,15 @@
mKeyguardDateWeatherViewInvisibility =
mView.getResources().getInteger(R.integer.keyguard_date_weather_view_invisibility);
- if (mOnlyClock) {
- View ksv = mView.findViewById(R.id.keyguard_slice_view);
- ksv.setVisibility(View.GONE);
+ if (mShownOnSecondaryDisplay) {
+ mView.setLargeClockOnSecondaryDisplay(true);
+ displayClock(LARGE, /* animate= */ false);
+ hideSliceViewAndNotificationIconContainer();
+ return;
+ }
- View nic = mView.findViewById(
- R.id.left_aligned_notification_icon_container);
- nic.setVisibility(View.GONE);
+ if (mOnlyClock) {
+ hideSliceViewAndNotificationIconContainer();
return;
}
updateAodIcons();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 9f21a31..1c5a575 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -43,16 +43,19 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import dagger.Lazy;
+
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import dagger.Lazy;
@SysUISingleton
public class KeyguardDisplayManager {
@@ -64,6 +67,9 @@
private final DisplayTracker mDisplayTracker;
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
+ private final ConnectedDisplayKeyguardPresentation.Factory
+ mConnectedDisplayKeyguardPresentationFactory;
+ private final FeatureFlags mFeatureFlags;
private final Context mContext;
private boolean mShowing;
@@ -105,7 +111,10 @@
@Main Executor mainExecutor,
@UiBackground Executor uiBgExecutor,
DeviceStateHelper deviceStateHelper,
- KeyguardStateController keyguardStateController) {
+ KeyguardStateController keyguardStateController,
+ ConnectedDisplayKeyguardPresentation.Factory
+ connectedDisplayKeyguardPresentationFactory,
+ FeatureFlags featureFlags) {
mContext = context;
mNavigationBarControllerLazy = navigationBarControllerLazy;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
@@ -115,6 +124,8 @@
mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
mDeviceStateHelper = deviceStateHelper;
mKeyguardStateController = keyguardStateController;
+ mConnectedDisplayKeyguardPresentationFactory = connectedDisplayKeyguardPresentationFactory;
+ mFeatureFlags = featureFlags;
}
private boolean isKeyguardShowable(Display display) {
@@ -185,8 +196,12 @@
return false;
}
- KeyguardPresentation createPresentation(Display display) {
- return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory);
+ Presentation createPresentation(Display display) {
+ if (mFeatureFlags.isEnabled(Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION)) {
+ return mConnectedDisplayKeyguardPresentationFactory.create(display);
+ } else {
+ return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 04692c4..9f3908a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -27,6 +27,7 @@
import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER;
import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
import static com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES;
import android.app.ActivityManager;
@@ -370,8 +371,12 @@
@Override
public void onOrientationChanged(int orientation) {
- KeyguardSecurityContainerController.this
+ if (mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE)) {
+ // TODO(b/295603468)
+ // Fix reinflation of views when flag is enabled.
+ KeyguardSecurityContainerController.this
.onDensityOrFontScaleOrOrientationChanged();
+ }
}
};
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 757022d..c314586 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -187,6 +187,11 @@
mConfigurationController.removeCallback(mConfigurationListener);
}
+ /** Sets the StatusView as shown on an external display. */
+ public void setDisplayedOnSecondaryDisplay() {
+ mKeyguardClockSwitchController.setShownOnSecondaryDisplay(true);
+ }
+
/**
* Called in notificationPanelViewController to avoid leak
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 58c8000..802a550 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -73,9 +73,7 @@
import com.android.systemui.biometrics.domain.model.BiometricModalities;
import com.android.systemui.biometrics.ui.BiometricPromptLayout;
import com.android.systemui.biometrics.ui.CredentialView;
-import com.android.systemui.biometrics.ui.binder.AuthBiometricFingerprintViewBinder;
import com.android.systemui.biometrics.ui.binder.BiometricViewBinder;
-import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
import com.android.systemui.dagger.qualifiers.Background;
@@ -142,8 +140,6 @@
// TODO: these should be migrated out once ready
private final Provider<PromptCredentialInteractor> mPromptCredentialInteractor;
- private final Provider<AuthBiometricFingerprintViewModel>
- mAuthBiometricFingerprintViewModelProvider;
private final @NonNull Provider<PromptSelectorInteractor> mPromptSelectorInteractorProvider;
// TODO(b/251476085): these should be migrated out of the view
private final Provider<CredentialViewModel> mCredentialViewModelProvider;
@@ -283,8 +279,6 @@
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils,
@NonNull InteractionJankMonitor jankMonitor,
- @NonNull Provider<AuthBiometricFingerprintViewModel>
- authBiometricFingerprintViewModelProvider,
@NonNull Provider<PromptCredentialInteractor> promptCredentialInteractor,
@NonNull Provider<PromptSelectorInteractor> promptSelectorInteractor,
@NonNull PromptViewModel promptViewModel,
@@ -293,9 +287,9 @@
@NonNull VibratorHelper vibratorHelper) {
this(config, featureFlags, applicationCoroutineScope, fpProps, faceProps,
wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
- jankMonitor, authBiometricFingerprintViewModelProvider, promptSelectorInteractor,
- promptCredentialInteractor, promptViewModel, credentialViewModelProvider,
- new Handler(Looper.getMainLooper()), bgExecutor, vibratorHelper);
+ jankMonitor, promptSelectorInteractor, promptCredentialInteractor, promptViewModel,
+ credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor,
+ vibratorHelper);
}
@VisibleForTesting
@@ -309,8 +303,6 @@
@NonNull UserManager userManager,
@NonNull LockPatternUtils lockPatternUtils,
@NonNull InteractionJankMonitor jankMonitor,
- @NonNull Provider<AuthBiometricFingerprintViewModel>
- authBiometricFingerprintViewModelProvider,
@NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider,
@NonNull Provider<PromptCredentialInteractor> credentialInteractor,
@NonNull PromptViewModel promptViewModel,
@@ -359,7 +351,6 @@
mBackgroundExecutor = bgExecutor;
mInteractionJankMonitor = jankMonitor;
mPromptCredentialInteractor = credentialInteractor;
- mAuthBiometricFingerprintViewModelProvider = authBiometricFingerprintViewModelProvider;
mPromptSelectorInteractorProvider = promptSelectorInteractorProvider;
mCredentialViewModelProvider = credentialViewModelProvider;
mPromptViewModel = promptViewModel;
@@ -442,9 +433,6 @@
fingerprintAndFaceView.updateOverrideIconLayoutParamsSize();
fingerprintAndFaceView.setFaceClass3(
faceProperties.sensorStrength == STRENGTH_STRONG);
- final AuthBiometricFingerprintViewModel fpAndFaceViewModel =
- mAuthBiometricFingerprintViewModelProvider.get();
- AuthBiometricFingerprintViewBinder.bind(fingerprintAndFaceView, fpAndFaceViewModel);
mBiometricView = fingerprintAndFaceView;
} else if (fpProperties != null) {
final AuthBiometricFingerprintView fpView =
@@ -453,9 +441,6 @@
fpView.setSensorProperties(fpProperties);
fpView.setScaleFactorProvider(config.mScaleProvider);
fpView.updateOverrideIconLayoutParamsSize();
- final AuthBiometricFingerprintViewModel fpViewModel =
- mAuthBiometricFingerprintViewModelProvider.get();
- AuthBiometricFingerprintViewBinder.bind(fpView, fpViewModel);
mBiometricView = fpView;
} else if (faceProperties != null) {
mBiometricView = (AuthBiometricFaceView) layoutInflater.inflate(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 7b288a8..d5289a4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -73,7 +73,6 @@
import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
-import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
import com.android.systemui.dagger.SysUISingleton;
@@ -134,8 +133,6 @@
private final CoroutineScope mApplicationCoroutineScope;
// TODO: these should be migrated out once ready
- @NonNull private final Provider<AuthBiometricFingerprintViewModel>
- mAuthBiometricFingerprintViewModelProvider;
@NonNull private final Provider<PromptCredentialInteractor> mPromptCredentialInteractor;
@NonNull private final Provider<PromptSelectorInteractor> mPromptSelectorInteractor;
@NonNull private final Provider<CredentialViewModel> mCredentialViewModelProvider;
@@ -765,8 +762,6 @@
@NonNull LockPatternUtils lockPatternUtils,
@NonNull UdfpsLogger udfpsLogger,
@NonNull LogContextInteractor logContextInteractor,
- @NonNull Provider<AuthBiometricFingerprintViewModel>
- authBiometricFingerprintViewModelProvider,
@NonNull Provider<PromptCredentialInteractor> promptCredentialInteractorProvider,
@NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider,
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@@ -801,7 +796,6 @@
mVibratorHelper = vibratorHelper;
mLogContextInteractor = logContextInteractor;
- mAuthBiometricFingerprintViewModelProvider = authBiometricFingerprintViewModelProvider;
mPromptSelectorInteractor = promptSelectorInteractorProvider;
mPromptCredentialInteractor = promptCredentialInteractorProvider;
mPromptViewModelProvider = promptViewModelProvider;
@@ -1344,9 +1338,8 @@
config.mScaleProvider = this::getScaleFactor;
return new AuthContainerView(config, mFeatureFlags, mApplicationCoroutineScope, mFpProps, mFaceProps,
wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
- mInteractionJankMonitor, mAuthBiometricFingerprintViewModelProvider,
- mPromptCredentialInteractor, mPromptSelectorInteractor, viewModel,
- mCredentialViewModelProvider, bgExecutor, mVibratorHelper);
+ mInteractionJankMonitor, mPromptCredentialInteractor, mPromptSelectorInteractor,
+ viewModel, mCredentialViewModelProvider, bgExecutor, mVibratorHelper);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index bb87dca..5badcaf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -21,15 +21,18 @@
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.Utils.getCredentialType
import com.android.systemui.biometrics.Utils.isDeviceCredentialAllowed
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.PromptRepository
import com.android.systemui.biometrics.domain.model.BiometricModalities
import com.android.systemui.biometrics.domain.model.BiometricOperationInfo
import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
import com.android.systemui.biometrics.shared.model.BiometricUserInfo
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -65,6 +68,9 @@
*/
val isConfirmationRequired: Flow<Boolean>
+ /** Fingerprint sensor type */
+ val sensorType: StateFlow<FingerprintSensorType>
+
/** Use biometrics for authentication. */
fun useBiometricsForAuthentication(
promptInfo: PromptInfo,
@@ -89,6 +95,7 @@
class PromptSelectorInteractorImpl
@Inject
constructor(
+ private val fingerprintPropertyRepository: FingerprintPropertyRepository,
private val promptRepository: PromptRepository,
lockPatternUtils: LockPatternUtils,
) : PromptSelectorInteractor {
@@ -140,6 +147,9 @@
}
}
+ override val sensorType: StateFlow<FingerprintSensorType> =
+ fingerprintPropertyRepository.sensorType
+
override fun useBiometricsForAuthentication(
promptInfo: PromptInfo,
userId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index caebc30..a3ee220 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -16,6 +16,7 @@
val description: String,
val userInfo: BiometricUserInfo,
val operationInfo: BiometricOperationInfo,
+ val showEmergencyCallButton: Boolean,
) {
/** Prompt using one or more biometrics. */
class Biometric(
@@ -29,7 +30,8 @@
subtitle = info.subtitle?.toString() ?: "",
description = info.description?.toString() ?: "",
userInfo = userInfo,
- operationInfo = operationInfo
+ operationInfo = operationInfo,
+ showEmergencyCallButton = info.isShowEmergencyCallButton
) {
val negativeButtonText: String = info.negativeButtonText?.toString() ?: ""
}
@@ -46,6 +48,7 @@
description = (info.deviceCredentialDescription ?: info.description)?.toString() ?: "",
userInfo = userInfo,
operationInfo = operationInfo,
+ showEmergencyCallButton = info.isShowEmergencyCallButton
) {
/** PIN prompt. */
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt
deleted file mode 100644
index 9c1bcec..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.biometrics.ui.binder
-
-import com.android.systemui.biometrics.AuthBiometricFingerprintView
-import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel
-
-object AuthBiometricFingerprintViewBinder {
-
- /**
- * Binds a [AuthBiometricFingerprintView.mIconView] to a [AuthBiometricFingerprintViewModel].
- */
- @JvmStatic
- fun bind(view: AuthBiometricFingerprintView, viewModel: AuthBiometricFingerprintViewModel) {
- if (view.isSfps) {
- AuthBiometricFingerprintIconViewBinder.bind(view.getIconView(), viewModel)
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index d054751..b1439fd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -108,6 +108,9 @@
val iconViewOverlay = view.requireViewById<LottieAnimationView>(R.id.biometric_icon_overlay)
val iconView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon)
+
+ PromptFingerprintIconViewBinder.bind(iconView, viewModel.fingerprintIconViewModel)
+
val indicatorMessageView = view.requireViewById<TextView>(R.id.indicator)
// Negative-side (left) buttons
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
index 9292bd7..4ac9f96 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -2,6 +2,7 @@
import android.view.View
import android.view.ViewGroup
+import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.lifecycle.Lifecycle
@@ -46,6 +47,7 @@
val descriptionView: TextView = view.requireViewById(R.id.description)
val iconView: ImageView? = view.findViewById(R.id.icon)
val errorView: TextView = view.requireViewById(R.id.error)
+ val emergencyButtonView: Button = view.requireViewById(R.id.emergencyCallButton)
var errorTimer: Job? = null
@@ -75,6 +77,13 @@
iconView?.setImageDrawable(header.icon)
+ if (header.showEmergencyCallButton) {
+ emergencyButtonView.visibility = View.VISIBLE
+ emergencyButtonView.setOnClickListener {
+ viewModel.doEmergencyCall(view.context)
+ }
+ }
+
// Only animate this if we're transitioning from a biometric view.
if (viewModel.animateContents.value) {
view.animateCredentialViewIn()
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt
similarity index 68%
rename from packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintIconViewBinder.kt
rename to packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt
index bd0907e..188c82b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptFingerprintIconViewBinder.kt
@@ -21,26 +21,29 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.biometrics.AuthBiometricFingerprintView
-import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel
+import com.android.systemui.biometrics.ui.viewmodel.PromptFingerprintIconViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import kotlinx.coroutines.launch
-/** Sub-binder for [AuthBiometricFingerprintView.mIconView]. */
-object AuthBiometricFingerprintIconViewBinder {
+/** Sub-binder for [BiometricPromptLayout.iconView]. */
+object PromptFingerprintIconViewBinder {
- /**
- * Binds a [AuthBiometricFingerprintView.mIconView] to a [AuthBiometricFingerprintViewModel].
- */
+ /** Binds [BiometricPromptLayout.iconView] to [PromptFingerprintIconViewModel]. */
@JvmStatic
- fun bind(view: LottieAnimationView, viewModel: AuthBiometricFingerprintViewModel) {
+ fun bind(view: LottieAnimationView, viewModel: PromptFingerprintIconViewModel) {
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
val displayInfo = DisplayInfo()
view.context.display?.getDisplayInfo(displayInfo)
viewModel.setRotation(displayInfo.rotation)
viewModel.onConfigurationChanged(view.context.resources.configuration)
- launch { viewModel.iconAsset.collect { iconAsset -> view.setAnimation(iconAsset) } }
+ launch {
+ viewModel.iconAsset.collect { iconAsset ->
+ if (iconAsset != -1) {
+ view.setAnimation(iconAsset)
+ }
+ }
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialHeaderViewModel.kt
index 3257f20..c6d9085 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialHeaderViewModel.kt
@@ -10,4 +10,5 @@
val subtitle: String
val description: String
val icon: Drawable
+ val showEmergencyCallButton: Boolean
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
index a3b23ca..6212ef0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -41,6 +41,7 @@
subtitle = request.subtitle,
description = request.description,
icon = applicationContext.asLockIcon(request.userInfo.deviceCredentialOwnerId),
+ showEmergencyCallButton = request.showEmergencyCallButton
)
}
@@ -136,6 +137,18 @@
}
}
}
+
+ fun doEmergencyCall(context: Context) {
+ val intent =
+ context
+ .getSystemService(android.telecom.TelecomManager::class.java)!!
+ .createLaunchEmergencyDialerIntent(null)
+ .setFlags(
+ android.content.Intent.FLAG_ACTIVITY_NEW_TASK or
+ android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
+ )
+ context.startActivity(intent)
+ }
}
private fun Context.asBadCredentialErrorMessage(prompt: BiometricPromptRequest?): String =
@@ -174,6 +187,7 @@
override val subtitle: String,
override val description: String,
override val icon: Drawable,
+ override val showEmergencyCallButton: Boolean,
) : CredentialHeaderViewModel
private fun CredentialHeaderViewModel.asRequest(): BiometricPromptRequest.Credential =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt
index 617d80c..9b30acb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModel.kt
@@ -22,23 +22,35 @@
import android.view.Surface
import com.android.systemui.R
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
-/** Models UI of AuthBiometricFingerprintView to support rear display state changes. */
-class AuthBiometricFingerprintViewModel
+/** Models UI of [BiometricPromptLayout.iconView] */
+class PromptFingerprintIconViewModel
@Inject
-constructor(private val interactor: DisplayStateInteractor) {
+constructor(
+ private val displayStateInteractor: DisplayStateInteractor,
+ private val promptSelectorInteractor: PromptSelectorInteractor,
+) {
/** Current device rotation. */
private var rotation: Int = Surface.ROTATION_0
- /** Current AuthBiometricFingerprintView asset. */
+ /** Current BiometricPromptLayout.iconView asset. */
val iconAsset: Flow<Int> =
- combine(interactor.isFolded, interactor.isInRearDisplayMode) {
- isFolded: Boolean,
- isInRearDisplayMode: Boolean ->
- getSideFpsAnimationAsset(isFolded, isInRearDisplayMode)
+ combine(
+ displayStateInteractor.isFolded,
+ displayStateInteractor.isInRearDisplayMode,
+ promptSelectorInteractor.sensorType,
+ ) { isFolded: Boolean, isInRearDisplayMode: Boolean, sensorType: FingerprintSensorType ->
+ when (sensorType) {
+ FingerprintSensorType.POWER_BUTTON ->
+ getSideFpsAnimationAsset(isFolded, isInRearDisplayMode)
+ // Replace below when non-SFPS iconAsset logic is migrated to this ViewModel
+ else -> -1
+ }
}
@RawRes
@@ -75,7 +87,7 @@
/** Called on configuration changes */
fun onConfigurationChanged(newConfig: Configuration) {
- interactor.onConfigurationChanged(newConfig)
+ displayStateInteractor.onConfigurationChanged(newConfig)
}
fun setRotation(newRotation: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 89561a5..4dc7720 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -20,6 +20,7 @@
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import com.android.systemui.biometrics.AuthBiometricView
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.model.BiometricModalities
import com.android.systemui.biometrics.shared.model.BiometricModality
@@ -45,16 +46,23 @@
class PromptViewModel
@Inject
constructor(
- private val interactor: PromptSelectorInteractor,
+ private val displayStateInteractor: DisplayStateInteractor,
+ private val promptSelectorInteractor: PromptSelectorInteractor,
private val vibrator: VibratorHelper,
private val featureFlags: FeatureFlags,
) {
+ /** Models UI of [BiometricPromptLayout.iconView] */
+ val fingerprintIconViewModel: PromptFingerprintIconViewModel =
+ PromptFingerprintIconViewModel(displayStateInteractor, promptSelectorInteractor)
+
/** The set of modalities available for this prompt */
val modalities: Flow<BiometricModalities> =
- interactor.prompt.map { it?.modalities ?: BiometricModalities() }.distinctUntilChanged()
+ promptSelectorInteractor.prompt
+ .map { it?.modalities ?: BiometricModalities() }
+ .distinctUntilChanged()
// TODO(b/251476085): remove after icon controllers are migrated - do not keep this state
- private var _legacyState = MutableStateFlow(AuthBiometricView.STATE_AUTHENTICATING_ANIMATING_IN)
+ private var _legacyState = MutableStateFlow(AuthBiometricView.STATE_IDLE)
val legacyState: StateFlow<Int> = _legacyState.asStateFlow()
private val _isAuthenticating: MutableStateFlow<Boolean> = MutableStateFlow(false)
@@ -75,17 +83,18 @@
* successful authentication.
*/
val isConfirmationRequired: Flow<Boolean> =
- combine(_isOverlayTouched, interactor.isConfirmationRequired) {
+ combine(_isOverlayTouched, promptSelectorInteractor.isConfirmationRequired) {
isOverlayTouched,
isConfirmationRequired ->
!isOverlayTouched && isConfirmationRequired
}
/** The kind of credential the user has. */
- val credentialKind: Flow<PromptKind> = interactor.credentialKind
+ val credentialKind: Flow<PromptKind> = promptSelectorInteractor.credentialKind
/** The label to use for the cancel button. */
- val negativeButtonText: Flow<String> = interactor.prompt.map { it?.negativeButtonText ?: "" }
+ val negativeButtonText: Flow<String> =
+ promptSelectorInteractor.prompt.map { it?.negativeButtonText ?: "" }
private val _message: MutableStateFlow<PromptMessage> = MutableStateFlow(PromptMessage.Empty)
@@ -113,7 +122,7 @@
_forceLargeSize,
_forceMediumSize,
modalities,
- interactor.isConfirmationRequired,
+ promptSelectorInteractor.isConfirmationRequired,
fingerprintStartMode,
) { forceLarge, forceMedium, modalities, confirmationRequired, fpStartMode ->
when {
@@ -129,14 +138,16 @@
.distinctUntilChanged()
/** Title for the prompt. */
- val title: Flow<String> = interactor.prompt.map { it?.title ?: "" }.distinctUntilChanged()
+ val title: Flow<String> =
+ promptSelectorInteractor.prompt.map { it?.title ?: "" }.distinctUntilChanged()
/** Subtitle for the prompt. */
- val subtitle: Flow<String> = interactor.prompt.map { it?.subtitle ?: "" }.distinctUntilChanged()
+ val subtitle: Flow<String> =
+ promptSelectorInteractor.prompt.map { it?.subtitle ?: "" }.distinctUntilChanged()
/** Description for the prompt. */
val description: Flow<String> =
- interactor.prompt.map { it?.description ?: "" }.distinctUntilChanged()
+ promptSelectorInteractor.prompt.map { it?.description ?: "" }.distinctUntilChanged()
/** If the indicator (help, error) message should be shown. */
val isIndicatorMessageVisible: Flow<Boolean> =
@@ -160,7 +171,9 @@
/** If the icon can be used as a confirmation button. */
val isIconConfirmButton: Flow<Boolean> =
- combine(size, interactor.isConfirmationRequired) { size, isConfirmationRequired ->
+ combine(size, promptSelectorInteractor.isConfirmationRequired) {
+ size,
+ isConfirmationRequired ->
size.isNotSmall && isConfirmationRequired
}
@@ -169,7 +182,7 @@
combine(
size,
isAuthenticated,
- interactor.isCredentialAllowed,
+ promptSelectorInteractor.isCredentialAllowed,
) { size, authState, credentialAllowed ->
size.isNotSmall && authState.isNotAuthenticated && !credentialAllowed
}
@@ -221,7 +234,7 @@
combine(
size,
isAuthenticated,
- interactor.isCredentialAllowed,
+ promptSelectorInteractor.isCredentialAllowed,
) { size, authState, credentialAllowed ->
size.isNotSmall && authState.isNotAuthenticated && credentialAllowed
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 970d00b..3bf9cc8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -107,6 +107,7 @@
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LetterboxModule;
+import com.android.systemui.statusbar.phone.NotificationIconAreaControllerModule;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -185,6 +186,7 @@
MediaProjectionModule.class,
MediaProjectionTaskSwitcherModule.class,
MotionToolModule.class,
+ NotificationIconAreaControllerModule.class,
PeopleHubModule.class,
PeopleModule.class,
PluginModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt
index b20e33a..83c239f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt
@@ -44,12 +44,12 @@
private var androidRestartRequested = false
override fun restartSystemUI(reason: String) {
- Log.d(FeatureFlagsDebug.TAG, "SystemUI Restart requested. Restarting when idle.")
+ Log.d(FeatureFlagsClassicDebug.TAG, "SystemUI Restart requested. Restarting when idle.")
scheduleRestart(reason)
}
override fun restartAndroid(reason: String) {
- Log.d(FeatureFlagsDebug.TAG, "Android Restart requested. Restarting when idle.")
+ Log.d(FeatureFlagsClassicDebug.TAG, "Android Restart requested. Restarting when idle.")
androidRestartRequested = true
scheduleRestart(reason)
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
index 95e7ad96..d48eb29 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -23,6 +23,21 @@
*
* See [Flags] for instructions on defining new flags.
*/
+interface FeatureFlagsClassic : FeatureFlags
+
+/**
+ * Class to manage simple DeviceConfig-based feature flags.
+ *
+ * See [Flags] for instructions on defining new flags.
+ */
+@Deprecated(
+ message = "Use FeatureFlagsClassic instead.",
+ replaceWith =
+ ReplaceWith(
+ "FeatureFlagsClassic",
+ "com.android.systemui.flags.FeatureFlagsClassic",
+ ),
+)
interface FeatureFlags : FlagListenable, Dumpable {
/** Returns a boolean value for the given flag. */
fun isEnabled(flag: UnreleasedFlag): Boolean
@@ -47,4 +62,4 @@
/** Returns an int value for a given flag/ */
fun getInt(flag: ResourceIntFlag): Int
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
rename to packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
index 4c78e4c..126a1b5 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
@@ -67,7 +67,7 @@
* To restore a flag back to its default, leave the `--ez value <0|1>` off of the command.
*/
@SysUISingleton
-public class FeatureFlagsDebug implements FeatureFlags {
+public class FeatureFlagsClassicDebug implements FeatureFlagsClassic {
static final String TAG = "SysUIFlags";
private final FlagManager mFlagManager;
@@ -116,7 +116,7 @@
};
@Inject
- public FeatureFlagsDebug(
+ public FeatureFlagsClassicDebug(
FlagManager flagManager,
Context context,
GlobalSettings globalSettings,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicRelease.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
rename to packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicRelease.java
index e03b438..79ebf9c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicRelease.java
@@ -43,7 +43,7 @@
* how to set flags.
*/
@SysUISingleton
-public class FeatureFlagsRelease implements FeatureFlags {
+public class FeatureFlagsClassicRelease implements FeatureFlagsClassic {
static final String TAG = "SysUIFlags";
private final Resources mResources;
@@ -89,7 +89,7 @@
};
@Inject
- public FeatureFlagsRelease(
+ public FeatureFlagsClassicRelease(
@Main Resources resources,
SystemPropertiesHelper systemProperties,
ServerFlagReader serverFlagReader,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
index 28c45b8..dd560b7 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt
@@ -34,13 +34,13 @@
dumpManager: DumpManager,
private val commandRegistry: CommandRegistry,
private val flagCommand: FlagCommand,
- private val featureFlags: FeatureFlagsDebug,
+ private val featureFlags: FeatureFlagsClassicDebug,
private val broadcastSender: BroadcastSender,
private val initializationChecker: InitializationChecker,
) : CoreStartable {
init {
- dumpManager.registerCriticalDumpable(FeatureFlagsDebug.TAG) { pw, args ->
+ dumpManager.registerCriticalDumpable(FeatureFlagsClassicDebug.TAG) { pw, args ->
featureFlags.dump(pw, args)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
index f97112d..dfcc7ea 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt
@@ -32,7 +32,7 @@
) : CoreStartable {
init {
- dumpManager.registerCriticalDumpable(FeatureFlagsRelease.TAG) { pw, args ->
+ dumpManager.registerCriticalDumpable(FeatureFlagsClassicRelease.TAG) { pw, args ->
featureFlags.dump(pw, args)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
index bd0ed48..e3cc2b0 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java
@@ -38,12 +38,12 @@
private final List<String> mOnCommands = List.of("true", "on", "1", "enabled");
private final List<String> mOffCommands = List.of("false", "off", "0", "disable");
private final List<String> mSetCommands = List.of("set", "put");
- private final FeatureFlagsDebug mFeatureFlags;
+ private final FeatureFlagsClassicDebug mFeatureFlags;
private final Map<String, Flag<?>> mAllFlags;
@Inject
FlagCommand(
- FeatureFlagsDebug featureFlags,
+ FeatureFlagsClassicDebug featureFlags,
@Named(ALL_FLAGS) Map<String, Flag<?>> allFlags
) {
mFeatureFlags = featureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index cca71e8..90e78c6 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -33,7 +33,7 @@
* On public release builds, flags will always return their default value. There is no way to change
* their value on release builds.
*
- * See [FeatureFlagsDebug] for instructions on flipping the flags via adb.
+ * See [FeatureFlagsClassicDebug] for instructions on flipping the flags via adb.
*/
object Flags {
@JvmField val TEAMFOOD = unreleasedFlag("teamfood")
@@ -115,7 +115,7 @@
// TODO(b/292213543): Tracking Bug
@JvmField
val NOTIFICATION_GROUP_EXPANSION_CHANGE =
- unreleasedFlag("notification_group_expansion_change", teamfood = false)
+ unreleasedFlag("notification_group_expansion_change", teamfood = true)
// 200 - keyguard/lockscreen
// ** Flag retired **
@@ -254,7 +254,7 @@
// TODO(b/290652751): Tracking bug.
@JvmField
val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA =
- unreleasedFlag("migrate_split_keyguard_bottom_area")
+ unreleasedFlag("migrate_split_keyguard_bottom_area", teamfood = true)
/** Whether to listen for fingerprint authentication over keyguard occluding activities. */
// TODO(b/283260512): Tracking bug.
@@ -269,7 +269,7 @@
/** Migrate the lock icon view to the new keyguard root view. */
// TODO(b/286552209): Tracking bug.
- @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag("migrate_lock_icon")
+ @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag("migrate_lock_icon", teamfood = true)
// TODO(b/288276738): Tracking bug.
@JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag("widget_on_keyguard")
@@ -355,7 +355,7 @@
// TODO(b/278068252): Tracking Bug
@JvmField
- val QS_PIPELINE_AUTO_ADD = unreleasedFlag("qs_pipeline_auto_add", teamfood = false)
+ val QS_PIPELINE_AUTO_ADD = unreleasedFlag("qs_pipeline_auto_add", teamfood = true)
// TODO(b/254512383): Tracking Bug
@JvmField
@@ -368,10 +368,6 @@
// TODO(b/244064524): Tracking Bug
@JvmField val QS_SECONDARY_DATA_SUB_INFO = releasedFlag("qs_secondary_data_sub_info")
- /** Enables Font Scaling Quick Settings tile */
- // TODO(b/269341316): Tracking Bug
- @JvmField val ENABLE_FONT_SCALING_TILE = releasedFlag("enable_font_scaling_tile")
-
/** Enables new QS Edit Mode visual refresh */
// TODO(b/269787742): Tracking Bug
@JvmField
@@ -398,6 +394,10 @@
// TODO(b/294588085): Tracking Bug
val WIFI_SECONDARY_NETWORKS = releasedFlag("wifi_secondary_networks")
+ // TODO(b/290676905): Tracking Bug
+ val NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS =
+ unreleasedFlag("new_shade_carrier_group_mobile_icons")
+
// 700 - dialer/calls
// TODO(b/254512734): Tracking Bug
val ONGOING_CALL_STATUS_BAR_CHIP = releasedFlag("ongoing_call_status_bar_chip")
@@ -502,21 +502,6 @@
val WM_CAPTION_ON_SHELL =
sysPropBooleanFlag("persist.wm.debug.caption_on_shell", default = true)
- @Keep
- @JvmField
- val ENABLE_FLING_TO_DISMISS_BUBBLE =
- sysPropBooleanFlag("persist.wm.debug.fling_to_dismiss_bubble", default = true)
-
- @Keep
- @JvmField
- val ENABLE_FLING_TO_DISMISS_PIP =
- sysPropBooleanFlag("persist.wm.debug.fling_to_dismiss_pip", default = true)
-
- @Keep
- @JvmField
- val ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
- sysPropBooleanFlag("persist.wm.debug.enable_pip_keep_clear_algorithm", default = true)
-
// TODO(b/256873975): Tracking Bug
@JvmField
@Keep
@@ -538,13 +523,6 @@
teamfood = false
)
- // TODO(b/198643358): Tracking bug
- @Keep
- @JvmField
- val ENABLE_PIP_SIZE_LARGE_SCREEN =
- sysPropBooleanFlag("persist.wm.debug.enable_pip_size_large_screen", default = true)
-
-
// TODO(b/293252410) : Tracking Bug
@JvmField
val LOCKSCREEN_ENABLE_LANDSCAPE =
@@ -628,6 +606,10 @@
// TODO(b/251205791): Tracking Bug
@JvmField val SCREENSHOT_APP_CLIPS = releasedFlag("screenshot_app_clips")
+ /** TODO(b/295143676): Tracking bug. When enable, captures a screenshot for each display. */
+ @JvmField
+ val MULTI_DISPLAY_SCREENSHOT = unreleasedFlag("multi_display_screenshot")
+
// 1400 - columbus
// TODO(b/254512756): Tracking Bug
val QUICK_TAP_IN_PCC = releasedFlag("quick_tap_in_pcc")
@@ -769,6 +751,10 @@
@JvmField
val ONE_WAY_HAPTICS_API_MIGRATION = unreleasedFlag("oneway_haptics_api_migration")
+ /** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
+ @JvmField
+ val ENABLE_CLOCK_KEYGUARD_PRESENTATION = unreleasedFlag("enable_clock_keyguard_presentation")
+
/** Enable the Compose implementation of the PeopleSpaceActivity. */
@JvmField
val COMPOSE_PEOPLE_SPACE = unreleasedFlag("compose_people_space")
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
index 3c50125..d13fa1e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagsCommonModule.kt
@@ -25,6 +25,8 @@
interface FlagsCommonModule {
@Binds fun bindsRestarter(impl: ConditionalRestarter): Restarter
+ @Binds fun bindsClassic(impl: FeatureFlagsClassic): FeatureFlags
+
companion object {
const val ALL_FLAGS = "all_flags"
diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt
index 46e28a7..9f41b61 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt
@@ -26,12 +26,12 @@
private val barService: IStatusBarService,
) : Restarter {
override fun restartAndroid(reason: String) {
- Log.d(FeatureFlagsDebug.TAG, "Restarting Android: " + reason)
+ Log.d(FeatureFlagsClassicDebug.TAG, "Restarting Android: " + reason)
barService.restart()
}
override fun restartSystemUI(reason: String) {
- Log.d(FeatureFlagsDebug.TAG, "Restarting SystemUI: " + reason)
+ Log.d(FeatureFlagsClassicDebug.TAG, "Restarting SystemUI: " + reason)
System.exit(0)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
new file mode 100644
index 0000000..3f2f67d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.keyevent.domain.interactor
+
+import android.view.KeyEvent
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
+import javax.inject.Inject
+
+/**
+ * Sends key events to the appropriate interactors and then acts upon key events that haven't
+ * already been handled but should be handled by SystemUI.
+ */
+@SysUISingleton
+class KeyEventInteractor
+@Inject
+constructor(
+ private val backActionInteractor: BackActionInteractor,
+ private val keyguardKeyEventInteractor: KeyguardKeyEventInteractor,
+) {
+ fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ if (keyguardKeyEventInteractor.dispatchKeyEvent(event)) {
+ return true
+ }
+
+ when (event.keyCode) {
+ KeyEvent.KEYCODE_BACK -> {
+ if (event.handleAction()) {
+ backActionInteractor.onBackRequested()
+ }
+ return true
+ }
+ }
+ return false
+ }
+
+ fun interceptMediaKey(event: KeyEvent): Boolean {
+ return keyguardKeyEventInteractor.interceptMediaKey(event)
+ }
+
+ fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
+ return keyguardKeyEventInteractor.dispatchKeyEventPreIme(event)
+ }
+
+ companion object {
+ // Most actions shouldn't be handled on the down event and instead handled on subsequent
+ // key events like ACTION_UP.
+ fun KeyEvent.handleAction(): Boolean {
+ return action != KeyEvent.ACTION_DOWN
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
index 059f72b..7234757 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
@@ -17,7 +17,10 @@
package com.android.systemui.keyguard.data.repository
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.core.view.children
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -96,8 +99,16 @@
/** Determines the constraints for the ConstraintSet in the lockscreen root view. */
interface KeyguardBlueprint {
val id: String
+ val shouldRemoveUnconstrainedViews: Boolean
+ get() = true
- fun apply(constraintSet: ConstraintSet)
+ fun apply(constraintLayout: ConstraintSet)
+ fun removeUnConstrainedViews(constraintLayout: ConstraintLayout, constraintSet: ConstraintSet) {
+ constraintLayout.children
+ .map { it.id }
+ .filterNot { constraintSet.knownIds.contains(it) }
+ .forEach { constraintSet.setVisibility(it, View.GONE) }
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 562c4db..ed84884 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -40,10 +40,10 @@
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -53,7 +53,6 @@
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
-import javax.inject.Inject
/**
* Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -68,18 +67,6 @@
bouncerRepository: KeyguardBouncerRepository,
configurationRepository: ConfigurationRepository,
) {
-
- data class PreviewMode(
- val isInPreviewMode: Boolean = false,
- val shouldHighlightSelectedAffordance: Boolean = false,
- )
-
- /**
- * Whether this view-model instance is powering the preview experience that renders exclusively
- * in the wallpaper picker application. This should _always_ be `false` for the real lock screen
- * experience.
- */
- val previewMode = MutableStateFlow(PreviewMode())
/**
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
* all.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
new file mode 100644
index 0000000..e501ece
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.content.Context
+import android.media.AudioManager
+import android.view.KeyEvent
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor.Companion.handleAction
+import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeController
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import javax.inject.Inject
+
+/** Handles key events arriving when the keyguard is showing or device is dozing. */
+@SysUISingleton
+class KeyguardKeyEventInteractor
+@Inject
+constructor(
+ private val context: Context,
+ private val statusBarStateController: StatusBarStateController,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
+ private val shadeController: ShadeController,
+ private val mediaSessionLegacyHelperWrapper: MediaSessionLegacyHelperWrapper,
+ private val backActionInteractor: BackActionInteractor,
+) {
+
+ fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ if (statusBarStateController.isDozing) {
+ when (event.keyCode) {
+ KeyEvent.KEYCODE_VOLUME_DOWN,
+ KeyEvent.KEYCODE_VOLUME_UP -> return dispatchVolumeKeyEvent(event)
+ }
+ }
+
+ if (event.handleAction()) {
+ when (event.keyCode) {
+ KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent()
+ KeyEvent.KEYCODE_SPACE,
+ KeyEvent.KEYCODE_ENTER ->
+ if (isDeviceInteractive()) {
+ return collapseShadeLockedOrShowPrimaryBouncer()
+ }
+ }
+ }
+ return false
+ }
+
+ /**
+ * While IME is active and a BACK event is detected, check with {@link
+ * StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event should be
+ * handled before routing to IME, in order to prevent the user from having to hit back twice to
+ * exit bouncer.
+ */
+ fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
+ when (event.keyCode) {
+ KeyEvent.KEYCODE_BACK ->
+ if (
+ statusBarStateController.state == StatusBarState.KEYGUARD &&
+ statusBarKeyguardViewManager.dispatchBackKeyEventPreIme()
+ ) {
+ return backActionInteractor.onBackRequested()
+ }
+ }
+ return false
+ }
+
+ fun interceptMediaKey(event: KeyEvent): Boolean {
+ return statusBarStateController.state == StatusBarState.KEYGUARD &&
+ statusBarKeyguardViewManager.interceptMediaKey(event)
+ }
+
+ private fun dispatchMenuKeyEvent(): Boolean {
+ val shouldUnlockOnMenuPressed =
+ isDeviceInteractive() &&
+ (statusBarStateController.state != StatusBarState.SHADE) &&
+ statusBarKeyguardViewManager.shouldDismissOnMenuPressed()
+ if (shouldUnlockOnMenuPressed) {
+ return collapseShadeLockedOrShowPrimaryBouncer()
+ }
+ return false
+ }
+
+ private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean {
+ when (statusBarStateController.state) {
+ StatusBarState.SHADE -> return false
+ StatusBarState.SHADE_LOCKED -> {
+ shadeController.animateCollapseShadeForced()
+ return true
+ }
+ StatusBarState.KEYGUARD -> {
+ statusBarKeyguardViewManager.showPrimaryBouncer(true)
+ return true
+ }
+ }
+ return false
+ }
+
+ private fun dispatchVolumeKeyEvent(event: KeyEvent): Boolean {
+ mediaSessionLegacyHelperWrapper
+ .getHelper(context)
+ .sendVolumeKeyEvent(event, AudioManager.USE_DEFAULT_STREAM_TYPE, true)
+ return true
+ }
+
+ private fun isDeviceInteractive(): Boolean {
+ return keyguardInteractor.wakefulnessModel.value.isDeviceInteractive()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index ed4dd6a..e40c279 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -43,6 +43,7 @@
val emptyLayout = ConstraintSet.Layout()
knownIds.forEach { getConstraint(it).layout.copyFrom(emptyLayout) }
blueprint?.apply(this)
+ blueprint?.removeUnConstrainedViews(constraintLayout, this)
applyTo(constraintLayout)
}
Trace.endSection()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index dd3da97..41c1c96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -49,6 +49,7 @@
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
+import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
@@ -56,25 +57,31 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.monet.ColorScheme
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.shared.clocks.DefaultClockController
import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
/** Renders the preview of the lock screen. */
class KeyguardPreviewRenderer
+@OptIn(ExperimentalCoroutinesApi::class)
@AssistedInject
constructor(
@Application private val context: Context,
@@ -99,6 +106,9 @@
@Assisted bundle: Bundle,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
+ private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
+ private val chipbarCoordinator: ChipbarCoordinator,
+ private val keyguardStateController: KeyguardStateController,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -130,19 +140,21 @@
init {
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- keyguardRootViewModel.enablePreviewMode(
+ keyguardRootViewModel.enablePreviewMode()
+ quickAffordancesCombinedViewModel.enablePreviewMode(
initiallySelectedSlotId =
- bundle.getString(
- KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
- ),
+ bundle.getString(
+ KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
+ )
+ ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
} else {
bottomAreaViewModel.enablePreviewMode(
initiallySelectedSlotId =
- bundle.getString(
- KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
- ),
+ bundle.getString(
+ KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
+ ),
shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
}
@@ -163,17 +175,8 @@
val rootView = FrameLayout(context)
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- val keyguardRootView = KeyguardRootView(context, null)
- rootView.addView(
- keyguardRootView,
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT,
- ),
- )
- setupShortcuts(keyguardRootView)
- KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
- keyguardBlueprintInteractor.refreshBlueprint()
+ setupKeyguardRootView(rootView)
+ setupShortcuts(rootView)
} else {
setUpBottomArea(rootView)
}
@@ -314,7 +317,7 @@
false,
) as KeyguardBottomAreaView
bottomAreaView.init(
- viewModel = bottomAreaViewModel,
+ viewModel = bottomAreaViewModel,
)
parentView.addView(
bottomAreaView,
@@ -325,10 +328,34 @@
)
}
- private fun setupShortcuts(keyguardRootView: KeyguardRootView) {
+ @OptIn(ExperimentalCoroutinesApi::class)
+ private fun setupKeyguardRootView(rootView: FrameLayout) {
+ val keyguardRootView = KeyguardRootView(context, null)
+ disposables.add(
+ KeyguardRootViewBinder.bind(
+ keyguardRootView,
+ keyguardRootViewModel,
+ featureFlags,
+ occludingAppDeviceEntryMessageViewModel,
+ chipbarCoordinator,
+ keyguardStateController,
+ )
+ )
+ rootView.addView(
+ keyguardRootView,
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ ),
+ )
+ KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
+ keyguardBlueprintInteractor.refreshBlueprint()
+ }
+
+ private fun setupShortcuts(rootView: FrameLayout) {
shortcutsBindings.add(
KeyguardQuickAffordanceViewBinder.bind(
- keyguardRootView.requireViewById(R.id.start_button),
+ rootView.requireViewById(R.id.start_button),
quickAffordancesCombinedViewModel.startButton,
keyguardRootViewModel.alpha,
falsingManager,
@@ -340,7 +367,7 @@
shortcutsBindings.add(
KeyguardQuickAffordanceViewBinder.bind(
- keyguardRootView.requireViewById(R.id.end_button),
+ rootView.requireViewById(R.id.end_button),
quickAffordancesCombinedViewModel.endButton,
keyguardRootViewModel.alpha,
falsingManager,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index 56a98455..02ea550 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -18,22 +18,20 @@
package com.android.systemui.keyguard.ui.viewmodel
import androidx.annotation.VisibleForTesting
-import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import javax.inject.Inject
@OptIn(ExperimentalCoroutinesApi::class)
class KeyguardQuickAffordancesCombinedViewModel
@@ -43,6 +41,18 @@
private val keyguardInteractor: KeyguardInteractor,
) {
+ data class PreviewMode(
+ val isInPreviewMode: Boolean = false,
+ val shouldHighlightSelectedAffordance: Boolean = false,
+ )
+
+ /**
+ * Whether this view-model instance is powering the preview experience that renders exclusively
+ * in the wallpaper picker application. This should _always_ be `false` for the real lock screen
+ * experience.
+ */
+ private val previewMode = MutableStateFlow(PreviewMode())
+
/**
* ID of the slot that's currently selected in the preview that renders exclusively in the
* wallpaper picker application. This is ignored for the actual, real lock screen experience.
@@ -85,39 +95,63 @@
selectedPreviewSlotId.value = slotId
}
+ /**
+ * Puts this view-model in "preview mode", which means it's being used for UI that is rendering
+ * the lock screen preview in wallpaper picker / settings and not the real experience on the
+ * lock screen.
+ *
+ * @param initiallySelectedSlotId The ID of the initial slot to render as the selected one.
+ * @param shouldHighlightSelectedAffordance Whether the selected quick affordance should be
+ * highlighted (while all others are dimmed to make the selected one stand out).
+ */
+ fun enablePreviewMode(
+ initiallySelectedSlotId: String?,
+ shouldHighlightSelectedAffordance: Boolean,
+ ) {
+ val newPreviewMode =
+ PreviewMode(
+ isInPreviewMode = true,
+ shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
+ )
+ onPreviewSlotSelected(
+ initiallySelectedSlotId ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
+ )
+ previewMode.value = newPreviewMode
+ }
+
private fun button(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceViewModel> {
- return keyguardInteractor.previewMode.flatMapLatest { previewMode ->
+ return previewMode.flatMapLatest { previewMode ->
combine(
- if (previewMode.isInPreviewMode) {
- quickAffordanceInteractor.quickAffordanceAlwaysVisible(position = position)
- } else {
- quickAffordanceInteractor.quickAffordance(position = position)
- },
- keyguardInteractor.animateDozingTransitions.distinctUntilChanged(),
- areQuickAffordancesFullyOpaque,
- selectedPreviewSlotId,
- quickAffordanceInteractor.useLongPress(),
- ) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId, useLongPress ->
- val slotId = position.toSlotId()
- val isSelected = selectedPreviewSlotId == slotId
- model.toViewModel(
- animateReveal = !previewMode.isInPreviewMode && animateReveal,
- isClickable = isFullyOpaque && !previewMode.isInPreviewMode,
- isSelected =
- previewMode.isInPreviewMode &&
- previewMode.shouldHighlightSelectedAffordance &&
- isSelected,
- isDimmed =
- previewMode.isInPreviewMode &&
- previewMode.shouldHighlightSelectedAffordance &&
- !isSelected,
- forceInactive = previewMode.isInPreviewMode,
- slotId = slotId,
- useLongPress = useLongPress,
- )
- }
+ if (previewMode.isInPreviewMode) {
+ quickAffordanceInteractor.quickAffordanceAlwaysVisible(position = position)
+ } else {
+ quickAffordanceInteractor.quickAffordance(position = position)
+ },
+ keyguardInteractor.animateDozingTransitions.distinctUntilChanged(),
+ areQuickAffordancesFullyOpaque,
+ selectedPreviewSlotId,
+ quickAffordanceInteractor.useLongPress(),
+ ) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId, useLongPress ->
+ val slotId = position.toSlotId()
+ val isSelected = selectedPreviewSlotId == slotId
+ model.toViewModel(
+ animateReveal = !previewMode.isInPreviewMode && animateReveal,
+ isClickable = isFullyOpaque && !previewMode.isInPreviewMode,
+ isSelected =
+ previewMode.isInPreviewMode &&
+ previewMode.shouldHighlightSelectedAffordance &&
+ isSelected,
+ isDimmed =
+ previewMode.isInPreviewMode &&
+ previewMode.shouldHighlightSelectedAffordance &&
+ !isSelected,
+ forceInactive = previewMode.isInPreviewMode,
+ slotId = slotId,
+ useLongPress = useLongPress,
+ )
+ }
.distinctUntilChanged()
}
}
@@ -167,8 +201,6 @@
// time, we don't want the number to be too close to 1.0 such that there is a chance that we
// never treat the affordance UI as "fully opaque" as that would risk making it forever not
// clickable.
- @VisibleForTesting
- const val AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD = 0.95f
+ @VisibleForTesting const val AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD = 0.95f
}
-
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 316ca77..92b9ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -19,29 +19,37 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
-import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
-import javax.inject.Inject
@OptIn(ExperimentalCoroutinesApi::class)
class KeyguardRootViewModel
@Inject
constructor(
private val keyguardInteractor: KeyguardInteractor,
- private val keyguardQuickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel
-)
-{
+) {
+
+ data class PreviewMode(val isInPreviewMode: Boolean = false)
+
+ /**
+ * Whether this view-model instance is powering the preview experience that renders exclusively
+ * in the wallpaper picker application. This should _always_ be `false` for the real lock screen
+ * experience.
+ */
+ private val previewMode = MutableStateFlow(PreviewMode())
+
/** Represents the current state of the KeyguardRootView visibility */
val keyguardRootViewVisibilityState: Flow<KeyguardRootViewVisibilityState> =
keyguardInteractor.keyguardRootViewVisibilityState
/** An observable for the alpha level for the entire keyguard root view. */
val alpha: Flow<Float> =
- keyguardInteractor.previewMode.flatMapLatest {
+ previewMode.flatMapLatest {
if (it.isInPreviewMode) {
flowOf(1f)
} else {
@@ -53,23 +61,9 @@
* Puts this view-model in "preview mode", which means it's being used for UI that is rendering
* the lock screen preview in wallpaper picker / settings and not the real experience on the
* lock screen.
- *
- * @param initiallySelectedSlotId The ID of the initial slot to render as the selected one.
- * @param shouldHighlightSelectedAffordance Whether the selected quick affordance should be
- * highlighted (while all others are dimmed to make the selected one stand out).
*/
- fun enablePreviewMode(
- initiallySelectedSlotId: String?,
- shouldHighlightSelectedAffordance: Boolean,
- ) {
- keyguardInteractor.previewMode.value =
- KeyguardInteractor.PreviewMode(
- isInPreviewMode = true,
- shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
- )
- keyguardQuickAffordancesCombinedViewModel.onPreviewSlotSelected(
- initiallySelectedSlotId ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
- )
+ fun enablePreviewMode() {
+ val newPreviewMode = PreviewMode(true)
+ previewMode.value = newPreviewMode
}
-
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index f8f784f..3d4fca1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -216,6 +216,58 @@
}
}
+ private void stopSound(Command cmd) {
+ final MediaPlayer mp;
+ synchronized (mPlayerLock) {
+ mp = mPlayer;
+ mPlayer = null;
+ }
+ if (mp == null) {
+ Log.w(mTag, "STOP command without a player");
+ return;
+ }
+
+ long delay = SystemClock.uptimeMillis() - cmd.requestTime;
+ if (delay > 1000) {
+ Log.w(mTag, "Notification stop delayed by " + delay + "msecs");
+ }
+ try {
+ mp.stop();
+ } catch (Exception e) {
+ Log.w(mTag, "Failed to stop MediaPlayer", e);
+ }
+ if (DEBUG) {
+ Log.i(mTag, "About to release MediaPlayer piid:"
+ + mp.getPlayerIId() + " due to notif cancelled");
+ }
+ try {
+ mp.release();
+ } catch (Exception e) {
+ Log.w(mTag, "Failed to release MediaPlayer", e);
+ }
+ synchronized (mQueueAudioFocusLock) {
+ if (mAudioManagerWithAudioFocus != null) {
+ if (DEBUG) {
+ Log.d(mTag, "in STOP: abandonning AudioFocus");
+ }
+ try {
+ mAudioManagerWithAudioFocus.abandonAudioFocus(null);
+ } catch (Exception e) {
+ Log.w(mTag, "Failed to abandon audio focus", e);
+ }
+ mAudioManagerWithAudioFocus = null;
+ }
+ }
+ synchronized (mCompletionHandlingLock) {
+ if ((mLooper != null) && (mLooper.getThread().getState() != Thread.State.TERMINATED)) {
+ if (DEBUG) {
+ Log.d(mTag, "in STOP: quitting looper " + mLooper);
+ }
+ mLooper.quit();
+ }
+ }
+ }
+
private final class CmdThread extends java.lang.Thread {
CmdThread() {
super("NotificationPlayer-" + mTag);
@@ -229,62 +281,28 @@
if (DEBUG) Log.d(mTag, "RemoveFirst");
cmd = mCmdQueue.removeFirst();
}
-
- switch (cmd.code) {
- case PLAY:
- if (DEBUG) Log.d(mTag, "PLAY");
- startSound(cmd);
- break;
- case STOP:
- if (DEBUG) Log.d(mTag, "STOP");
- final MediaPlayer mp;
- synchronized (mPlayerLock) {
- mp = mPlayer;
- mPlayer = null;
+ try {
+ switch (cmd.code) {
+ case PLAY:
+ if (DEBUG) Log.d(mTag, "PLAY");
+ startSound(cmd);
+ break;
+ case STOP:
+ if (DEBUG) Log.d(mTag, "STOP");
+ stopSound(cmd);
+ break;
}
- if (mp != null) {
- long delay = SystemClock.uptimeMillis() - cmd.requestTime;
- if (delay > 1000) {
- Log.w(mTag, "Notification stop delayed by " + delay + "msecs");
+ } finally {
+ synchronized (mCmdQueue) {
+ if (mCmdQueue.size() == 0) {
+ // nothing left to do, quit
+ // doing this check after we're done prevents the case where they
+ // added it during the operation from spawning two threads and
+ // trying to do them in parallel.
+ mThread = null;
+ releaseWakeLock();
+ return;
}
- try {
- mp.stop();
- } catch (Exception e) { }
- if (DEBUG) {
- Log.i(mTag, "About to release MediaPlayer piid:"
- + mp.getPlayerIId() + " due to notif cancelled");
- }
- mp.release();
- synchronized(mQueueAudioFocusLock) {
- if (mAudioManagerWithAudioFocus != null) {
- if (DEBUG) { Log.d(mTag, "in STOP: abandonning AudioFocus"); }
- mAudioManagerWithAudioFocus.abandonAudioFocus(null);
- mAudioManagerWithAudioFocus = null;
- }
- }
- synchronized (mCompletionHandlingLock) {
- if ((mLooper != null) &&
- (mLooper.getThread().getState() != Thread.State.TERMINATED))
- {
- if (DEBUG) { Log.d(mTag, "in STOP: quitting looper "+ mLooper); }
- mLooper.quit();
- }
- }
- } else {
- Log.w(mTag, "STOP command without a player");
- }
- break;
- }
-
- synchronized (mCmdQueue) {
- if (mCmdQueue.size() == 0) {
- // nothing left to do, quit
- // doing this check after we're done prevents the case where they
- // added it during the operation from spawning two threads and
- // trying to do them in parallel.
- mThread = null;
- releaseWakeLock();
- return;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSessionLegacyHelperWrapper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSessionLegacyHelperWrapper.kt
new file mode 100644
index 0000000..9924369
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSessionLegacyHelperWrapper.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.media.controls.util
+
+import android.content.Context
+import android.media.session.MediaSessionLegacyHelper
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** Injectable wrapper around `MediaSessionLegacyHelper` functions */
+@SysUISingleton
+class MediaSessionLegacyHelperWrapper @Inject constructor() {
+ fun getHelper(context: Context): MediaSessionLegacyHelper {
+ return MediaSessionLegacyHelper.getHelper(context)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index a3d1d8c..d8824983 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -515,11 +515,13 @@
mSeekBar.setOnTouchListener((v, event) -> false);
updateIconAreaClickListener((v) -> {
if (device.getCurrentVolume() == 0) {
+ mController.logInteractionUnmuteDevice(device);
mSeekBar.setVolume(UNMUTE_DEFAULT_VOLUME);
mController.adjustVolume(device, UNMUTE_DEFAULT_VOLUME);
updateUnmutedVolumeIcon();
mIconAreaLayout.setOnTouchListener(((iconV, event) -> false));
} else {
+ mController.logInteractionMuteDevice(device);
mSeekBar.resetVolume();
mController.adjustVolume(device, 0);
updateMutedVolumeIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index d281f50..bb0e9d1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -837,6 +837,14 @@
mMetricLogger.logInteractionAdjustVolume(device);
}
+ void logInteractionMuteDevice(MediaDevice device) {
+ mMetricLogger.logInteractionMute(device);
+ }
+
+ void logInteractionUnmuteDevice(MediaDevice device) {
+ mMetricLogger.logInteractionUnmute(device);
+ }
+
String getPackageName() {
return mPackageName;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
index 412d1a3..ffd626a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
@@ -154,6 +154,38 @@
}
/**
+ * Do the metric logging of muting device.
+ */
+ public void logInteractionMute(MediaDevice source) {
+ if (DEBUG) {
+ Log.d(TAG, "logInteraction - Mute");
+ }
+
+ SysUiStatsLog.write(
+ SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
+ SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__MUTE,
+ getInteractionDeviceType(source),
+ getLoggingPackageName(),
+ source.isSuggestedDevice());
+ }
+
+ /**
+ * Do the metric logging of unmuting device.
+ */
+ public void logInteractionUnmute(MediaDevice source) {
+ if (DEBUG) {
+ Log.d(TAG, "logInteraction - Unmute");
+ }
+
+ SysUiStatsLog.write(
+ SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
+ SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__UNMUTE,
+ getInteractionDeviceType(source),
+ getLoggingPackageName(),
+ source.isSuggestedDevice());
+ }
+
+ /**
* Do the metric logging of content switching failure.
*
* @param deviceItemList media item list for device count updating
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index e134f7c..ae0ab84 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -25,6 +25,7 @@
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR;
import static android.view.InsetsSource.FLAG_SUPPRESS_SCRIM;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -1714,10 +1715,12 @@
private InsetsFrameProvider[] getInsetsFrameProvider(int insetsHeight, Context userContext) {
final InsetsFrameProvider navBarProvider =
- new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.navigationBars())
- .setInsetsSizeOverrides(new InsetsFrameProvider.InsetsSizeOverride[] {
- new InsetsFrameProvider.InsetsSizeOverride(
- TYPE_INPUT_METHOD, null)});
+ new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.navigationBars());
+ if (!ENABLE_HIDE_IME_CAPTION_BAR) {
+ navBarProvider.setInsetsSizeOverrides(new InsetsFrameProvider.InsetsSizeOverride[] {
+ new InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, null)
+ });
+ }
if (insetsHeight != -1 && !mEdgeBackGestureHandler.isButtonForcedVisible()) {
navBarProvider.setInsetsSize(Insets.of(0, 0, 0, insetsHeight));
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index 67927a4..cb87e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -22,13 +22,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTileView
import com.android.systemui.qs.external.TileServiceRequestController
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
+import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -37,10 +36,11 @@
/**
* Adapter to determine what real class to use for classes that depend on [QSHost].
- * * When [Flags.QS_PIPELINE_NEW_HOST] is off, all calls will be routed to [QSTileHost].
- * * When [Flags.QS_PIPELINE_NEW_HOST] is on, calls regarding the current set of tiles will be
- * routed to [CurrentTilesInteractor]. Other calls (like [createTileView]) will still be routed to
+ * * When [QSPipelineFlagsRepository.pipelineHostEnabled] is false, all calls will be routed to
* [QSTileHost].
+ * * When [QSPipelineFlagsRepository.pipelineHostEnabled] is true, calls regarding the current set
+ * of tiles will be routed to [CurrentTilesInteractor]. Other calls (like [createTileView]) will
+ * still be routed to [QSTileHost].
*
* This routing also includes dumps.
*/
@@ -53,7 +53,7 @@
private val context: Context,
private val tileServiceRequestControllerBuilder: TileServiceRequestController.Builder,
@Application private val scope: CoroutineScope,
- private val featureFlags: FeatureFlags,
+ flags: QSPipelineFlagsRepository,
dumpManager: DumpManager,
) : QSHost {
@@ -61,7 +61,7 @@
private const val TAG = "QSTileHost"
}
- private val useNewHost = featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)
+ private val useNewHost = flags.pipelineHostEnabled
@GuardedBy("callbacksMap") private val callbacksMap = mutableMapOf<QSHost.Callback, Job>()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 432147f..e57db56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -35,8 +35,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.nano.SystemUIProtoDump;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.qs.QSFactory;
@@ -50,6 +48,7 @@
import com.android.systemui.qs.nano.QsTileState;
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
+import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
@@ -119,7 +118,7 @@
private TileLifecycleManager.Factory mTileLifeCycleManagerFactory;
- private final FeatureFlags mFeatureFlags;
+ private final QSPipelineFlagsRepository mFeatureFlags;
@Inject
public QSTileHost(Context context,
@@ -135,7 +134,7 @@
CustomTileStatePersister customTileStatePersister,
TileLifecycleManager.Factory tileLifecycleManagerFactory,
UserFileManager userFileManager,
- FeatureFlags featureFlags
+ QSPipelineFlagsRepository featureFlags
) {
mContext = context;
mUserContext = context;
@@ -162,7 +161,7 @@
// finishes before creating any tiles.
tunerService.addTunable(this, TILES_SETTING);
// AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
- if (!mFeatureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)) {
+ if (!mFeatureFlags.getPipelineAutoAddEnabled()) {
mAutoTiles = autoTiles.get();
}
});
@@ -283,7 +282,7 @@
}
}
// Do not process tiles if the flag is enabled.
- if (mFeatureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
+ if (mFeatureFlags.getPipelineHostEnabled()) {
return;
}
if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
index 1f63f5d..b2111d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
@@ -16,8 +16,6 @@
package com.android.systemui.qs.dagger
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QSHostAdapter
import com.android.systemui.qs.QSTileHost
@@ -27,6 +25,7 @@
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedSharedPrefsRepository
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractorImpl
+import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -45,11 +44,11 @@
@Provides
@JvmStatic
fun providePanelInteractor(
- featureFlags: FeatureFlags,
+ featureFlags: QSPipelineFlagsRepository,
qsHost: QSTileHost,
panelInteractorImpl: PanelInteractorImpl
): PanelInteractor {
- return if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
+ return if (featureFlags.pipelineHostEnabled) {
panelInteractorImpl
} else {
qsHost
@@ -59,11 +58,11 @@
@Provides
@JvmStatic
fun provideCustomTileAddedRepository(
- featureFlags: FeatureFlags,
+ featureFlags: QSPipelineFlagsRepository,
qsHost: QSTileHost,
customTileAddedRepository: CustomTileAddedSharedPrefsRepository
): CustomTileAddedRepository {
- return if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
+ return if (featureFlags.pipelineHostEnabled) {
customTileAddedRepository
} else {
qsHost
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 5d02830..34d6233 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -36,7 +36,6 @@
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
-import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.IWindowManager;
@@ -190,13 +189,8 @@
if (updateIcon) {
mTile.setIcon(mDefaultIcon);
}
- // Update the label if there is no label or it is the default label.
- boolean updateLabel = mTile.getLabel() == null
- || TextUtils.equals(mTile.getLabel(), mDefaultLabel);
mDefaultLabel = info.loadLabel(pm);
- if (updateLabel) {
- mTile.setLabel(mDefaultLabel);
- }
+ mTile.setDefaultLabel(mDefaultLabel);
} catch (PackageManager.NameNotFoundException e) {
mDefaultIcon = null;
mDefaultLabel = null;
@@ -291,8 +285,8 @@
if (tile.getIcon() != null || overwriteNulls) {
mTile.setIcon(tile.getIcon());
}
- if (tile.getLabel() != null || overwriteNulls) {
- mTile.setLabel(tile.getLabel());
+ if (tile.getCustomLabel() != null || overwriteNulls) {
+ mTile.setLabel(tile.getCustomLabel());
}
if (tile.getSubtitle() != null || overwriteNulls) {
mTile.setSubtitle(tile.getSubtitle());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
index 021e632..a321eef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
@@ -113,7 +113,7 @@
return with(tile) {
JSONObject()
.put(STATE, state)
- .put(LABEL, label)
+ .put(LABEL, customLabel)
.put(SUBTITLE, subtitle)
.put(CONTENT_DESCRIPTION, contentDescription)
.put(STATE_DESCRIPTION, stateDescription)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 966f370..5a5e47a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -27,8 +27,6 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.nano.SystemUIProtoDump
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.qs.QSFactory
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.external.CustomTile
@@ -39,6 +37,7 @@
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
import com.android.systemui.qs.pipeline.domain.model.TileModel
+import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import com.android.systemui.qs.toProto
@@ -139,7 +138,7 @@
@Background private val backgroundDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
private val logger: QSPipelineLogger,
- featureFlags: FeatureFlags,
+ featureFlags: QSPipelineFlagsRepository,
) : CurrentTilesInteractor {
private val _currentSpecsAndTiles: MutableStateFlow<List<TileModel>> =
@@ -171,7 +170,7 @@
}
init {
- if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
+ if (featureFlags.pipelineHostEnabled) {
startTileCollection()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
index 224fc1a..0743ba0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
@@ -18,10 +18,9 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.qs.pipeline.domain.interactor.AutoAddInteractor
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
+import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import javax.inject.Inject
@SysUISingleton
@@ -30,14 +29,11 @@
constructor(
private val currentTilesInteractor: CurrentTilesInteractor,
private val autoAddInteractor: AutoAddInteractor,
- private val featureFlags: FeatureFlags,
+ private val featureFlags: QSPipelineFlagsRepository,
) : CoreStartable {
override fun start() {
- if (
- featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST) &&
- featureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)
- ) {
+ if (featureFlags.pipelineAutoAddEnabled) {
autoAddInteractor.init(currentTilesInteractor)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
new file mode 100644
index 0000000..551b0f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
@@ -0,0 +1,23 @@
+package com.android.systemui.qs.pipeline.shared
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+
+/** Encapsulate the different QS pipeline flags and their dependencies */
+@SysUISingleton
+class QSPipelineFlagsRepository
+@Inject
+constructor(
+ private val featureFlags: FeatureFlags,
+) {
+
+ /** @see Flags.QS_PIPELINE_NEW_HOST */
+ val pipelineHostEnabled: Boolean
+ get() = featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)
+
+ /** @see Flags.QS_PIPELINE_AUTO_ADD */
+ val pipelineAutoAddEnabled: Boolean
+ get() = pipelineHostEnabled && featureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
index 423fa80..1f9979a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
@@ -28,8 +28,6 @@
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
@@ -64,7 +62,6 @@
private val systemSettings: SystemSettings,
private val secureSettings: SecureSettings,
private val systemClock: SystemClock,
- private val featureFlags: FeatureFlags,
private val userTracker: UserTracker,
@Background private val backgroundDelayableExecutor: DelayableExecutor
) :
@@ -81,10 +78,6 @@
) {
private val icon = ResourceIcon.get(R.drawable.ic_qs_font_scaling)
- override fun isAvailable(): Boolean {
- return featureFlags.isEnabled(Flags.ENABLE_FONT_SCALING_TILE)
- }
-
override fun newTileState(): QSTile.State {
return QSTile.State()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 6e1ef91..c67078f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -128,12 +128,15 @@
private TextView mMobileTitleText;
private TextView mMobileSummaryText;
private TextView mSecondaryMobileTitleText;
- private TextView mSecondaryMobileSummaryText;
+ private TextView mSecondaryMobileSummaryText;
private TextView mAirplaneModeSummaryText;
private Switch mMobileDataToggle;
private View mMobileToggleDivider;
private Switch mWiFiToggle;
private Button mDoneButton;
+
+ @VisibleForTesting
+ protected Button mShareWifiButton;
private Button mAirplaneModeButton;
private Drawable mBackgroundOn;
private KeyguardStateController mKeyguard;
@@ -142,7 +145,6 @@
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mCanConfigMobileData;
private boolean mCanChangeWifiState;
-
// Wi-Fi entries
private int mWifiNetworkHeight;
@Nullable
@@ -236,6 +238,7 @@
mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
mDoneButton = mDialogView.requireViewById(R.id.done_button);
+ mShareWifiButton = mDialogView.requireViewById(R.id.share_wifi_button);
mAirplaneModeButton = mDialogView.requireViewById(R.id.apm_button);
mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
@@ -274,6 +277,7 @@
mConnectedWifListLayout.setVisibility(View.GONE);
mWifiRecyclerView.setVisibility(View.GONE);
mSeeAllLayout.setVisibility(View.GONE);
+ mShareWifiButton.setVisibility(View.GONE);
}
@Override
@@ -291,6 +295,7 @@
mSeeAllLayout.setOnClickListener(null);
mWiFiToggle.setOnCheckedChangeListener(null);
mDoneButton.setOnClickListener(null);
+ mShareWifiButton.setOnClickListener(null);
mAirplaneModeButton.setOnClickListener(null);
mInternetDialogController.onStop();
mInternetDialogFactory.destroyDialog();
@@ -309,7 +314,7 @@
* Update the internet dialog when receiving the callback.
*
* @param shouldUpdateMobileNetwork {@code true} for update the mobile network layout,
- * otherwise {@code false}.
+ * otherwise {@code false}.
*/
void updateDialog(boolean shouldUpdateMobileNetwork) {
if (DEBUG) {
@@ -366,6 +371,11 @@
mInternetDialogController.setWifiEnabled(isChecked);
});
mDoneButton.setOnClickListener(v -> dismiss());
+ mShareWifiButton.setOnClickListener(v -> {
+ if (mInternetDialogController.mayLaunchShareWifiSettings(mConnectedWifiEntry)) {
+ mUiEventLogger.log(InternetDialogEvent.SHARE_WIFI_QS_BUTTON_CLICKED);
+ }
+ });
mAirplaneModeButton.setOnClickListener(v -> {
mInternetDialogController.setAirplaneModeDisabled();
});
@@ -526,6 +536,7 @@
private void updateConnectedWifi(boolean isWifiEnabled, boolean isDeviceLocked) {
if (!isWifiEnabled || mConnectedWifiEntry == null || isDeviceLocked) {
mConnectedWifListLayout.setVisibility(View.GONE);
+ mShareWifiButton.setVisibility(View.GONE);
return;
}
mConnectedWifListLayout.setVisibility(View.VISIBLE);
@@ -535,6 +546,12 @@
mInternetDialogController.getInternetWifiDrawable(mConnectedWifiEntry));
mWifiSettingsIcon.setColorFilter(
mContext.getColor(R.color.connected_network_primary_color));
+ if (mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(
+ mConnectedWifiEntry) != null) {
+ mShareWifiButton.setVisibility(View.VISIBLE);
+ } else {
+ mShareWifiButton.setVisibility(View.GONE);
+ }
if (mSecondaryMobileNetworkLayout != null) {
mSecondaryMobileNetworkLayout.setVisibility(View.GONE);
@@ -683,7 +700,8 @@
mAlertDialog = new Builder(mContext)
.setTitle(R.string.mobile_data_disable_title)
.setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
- .setNegativeButton(android.R.string.cancel, (d, w) -> {})
+ .setNegativeButton(android.R.string.cancel, (d, w) -> {
+ })
.setPositiveButton(
com.android.internal.R.string.alert_windows_notification_turn_off_action,
(d, w) -> {
@@ -709,7 +727,8 @@
.setTitle(mContext.getString(R.string.auto_data_switch_disable_title, carrierName))
.setMessage(R.string.auto_data_switch_disable_message)
.setNegativeButton(R.string.auto_data_switch_dialog_negative_button,
- (d, w) -> {})
+ (d, w) -> {
+ })
.setPositiveButton(R.string.auto_data_switch_dialog_positive_button,
(d, w) -> {
mInternetDialogController
@@ -815,7 +834,10 @@
public enum InternetDialogEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "The Internet dialog became visible on the screen.")
- INTERNET_DIALOG_SHOW(843);
+ INTERNET_DIALOG_SHOW(843),
+
+ @UiEvent(doc = "The share wifi button is clicked.")
+ SHARE_WIFI_QS_BUTTON_CLICKED(1462);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index ec12580..3c0b87d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -73,6 +73,7 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.settingslib.wifi.WifiUtils;
+import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogLaunchAnimator;
@@ -1318,6 +1319,18 @@
return mWifiIconInjector;
}
+ boolean mayLaunchShareWifiSettings(WifiEntry wifiEntry) {
+ Intent intent = getConfiguratorQrCodeGeneratorIntentOrNull(wifiEntry);
+ if (intent == null) {
+ return false;
+ }
+ if (mCallback != null) {
+ mCallback.dismissDialog();
+ }
+ mActivityStarter.startActivity(intent, false /* dismissShade */);
+ return true;
+ }
+
interface InternetDialogCallback {
void onRefreshCarrierInfo();
@@ -1403,4 +1416,17 @@
}
}, SHORT_DURATION_TIMEOUT);
}
+
+ Intent getConfiguratorQrCodeGeneratorIntentOrNull(WifiEntry wifiEntry) {
+ if (!mFeatureFlags.isEnabled(Flags.SHARE_WIFI_QS_BUTTON) || wifiEntry == null
+ || mWifiManager == null || !wifiEntry.canShare()) {
+ return null;
+ }
+ Intent intent = new Intent();
+ intent.setAction(WifiDppIntentHelper.ACTION_CONFIGURATOR_AUTH_QR_CODE_GENERATOR);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ WifiDppIntentHelper.setConfiguratorIntentExtra(intent, mWifiManager,
+ wifiEntry.getWifiConfiguration());
+ return intent;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index e8683fb..fb99775 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -63,6 +63,7 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setDialogTitle(R.string.screenrecord_permission_dialog_title)
+ setTitle(R.string.screenrecord_title)
setStartButtonText(R.string.screenrecord_permission_dialog_continue)
setStartButtonOnClickListener { v: View? ->
onStartRecordingClicked?.run()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 132cd61..014093d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -985,6 +985,7 @@
// Make sure the clock is in the correct position after the unlock animation
// so that it's not in the wrong place when we show the keyguard again.
positionClockAndNotifications(true /* forceClockUpdate */);
+ mScrimController.onUnlockAnimationFinished();
}
private void unlockAnimationStarted(
@@ -1243,6 +1244,13 @@
mKeyguardStatusViewController.init();
}
mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
+ mKeyguardStatusViewController.getView().addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ int oldHeight = oldBottom - oldTop;
+ if (v.getHeight() != oldHeight) {
+ mNotificationStackScrollLayoutController.animateNextTopPaddingChange();
+ }
+ });
updateClockAppearance();
@@ -3198,6 +3206,11 @@
}
}
+ @Override
+ public void performHapticFeedback(int constant) {
+ mVibratorHelper.performHapticFeedback(mView, constant);
+ }
+
private class ShadeHeadsUpTrackerImpl implements ShadeHeadsUpTracker {
@Override
public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 832a25b..798f2d5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -21,8 +21,6 @@
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.app.StatusBarManager;
-import android.media.AudioManager;
-import android.media.session.MediaSessionLegacyHelper;
import android.os.PowerManager;
import android.util.Log;
import android.view.GestureDetector;
@@ -47,6 +45,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.TransitionState;
@@ -101,13 +100,21 @@
private final NotificationInsetsController mNotificationInsetsController;
private final boolean mIsTrackpadCommonEnabled;
private final FeatureFlags mFeatureFlags;
+ private final KeyEventInteractor mKeyEventInteractor;
private GestureDetector mPulsingWakeupGestureHandler;
private GestureDetector mDreamingWakeupGestureHandler;
private View mBrightnessMirror;
private boolean mTouchActive;
private boolean mTouchCancelled;
private MotionEvent mDownEvent;
+ // TODO rename to mLaunchAnimationRunning
private boolean mExpandAnimationRunning;
+ /**
+ * When mExpandAnimationRunning is true and the touch dispatcher receives a down even after
+ * uptime exceeds this, the dispatcher will stop blocking touches for the launch animation,
+ * which has presumabely not completed due to an error.
+ */
+ private long mLaunchAnimationTimeout;
private NotificationStackScrollLayout mStackScrollLayout;
private PhoneStatusBarViewController mStatusBarViewController;
private final CentralSurfaces mService;
@@ -164,7 +171,8 @@
FeatureFlags featureFlags,
SystemClock clock,
BouncerMessageInteractor bouncerMessageInteractor,
- BouncerLogger bouncerLogger) {
+ BouncerLogger bouncerLogger,
+ KeyEventInteractor keyEventInteractor) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -190,6 +198,7 @@
mNotificationInsetsController = notificationInsetsController;
mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON);
mFeatureFlags = featureFlags;
+ mKeyEventInteractor = keyEventInteractor;
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -278,7 +287,12 @@
return logDownDispatch(ev, "touch cancelled", false);
}
if (mExpandAnimationRunning) {
- return logDownDispatch(ev, "expand animation running", false);
+ if (isDown && mClock.uptimeMillis() > mLaunchAnimationTimeout) {
+ mShadeLogger.d("NSWVC: launch animation timed out");
+ setExpandAnimationRunning(false);
+ } else {
+ return logDownDispatch(ev, "expand animation running", false);
+ }
}
if (mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
@@ -457,44 +471,17 @@
@Override
public boolean interceptMediaKey(KeyEvent event) {
- return mService.interceptMediaKey(event);
+ return mKeyEventInteractor.interceptMediaKey(event);
}
@Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
- return mService.dispatchKeyEventPreIme(event);
+ return mKeyEventInteractor.dispatchKeyEventPreIme(event);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_BACK:
- if (!down) {
- mBackActionInteractor.onBackRequested();
- }
- return true;
- case KeyEvent.KEYCODE_MENU:
- if (!down) {
- return mService.onMenuPressed();
- }
- break;
- case KeyEvent.KEYCODE_SPACE:
- if (!down) {
- return mService.onSpacePressed();
- }
- break;
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- case KeyEvent.KEYCODE_VOLUME_UP:
- if (mStatusBarStateController.isDozing()) {
- MediaSessionLegacyHelper.getHelper(mView.getContext())
- .sendVolumeKeyEvent(
- event, AudioManager.USE_DEFAULT_STREAM_TYPE, true);
- return true;
- }
- break;
- }
- return false;
+ return mKeyEventInteractor.dispatchKeyEvent(event);
}
});
@@ -555,8 +542,12 @@
pw.println(mTouchActive);
}
- private void setExpandAnimationRunning(boolean running) {
+ @VisibleForTesting
+ void setExpandAnimationRunning(boolean running) {
if (mExpandAnimationRunning != running) {
+ if (running) {
+ mLaunchAnimationTimeout = mClock.uptimeMillis() + 5000;
+ }
mExpandAnimationRunning = running;
mNotificationShadeWindowController.setLaunchingActivity(mExpandAnimationRunning);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index bea12de..6564118 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -263,9 +263,11 @@
resources.getDimensionPixelSize(
R.dimen.shade_header_system_icons_padding_start
),
- systemIcons.paddingTop,
+ resources.getDimensionPixelSize(R.dimen.shade_header_system_icons_padding_top),
resources.getDimensionPixelSize(R.dimen.shade_header_system_icons_padding_end),
- systemIcons.paddingBottom
+ resources.getDimensionPixelSize(
+ R.dimen.shade_header_system_icons_padding_bottom
+ )
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index d5b5c87..182a676 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -248,6 +248,16 @@
/** Starts tracking a shade expansion gesture that originated from the status bar. */
fun startTrackingExpansionFromStatusBar()
+ /**
+ * Performs haptic feedback from a view with a haptic feedback constant.
+ *
+ * The implementation of this method should use the [android.view.View.performHapticFeedback]
+ * method with the provided constant.
+ *
+ * @param[constant] One of [android.view.HapticFeedbackConstants]
+ */
+ fun performHapticFeedback(constant: Int)
+
// ******* End Keyguard Section *********
/** Returns the ShadeHeadsUpTracker. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 287ac52..09b74b2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -86,6 +86,8 @@
return false
}
override fun startTrackingExpansionFromStatusBar() {}
+ override fun performHapticFeedback(constant: Int) {}
+
override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl()
override val shadeFoldAnimator = ShadeFoldAnimatorEmptyImpl()
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
index 8586828..8612cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrier.java
@@ -34,6 +34,7 @@
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
+import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView;
import com.android.systemui.util.LargeScreenUtils;
import java.util.Objects;
@@ -44,6 +45,7 @@
private TextView mCarrierText;
private ImageView mMobileSignal;
private ImageView mMobileRoaming;
+ private ModernShadeCarrierGroupMobileView mModernMobileView;
private View mSpacer;
@Nullable
private CellSignalState mLastSignalState;
@@ -77,6 +79,23 @@
updateResources();
}
+ /** Removes a ModernStatusBarMobileView from the ViewGroup. */
+ public void removeModernMobileView() {
+ if (mModernMobileView != null) {
+ removeView(mModernMobileView);
+ mModernMobileView = null;
+ }
+ }
+
+ /** Adds a ModernStatusBarMobileView to the ViewGroup. */
+ public void addModernMobileView(ModernShadeCarrierGroupMobileView mobileView) {
+ mModernMobileView = mobileView;
+ mMobileGroup.setVisibility(View.GONE);
+ mSpacer.setVisibility(View.GONE);
+ mCarrierText.setVisibility(View.GONE);
+ addView(mobileView);
+ }
+
/**
* Update the state of this view
* @param state the current state of the signal for this view
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
index ad49b26..98d8a53 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -16,6 +16,7 @@
package com.android.systemui.shade.carrier;
+import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
import android.annotation.MainThread;
@@ -46,8 +47,17 @@
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
+import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
+import com.android.systemui.statusbar.phone.StatusBarLocation;
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
+import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder;
+import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView;
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel;
import com.android.systemui.util.CarrierConfigTracker;
+import java.util.List;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -62,12 +72,16 @@
private final ActivityStarter mActivityStarter;
private final Handler mBgHandler;
+ private final Context mContext;
private final NetworkController mNetworkController;
private final CarrierTextManager mCarrierTextManager;
private final TextView mNoSimTextView;
// Non final for testing
private H mMainHandler;
private final Callback mCallback;
+ private final MobileIconsViewModel mMobileIconsViewModel;
+ private final MobileContextProvider mMobileContextProvider;
+ private final StatusBarPipelineFlags mStatusBarPipelineFlags;
private boolean mListening;
private final CellSignalState[] mInfos =
new CellSignalState[SIM_SLOTS];
@@ -91,7 +105,7 @@
Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
return;
}
- if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ if (slotIndex == INVALID_SIM_SLOT_INDEX) {
Log.e(TAG, "Invalid SIM slot index for subscription: " + indicators.subId);
return;
}
@@ -129,15 +143,25 @@
}
}
- private ShadeCarrierGroupController(ShadeCarrierGroup view, ActivityStarter activityStarter,
- @Background Handler bgHandler, @Main Looper mainLooper,
+ private ShadeCarrierGroupController(
+ ShadeCarrierGroup view,
+ ActivityStarter activityStarter,
+ @Background Handler bgHandler,
+ @Main Looper mainLooper,
NetworkController networkController,
- CarrierTextManager.Builder carrierTextManagerBuilder, Context context,
- CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
-
+ CarrierTextManager.Builder carrierTextManagerBuilder,
+ Context context,
+ CarrierConfigTracker carrierConfigTracker,
+ SlotIndexResolver slotIndexResolver,
+ MobileUiAdapter mobileUiAdapter,
+ MobileContextProvider mobileContextProvider,
+ StatusBarPipelineFlags statusBarPipelineFlags
+ ) {
+ mContext = context;
mActivityStarter = activityStarter;
mBgHandler = bgHandler;
mNetworkController = networkController;
+ mStatusBarPipelineFlags = statusBarPipelineFlags;
mCarrierTextManager = carrierTextManagerBuilder
.setShowAirplaneMode(false)
.setShowMissingSim(false)
@@ -162,6 +186,14 @@
mCarrierGroups[1] = view.getCarrier2View();
mCarrierGroups[2] = view.getCarrier3View();
+ mMobileContextProvider = mobileContextProvider;
+ mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel();
+
+ if (mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()) {
+ mobileUiAdapter.setShadeCarrierGroupController(this);
+ MobileIconsBinder.bind(view, mMobileIconsViewModel);
+ }
+
mCarrierDividers[0] = view.getCarrierDivider1();
mCarrierDividers[1] = view.getCarrierDivider2();
@@ -193,6 +225,50 @@
});
}
+ /** Updates the number of visible mobile icons using the new pipeline. */
+ public void updateModernMobileIcons(List<Integer> subIds) {
+ if (!mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()) {
+ Log.d(TAG, "ignoring new pipeline callback because new mobile icon is disabled");
+ return;
+ }
+
+ for (ShadeCarrier carrier : mCarrierGroups) {
+ carrier.removeModernMobileView();
+ }
+
+ List<IconData> iconDataList = processSubIdList(subIds);
+
+ for (IconData iconData : iconDataList) {
+ ShadeCarrier carrier = mCarrierGroups[iconData.slotIndex];
+
+ Context mobileContext =
+ mMobileContextProvider.getMobileContextForSub(iconData.subId, mContext);
+ ModernShadeCarrierGroupMobileView modernMobileView = ModernShadeCarrierGroupMobileView
+ .constructAndBind(
+ mobileContext,
+ mMobileIconsViewModel.getLogger(),
+ "mobile_carrier_shade_group",
+ (ShadeCarrierGroupMobileIconViewModel) mMobileIconsViewModel
+ .viewModelForSub(iconData.subId,
+ StatusBarLocation.SHADE_CARRIER_GROUP)
+ );
+ carrier.addModernMobileView(modernMobileView);
+ }
+ }
+
+ @VisibleForTesting
+ List<IconData> processSubIdList(List<Integer> subIds) {
+ return subIds
+ .stream()
+ .limit(SIM_SLOTS)
+ .map(subId -> new IconData(subId, getSlotIndex(subId)))
+ .filter(iconData ->
+ iconData.slotIndex < SIM_SLOTS
+ && iconData.slotIndex != INVALID_SIM_SLOT_INDEX
+ )
+ .toList();
+ }
+
@VisibleForTesting
protected int getSlotIndex(int subscriptionId) {
return mSlotIndexResolver.getSlotIndex(subscriptionId);
@@ -269,8 +345,12 @@
}
}
- for (int i = 0; i < SIM_SLOTS; i++) {
- mCarrierGroups[i].updateState(mInfos[i], singleCarrier);
+ if (mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()) {
+ Log.d(TAG, "ignoring old pipeline callback because new mobile icon is enabled");
+ } else {
+ for (int i = 0; i < SIM_SLOTS; i++) {
+ mCarrierGroups[i].updateState(mInfos[i], singleCarrier);
+ }
}
mCarrierDividers[0].setVisibility(
@@ -306,7 +386,7 @@
Log.w(TAG, "updateInfoCarrier - slot: " + slot);
continue;
}
- if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ if (slot == INVALID_SIM_SLOT_INDEX) {
Log.e(TAG,
"Invalid SIM slot index for subscription: "
+ info.subscriptionIds[i]);
@@ -385,12 +465,24 @@
private final Context mContext;
private final CarrierConfigTracker mCarrierConfigTracker;
private final SlotIndexResolver mSlotIndexResolver;
+ private final MobileUiAdapter mMobileUiAdapter;
+ private final MobileContextProvider mMobileContextProvider;
+ private final StatusBarPipelineFlags mStatusBarPipelineFlags;
@Inject
- public Builder(ActivityStarter activityStarter, @Background Handler handler,
- @Main Looper looper, NetworkController networkController,
- CarrierTextManager.Builder carrierTextControllerBuilder, Context context,
- CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
+ public Builder(
+ ActivityStarter activityStarter,
+ @Background Handler handler,
+ @Main Looper looper,
+ NetworkController networkController,
+ CarrierTextManager.Builder carrierTextControllerBuilder,
+ Context context,
+ CarrierConfigTracker carrierConfigTracker,
+ SlotIndexResolver slotIndexResolver,
+ MobileUiAdapter mobileUiAdapter,
+ MobileContextProvider mobileContextProvider,
+ StatusBarPipelineFlags statusBarPipelineFlags
+ ) {
mActivityStarter = activityStarter;
mHandler = handler;
mLooper = looper;
@@ -399,6 +491,9 @@
mContext = context;
mCarrierConfigTracker = carrierConfigTracker;
mSlotIndexResolver = slotIndexResolver;
+ mMobileUiAdapter = mobileUiAdapter;
+ mMobileContextProvider = mobileContextProvider;
+ mStatusBarPipelineFlags = statusBarPipelineFlags;
}
public Builder setShadeCarrierGroup(ShadeCarrierGroup view) {
@@ -407,9 +502,20 @@
}
public ShadeCarrierGroupController build() {
- return new ShadeCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
- mNetworkController, mCarrierTextControllerBuilder, mContext,
- mCarrierConfigTracker, mSlotIndexResolver);
+ return new ShadeCarrierGroupController(
+ mView,
+ mActivityStarter,
+ mHandler,
+ mLooper,
+ mNetworkController,
+ mCarrierTextControllerBuilder,
+ mContext,
+ mCarrierConfigTracker,
+ mSlotIndexResolver,
+ mMobileUiAdapter,
+ mMobileContextProvider,
+ mStatusBarPipelineFlags
+ );
}
}
@@ -448,4 +554,15 @@
return SubscriptionManager.getSlotIndex(subscriptionId);
}
}
+
+ @VisibleForTesting
+ static class IconData {
+ public final int subId;
+ public final int slotIndex;
+
+ IconData(int subId, int slotIndex) {
+ this.subId = subId;
+ this.slotIndex = slotIndex;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
index d3c19b7..5042f1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
@@ -31,56 +31,68 @@
) {
fun logInitialClick(
entry: NotificationEntry?,
+ index: Integer?,
pendingIntent: PendingIntent
) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = entry?.key
str2 = entry?.ranking?.channel?.id
- str3 = pendingIntent.intent.toString()
+ str3 = pendingIntent.toString()
+ int1 = index?.toInt() ?: Int.MIN_VALUE
}, {
- "ACTION CLICK $str1 (channel=$str2) for pending intent $str3"
+ "ACTION CLICK $str1 (channel=$str2) for pending intent $str3 at index $int1"
})
}
fun logRemoteInputWasHandled(
- entry: NotificationEntry?
+ entry: NotificationEntry?,
+ index: Int?
) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = entry?.key
+ int1 = index ?: Int.MIN_VALUE
}, {
- " [Action click] Triggered remote input (for $str1))"
+ " [Action click] Triggered remote input (for $str1) at index $int1"
})
}
fun logStartingIntentWithDefaultHandler(
entry: NotificationEntry?,
- pendingIntent: PendingIntent
+ pendingIntent: PendingIntent,
+ index: Int?
) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = entry?.key
- str2 = pendingIntent.intent.toString()
+ str2 = pendingIntent.toString()
+ int1 = index ?: Int.MIN_VALUE
}, {
- " [Action click] Launching intent $str2 via default handler (for $str1)"
+ " [Action click] Launching intent $str2 via default handler (for $str1 at index $int1)"
})
}
fun logWaitingToCloseKeyguard(
- pendingIntent: PendingIntent
+ pendingIntent: PendingIntent,
+ index: Int?
) {
buffer.log(TAG, LogLevel.DEBUG, {
- str1 = pendingIntent.intent.toString()
+ str1 = pendingIntent.toString()
+ int1 = index ?: Int.MIN_VALUE
}, {
- " [Action click] Intent $str1 launches an activity, dismissing keyguard first..."
+ " [Action click] Intent $str1 at index $int1 launches an activity, dismissing " +
+ "keyguard first..."
})
}
fun logKeyguardGone(
- pendingIntent: PendingIntent
+ pendingIntent: PendingIntent,
+ index: Int?
) {
buffer.log(TAG, LogLevel.DEBUG, {
- str1 = pendingIntent.intent.toString()
+ str1 = pendingIntent.toString()
+ int1 = index ?: Int.MIN_VALUE
}, {
- " [Action click] Keyguard dismissed, calling default handler for intent $str1"
+ " [Action click] Keyguard dismissed, calling default handler for intent $str1 at " +
+ "index $int1"
})
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt
index abf81c5..692a997 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt
@@ -2,10 +2,12 @@
import android.app.Notification
import android.os.RemoteException
+import android.util.Log
import com.android.internal.statusbar.IStatusBarService
import com.android.internal.statusbar.NotificationVisibility
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.util.Assert
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -21,7 +23,8 @@
@SysUISingleton
public class NotificationClickNotifier @Inject constructor(
val barService: IStatusBarService,
- @Main val mainExecutor: Executor
+ @Main val mainExecutor: Executor,
+ @UiBackground val backgroundExecutor: Executor
) {
val listeners = mutableListOf<NotificationInteractionListener>()
@@ -48,13 +51,14 @@
visibility: NotificationVisibility,
generatedByAssistant: Boolean
) {
- try {
- barService.onNotificationActionClick(
- key, actionIndex, action, visibility, generatedByAssistant)
- } catch (e: RemoteException) {
- // nothing
+ backgroundExecutor.execute {
+ try {
+ barService.onNotificationActionClick(
+ key, actionIndex, action, visibility, generatedByAssistant)
+ } catch (e: RemoteException) {
+ // nothing
+ }
}
-
mainExecutor.execute {
notifyListenersAboutInteraction(key)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index da84afe..8089fd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -119,11 +119,14 @@
mPowerInteractor.wakeUpIfDozing(
"NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE);
+ Integer actionIndex = (Integer)
+ view.getTag(com.android.internal.R.id.notification_action_index_tag);
+
final NotificationEntry entry = getNotificationForParent(view.getParent());
- mLogger.logInitialClick(entry, pendingIntent);
+ mLogger.logInitialClick(entry, actionIndex, pendingIntent);
if (handleRemoteInput(view, pendingIntent)) {
- mLogger.logRemoteInputWasHandled(entry);
+ mLogger.logRemoteInputWasHandled(entry, actionIndex);
return true;
}
@@ -141,9 +144,9 @@
}
Notification.Action action = getActionFromView(view, entry, pendingIntent);
return mCallback.handleRemoteViewClick(view, pendingIntent,
- action == null ? false : action.isAuthenticationRequired(), () -> {
+ action == null ? false : action.isAuthenticationRequired(), actionIndex, () -> {
Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
- mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent);
+ mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent, actionIndex);
boolean started = RemoteViews.startPendingIntent(view, pendingIntent, options);
if (started) releaseNotificationIfKeptForRemoteInputHistory(entry);
return started;
@@ -692,11 +695,13 @@
* @param view
* @param pendingIntent
* @param appRequestedAuth
+ * @param actionIndex
* @param defaultHandler
* @return true iff the click was handled
*/
boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
- boolean appRequestedAuth, ClickHandler defaultHandler);
+ boolean appRequestedAuth, @Nullable Integer actionIndex,
+ ClickHandler defaultHandler);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index d6a14604..6dd24ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -48,8 +48,10 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardClockSwitch;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
+import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
@@ -420,6 +422,25 @@
}
}
+ /** Returns the id of the currently rendering clock */
+ public String getClockId() {
+ if (mView == null) {
+ return KeyguardClockSwitch.MISSING_CLOCK_ID;
+ }
+
+ View clockSwitch = mView.findViewById(R.id.keyguard_clock_container);
+ if (clockSwitch == null) {
+ Log.e(TAG, "Clock container was missing");
+ return KeyguardClockSwitch.MISSING_CLOCK_ID;
+ }
+ if (!(clockSwitch instanceof KeyguardClockSwitch)) {
+ Log.e(TAG, "Clock container was incorrect type: " + clockSwitch);
+ return KeyguardClockSwitch.MISSING_CLOCK_ID;
+ }
+
+ return ((KeyguardClockSwitch) clockSwitch).getClockId();
+ }
+
private void beginInteractionJankMonitor() {
final boolean shouldPost =
(mIsDozing && mDozeAmount == 0) || (!mIsDozing && mDozeAmount == 1);
@@ -429,6 +450,7 @@
Choreographer.CALLBACK_ANIMATION, this::beginInteractionJankMonitor, null);
} else {
Configuration.Builder builder = Configuration.Builder.withView(getCujType(), mView)
+ .setTag(getClockId())
.setDeferMonitorForAnimationStart(false);
mInteractionJankMonitor.begin(builder);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
index ed80f33..5558ab1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
@@ -2,13 +2,17 @@
# Bug component: 78010
-aaliomer@google.com
+aioana@google.com
aroederer@google.com
+iyz@google.com
jeffdq@google.com
juliacr@google.com
juliatuttle@google.com
+kurucz@google.com
+liuyining@google.com
lynhan@google.com
-steell@google.com
+matiashe@google.com
+valiiftime@google.com
yurilin@google.com
per-file MediaNotificationProcessor.java = ethibodeau@google.com, asc@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
new file mode 100644
index 0000000..26dfe3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
@@ -0,0 +1,684 @@
+/*
+ * 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.icon.ui.viewbinder
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Rect
+import android.os.Bundle
+import android.os.Trace
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import androidx.annotation.ColorInt
+import androidx.annotation.VisibleForTesting
+import androidx.collection.ArrayMap
+import com.android.app.animation.Interpolators
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.internal.util.ContrastColorUtil
+import com.android.settingslib.Utils
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.demomode.DemoMode
+import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.ViewRefactorFlag
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.CrossFadeHelper
+import com.android.systemui.statusbar.NotificationListener
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.NotificationShelfController
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.NotificationUtils
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
+import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.NotificationIconAreaController
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.wm.shell.bubbles.Bubbles
+import java.util.Optional
+import java.util.function.Function
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+
+/**
+ * Controller class for [NotificationIconContainer]. This implementation serves as a temporary
+ * wrapper around [NotificationIconContainerViewBinder], so that external code can continue to
+ * depend on the [NotificationIconAreaController] interface. Once
+ * [LegacyNotificationIconAreaControllerImpl] is removed, this class can go away and the ViewBinder
+ * can be used directly.
+ */
+@SysUISingleton
+class NotificationIconAreaControllerViewBinderWrapperImpl
+@Inject
+constructor(
+ private val context: Context,
+ private val statusBarStateController: StatusBarStateController,
+ private val wakeUpCoordinator: NotificationWakeUpCoordinator,
+ private val bypassController: KeyguardBypassController,
+ private val mediaManager: NotificationMediaManager,
+ notificationListener: NotificationListener,
+ private val dozeParameters: DozeParameters,
+ private val sectionStyleProvider: SectionStyleProvider,
+ private val bubblesOptional: Optional<Bubbles>,
+ demoModeController: DemoModeController,
+ darkIconDispatcher: DarkIconDispatcher,
+ featureFlags: FeatureFlags,
+ private val statusBarWindowController: StatusBarWindowController,
+ private val screenOffAnimationController: ScreenOffAnimationController,
+ private val shelfIconsViewModel: NotificationIconContainerShelfViewModel,
+ private val statusBarIconsViewModel: NotificationIconContainerStatusBarViewModel,
+ private val aodIconsViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
+) :
+ NotificationIconAreaController,
+ DarkIconDispatcher.DarkReceiver,
+ StatusBarStateController.StateListener,
+ NotificationWakeUpCoordinator.WakeUpListener,
+ DemoMode {
+
+ private val contrastColorUtil: ContrastColorUtil = ContrastColorUtil.getInstance(context)
+ private val updateStatusBarIcons = Runnable { updateStatusBarIcons() }
+ private val shelfRefactor = ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR)
+ private val tintAreas = ArrayList<Rect>()
+
+ private var iconSize = 0
+ private var iconHPadding = 0
+ private var iconTint = Color.WHITE
+ private var notificationEntries = listOf<ListEntry>()
+ private var notificationIconArea: View? = null
+ private var notificationIcons: NotificationIconContainer? = null
+ private var shelfIcons: NotificationIconContainer? = null
+ private var aodIcons: NotificationIconContainer? = null
+ private var aodBindJob: DisposableHandle? = null
+ private var aodIconAppearTranslation = 0
+ private var animationsEnabled = false
+ private var aodIconTint = 0
+ private var aodIconsVisible = false
+ private var showLowPriority = true
+
+ @VisibleForTesting
+ val settingsListener: NotificationListener.NotificationSettingsListener =
+ object : NotificationListener.NotificationSettingsListener {
+ override fun onStatusBarIconsBehaviorChanged(hideSilentStatusIcons: Boolean) {
+ showLowPriority = !hideSilentStatusIcons
+ updateStatusBarIcons()
+ }
+ }
+
+ init {
+ statusBarStateController.addCallback(this)
+ wakeUpCoordinator.addListener(this)
+ demoModeController.addCallback(this)
+ notificationListener.addNotificationSettingsListener(settingsListener)
+ initializeNotificationAreaViews(context)
+ reloadAodColor()
+ darkIconDispatcher.addDarkReceiver(this)
+ }
+
+ @VisibleForTesting
+ fun shouldShowLowPriorityIcons(): Boolean {
+ return showLowPriority
+ }
+
+ /** Called by the Keyguard*ViewController whose view contains the aod icons. */
+ override fun setupAodIcons(aodIcons: NotificationIconContainer) {
+ val changed = this.aodIcons != null && aodIcons !== this.aodIcons
+ if (changed) {
+ this.aodIcons!!.setAnimationsEnabled(false)
+ this.aodIcons!!.removeAllViews()
+ aodBindJob?.dispose()
+ }
+ this.aodIcons = aodIcons
+ this.aodIcons!!.setOnLockScreen(true)
+ aodBindJob = NotificationIconContainerViewBinder.bind(aodIcons, aodIconsViewModel)
+ updateAodIconsVisibility(animate = false, forceUpdate = changed)
+ updateAnimations()
+ if (changed) {
+ updateAodNotificationIcons()
+ }
+ updateIconLayoutParams(context)
+ }
+
+ override fun setupShelf(notificationShelfController: NotificationShelfController) =
+ NotificationShelfViewBinderWrapperControllerImpl.unsupported
+
+ override fun setShelfIcons(icons: NotificationIconContainer) {
+ if (shelfRefactor.expectEnabled()) {
+ NotificationIconContainerViewBinder.bind(icons, shelfIconsViewModel)
+ shelfIcons = icons
+ }
+ }
+
+ override fun onDensityOrFontScaleChanged(context: Context) {
+ updateIconLayoutParams(context)
+ }
+
+ /** Returns the view that represents the notification area. */
+ override fun getNotificationInnerAreaView(): View? {
+ return notificationIconArea
+ }
+
+ /**
+ * See [com.android.systemui.statusbar.policy.DarkIconDispatcher.setIconsDarkArea]. Sets the
+ * color that should be used to tint any icons in the notification area.
+ *
+ * @param tintAreas the areas in which to tint the icons, specified in screen coordinates
+ * @param darkIntensity
+ */
+ override fun onDarkChanged(tintAreas: ArrayList<Rect>, darkIntensity: Float, iconTint: Int) {
+ this.tintAreas.clear()
+ this.tintAreas.addAll(tintAreas)
+ if (DarkIconDispatcher.isInAreas(tintAreas, notificationIconArea)) {
+ this.iconTint = iconTint
+ }
+ applyNotificationIconsTint()
+ }
+
+ /** Updates the notifications with the given list of notifications to display. */
+ override fun updateNotificationIcons(entries: List<ListEntry>) {
+ notificationEntries = entries
+ updateNotificationIcons()
+ }
+
+ private fun updateStatusBarIcons() {
+ updateIconsForLayout(
+ { entry: NotificationEntry -> entry.icons.statusBarIcon },
+ notificationIcons,
+ showAmbient = false /* showAmbient */,
+ showLowPriority = showLowPriority,
+ hideDismissed = true /* hideDismissed */,
+ hideRepliedMessages = true /* hideRepliedMessages */,
+ hideCurrentMedia = false /* hideCurrentMedia */,
+ hidePulsing = false /* hidePulsing */
+ )
+ }
+
+ override fun updateAodNotificationIcons() {
+ if (aodIcons == null) {
+ return
+ }
+ updateIconsForLayout(
+ { entry: NotificationEntry -> entry.icons.aodIcon },
+ aodIcons,
+ showAmbient = false /* showAmbient */,
+ showLowPriority = true /* showLowPriority */,
+ hideDismissed = true /* hideDismissed */,
+ hideRepliedMessages = true /* hideRepliedMessages */,
+ hideCurrentMedia = true /* hideCurrentMedia */,
+ hidePulsing = bypassController.bypassEnabled /* hidePulsing */
+ )
+ }
+
+ override fun showIconIsolated(icon: StatusBarIconView?, animated: Boolean) {
+ notificationIcons!!.showIconIsolated(icon, animated)
+ }
+
+ override fun setIsolatedIconLocation(iconDrawingRect: Rect, requireStateUpdate: Boolean) {
+ notificationIcons!!.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate)
+ }
+
+ override fun onDozingChanged(isDozing: Boolean) {
+ if (aodIcons == null) {
+ return
+ }
+ val animate = (dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking)
+ aodIcons!!.setDozing(isDozing, animate, 0)
+ }
+
+ override fun setAnimationsEnabled(enabled: Boolean) {
+ animationsEnabled = enabled
+ updateAnimations()
+ }
+
+ override fun onStateChanged(newState: Int) {
+ updateAodIconsVisibility(animate = false, forceUpdate = false)
+ updateAnimations()
+ }
+
+ override fun onThemeChanged() {
+ reloadAodColor()
+ updateAodIconColors()
+ }
+
+ override fun getHeight(): Int {
+ return if (aodIcons == null) 0 else aodIcons!!.height
+ }
+
+ @VisibleForTesting
+ fun appearAodIcons() {
+ if (aodIcons == null) {
+ return
+ }
+ if (screenOffAnimationController.shouldAnimateAodIcons()) {
+ aodIcons!!.translationY = -aodIconAppearTranslation.toFloat()
+ aodIcons!!.alpha = 0f
+ animateInAodIconTranslation()
+ aodIcons!!
+ .animate()
+ .alpha(1f)
+ .setInterpolator(Interpolators.LINEAR)
+ .setDuration(AOD_ICONS_APPEAR_DURATION)
+ .start()
+ } else {
+ aodIcons!!.alpha = 1.0f
+ aodIcons!!.translationY = 0f
+ }
+ }
+
+ override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
+ var animate = true
+ if (!bypassController.bypassEnabled) {
+ animate = dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking
+ // We only want the appear animations to happen when the notifications get fully hidden,
+ // since otherwise the unhide animation overlaps
+ animate = animate and isFullyHidden
+ }
+ updateAodIconsVisibility(animate, false /* force */)
+ updateAodNotificationIcons()
+ updateAodIconColors()
+ }
+
+ override fun onPulseExpansionChanged(expandingChanged: Boolean) {
+ if (expandingChanged) {
+ updateAodIconsVisibility(animate = true, forceUpdate = false)
+ }
+ }
+
+ override fun demoCommands(): List<String> {
+ val commands = ArrayList<String>()
+ commands.add(DemoMode.COMMAND_NOTIFICATIONS)
+ return commands
+ }
+
+ override fun dispatchDemoCommand(command: String, args: Bundle) {
+ if (notificationIconArea != null) {
+ val visible = args.getString("visible")
+ val vis = if ("false" == visible) View.INVISIBLE else View.VISIBLE
+ notificationIconArea?.visibility = vis
+ }
+ }
+
+ override fun onDemoModeFinished() {
+ if (notificationIconArea != null) {
+ notificationIconArea?.visibility = View.VISIBLE
+ }
+ }
+
+ private fun inflateIconArea(inflater: LayoutInflater): View {
+ return inflater.inflate(R.layout.notification_icon_area, null)
+ }
+
+ /** Initializes the views that will represent the notification area. */
+ private fun initializeNotificationAreaViews(context: Context) {
+ reloadDimens(context)
+ val layoutInflater = LayoutInflater.from(context)
+ notificationIconArea = inflateIconArea(layoutInflater)
+ notificationIcons = notificationIconArea?.findViewById(R.id.notificationIcons)
+ NotificationIconContainerViewBinder.bind(notificationIcons!!, statusBarIconsViewModel)
+ }
+
+ private fun updateIconLayoutParams(context: Context) {
+ reloadDimens(context)
+ val params = generateIconLayoutParams()
+ for (i in 0 until notificationIcons!!.childCount) {
+ val child = notificationIcons!!.getChildAt(i)
+ child.layoutParams = params
+ }
+ if (shelfIcons != null) {
+ for (i in 0 until shelfIcons!!.childCount) {
+ val child = shelfIcons!!.getChildAt(i)
+ child.layoutParams = params
+ }
+ }
+ if (aodIcons != null) {
+ for (i in 0 until aodIcons!!.childCount) {
+ val child = aodIcons!!.getChildAt(i)
+ child.layoutParams = params
+ }
+ }
+ }
+
+ private fun generateIconLayoutParams(): FrameLayout.LayoutParams {
+ return FrameLayout.LayoutParams(
+ iconSize + 2 * iconHPadding,
+ statusBarWindowController.statusBarHeight
+ )
+ }
+
+ private fun reloadDimens(context: Context) {
+ val res = context.resources
+ iconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size_sp)
+ iconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin)
+ aodIconAppearTranslation = res.getDimensionPixelSize(R.dimen.shelf_appear_translation)
+ }
+
+ private fun shouldShowNotificationIcon(
+ entry: NotificationEntry,
+ showAmbient: Boolean,
+ showLowPriority: Boolean,
+ hideDismissed: Boolean,
+ hideRepliedMessages: Boolean,
+ hideCurrentMedia: Boolean,
+ hidePulsing: Boolean
+ ): Boolean {
+ if (!showAmbient && sectionStyleProvider.isMinimized(entry)) {
+ return false
+ }
+ if (hideCurrentMedia && entry.key == mediaManager.mediaNotificationKey) {
+ return false
+ }
+ if (!showLowPriority && sectionStyleProvider.isSilent(entry)) {
+ return false
+ }
+ if (entry.isRowDismissed && hideDismissed) {
+ return false
+ }
+ if (hideRepliedMessages && entry.isLastMessageFromReply) {
+ return false
+ }
+ // showAmbient == show in shade but not shelf
+ if (!showAmbient && entry.shouldSuppressStatusBar()) {
+ return false
+ }
+ if (
+ hidePulsing &&
+ entry.showingPulsing() &&
+ (!wakeUpCoordinator.notificationsFullyHidden || !entry.isPulseSuppressed)
+ ) {
+ return false
+ }
+ return if (bubblesOptional.isPresent && bubblesOptional.get().isBubbleExpanded(entry.key)) {
+ false
+ } else true
+ }
+
+ private fun updateNotificationIcons() {
+ Trace.beginSection("NotificationIconAreaController.updateNotificationIcons")
+ updateStatusBarIcons()
+ updateShelfIcons()
+ updateAodNotificationIcons()
+ applyNotificationIconsTint()
+ Trace.endSection()
+ }
+
+ private fun updateShelfIcons() {
+ if (shelfIcons == null) {
+ return
+ }
+ updateIconsForLayout(
+ { entry: NotificationEntry -> entry.icons.shelfIcon },
+ shelfIcons,
+ showAmbient = true,
+ showLowPriority = true,
+ hideDismissed = false,
+ hideRepliedMessages = false,
+ hideCurrentMedia = false,
+ hidePulsing = false
+ )
+ }
+
+ /**
+ * Updates the notification icons for a host layout. This will ensure that the notification host
+ * layout will have the same icons like the ones in here.
+ *
+ * @param function A function to look up an icon view based on an entry
+ * @param hostLayout which layout should be updated
+ * @param showAmbient should ambient notification icons be shown
+ * @param showLowPriority should icons from silent notifications be shown
+ * @param hideDismissed should dismissed icons be hidden
+ * @param hideRepliedMessages should messages that have been replied to be hidden
+ * @param hidePulsing should pulsing notifications be hidden
+ */
+ private fun updateIconsForLayout(
+ function: Function<NotificationEntry, StatusBarIconView?>,
+ hostLayout: NotificationIconContainer?,
+ showAmbient: Boolean,
+ showLowPriority: Boolean,
+ hideDismissed: Boolean,
+ hideRepliedMessages: Boolean,
+ hideCurrentMedia: Boolean,
+ hidePulsing: Boolean,
+ ) {
+ val toShow = ArrayList<StatusBarIconView>(notificationEntries.size)
+ // Filter out ambient notifications and notification children.
+ for (i in notificationEntries.indices) {
+ val entry = notificationEntries[i].representativeEntry
+ if (entry != null && entry.row != null) {
+ if (
+ shouldShowNotificationIcon(
+ entry,
+ showAmbient,
+ showLowPriority,
+ hideDismissed,
+ hideRepliedMessages,
+ hideCurrentMedia,
+ hidePulsing
+ )
+ ) {
+ val iconView = function.apply(entry)
+ if (iconView != null) {
+ toShow.add(iconView)
+ }
+ }
+ }
+ }
+
+ // In case we are changing the suppression of a group, the replacement shouldn't flicker
+ // and it should just be replaced instead. We therefore look for notifications that were
+ // just replaced by the child or vice-versa to suppress this.
+ val replacingIcons = ArrayMap<String, ArrayList<StatusBarIcon>>()
+ val toRemove = ArrayList<View>()
+ for (i in 0 until hostLayout!!.childCount) {
+ val child = hostLayout.getChildAt(i) as? StatusBarIconView ?: continue
+ if (!toShow.contains(child)) {
+ var iconWasReplaced = false
+ val removedGroupKey = child.notification.groupKey
+ for (j in toShow.indices) {
+ val candidate = toShow[j]
+ if (
+ candidate.sourceIcon.sameAs(child.sourceIcon) &&
+ candidate.notification.groupKey == removedGroupKey
+ ) {
+ if (!iconWasReplaced) {
+ iconWasReplaced = true
+ } else {
+ iconWasReplaced = false
+ break
+ }
+ }
+ }
+ if (iconWasReplaced) {
+ var statusBarIcons = replacingIcons[removedGroupKey]
+ if (statusBarIcons == null) {
+ statusBarIcons = ArrayList()
+ replacingIcons[removedGroupKey] = statusBarIcons
+ }
+ statusBarIcons.add(child.statusBarIcon)
+ }
+ toRemove.add(child)
+ }
+ }
+ // removing all duplicates
+ val duplicates = ArrayList<String?>()
+ for (key in replacingIcons.keys) {
+ val statusBarIcons = replacingIcons[key]!!
+ if (statusBarIcons.size != 1) {
+ duplicates.add(key)
+ }
+ }
+ replacingIcons.removeAll(duplicates)
+ hostLayout.setReplacingIcons(replacingIcons)
+ val toRemoveCount = toRemove.size
+ for (i in 0 until toRemoveCount) {
+ hostLayout.removeView(toRemove[i])
+ }
+ val params = generateIconLayoutParams()
+ for (i in toShow.indices) {
+ val v = toShow[i]
+ // The view might still be transiently added if it was just removed and added again
+ hostLayout.removeTransientView(v)
+ if (v.parent == null) {
+ if (hideDismissed) {
+ v.setOnDismissListener(updateStatusBarIcons)
+ }
+ hostLayout.addView(v, i, params)
+ }
+ }
+ hostLayout.setChangingViewPositions(true)
+ // Re-sort notification icons
+ val childCount = hostLayout.childCount
+ for (i in 0 until childCount) {
+ val actual = hostLayout.getChildAt(i)
+ val expected = toShow[i]
+ if (actual === expected) {
+ continue
+ }
+ hostLayout.removeView(expected)
+ hostLayout.addView(expected, i)
+ }
+ hostLayout.setChangingViewPositions(false)
+ hostLayout.setReplacingIcons(null)
+ }
+
+ /** Applies [.mIconTint] to the notification icons. */
+ private fun applyNotificationIconsTint() {
+ for (i in 0 until notificationIcons!!.childCount) {
+ val iv = notificationIcons!!.getChildAt(i) as StatusBarIconView
+ if (iv.width != 0) {
+ updateTintForIcon(iv, iconTint)
+ } else {
+ iv.executeOnLayout { updateTintForIcon(iv, iconTint) }
+ }
+ }
+ updateAodIconColors()
+ }
+
+ private fun updateTintForIcon(v: StatusBarIconView, tint: Int) {
+ val isPreL = java.lang.Boolean.TRUE == v.getTag(R.id.icon_is_pre_L)
+ var color = StatusBarIconView.NO_COLOR
+ val colorize = !isPreL || NotificationUtils.isGrayscale(v, contrastColorUtil)
+ if (colorize) {
+ color = DarkIconDispatcher.getTint(tintAreas, v, tint)
+ }
+ v.staticDrawableColor = color
+ v.setDecorColor(tint)
+ }
+
+ private fun updateAnimations() {
+ val inShade = statusBarStateController.state == StatusBarState.SHADE
+ if (aodIcons != null) {
+ aodIcons!!.setAnimationsEnabled(animationsEnabled && !inShade)
+ }
+ notificationIcons!!.setAnimationsEnabled(animationsEnabled && inShade)
+ }
+
+ private fun animateInAodIconTranslation() {
+ aodIcons!!
+ .animate()
+ .setInterpolator(Interpolators.DECELERATE_QUINT)
+ .translationY(0f)
+ .setDuration(AOD_ICONS_APPEAR_DURATION)
+ .start()
+ }
+
+ private fun reloadAodColor() {
+ aodIconTint =
+ Utils.getColorAttrDefaultColor(
+ context,
+ R.attr.wallpaperTextColor,
+ DEFAULT_AOD_ICON_COLOR
+ )
+ }
+
+ private fun updateAodIconColors() {
+ if (aodIcons != null) {
+ for (i in 0 until aodIcons!!.childCount) {
+ val iv = aodIcons!!.getChildAt(i) as StatusBarIconView
+ if (iv.width != 0) {
+ updateTintForIcon(iv, aodIconTint)
+ } else {
+ iv.executeOnLayout { updateTintForIcon(iv, aodIconTint) }
+ }
+ }
+ }
+ }
+
+ private fun updateAodIconsVisibility(animate: Boolean, forceUpdate: Boolean) {
+ if (aodIcons == null) {
+ return
+ }
+ var visible = (bypassController.bypassEnabled || wakeUpCoordinator.notificationsFullyHidden)
+
+ // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off animation is
+ // playing, in which case we want them to be visible since we're animating in the AOD UI and
+ // will be switching to KEYGUARD shortly.
+ if (
+ statusBarStateController.state != StatusBarState.KEYGUARD &&
+ !screenOffAnimationController.shouldShowAodIconsWhenShade()
+ ) {
+ visible = false
+ }
+ if (visible && wakeUpCoordinator.isPulseExpanding() && !bypassController.bypassEnabled) {
+ visible = false
+ }
+ if (aodIconsVisible != visible || forceUpdate) {
+ aodIconsVisible = visible
+ aodIcons!!.animate().cancel()
+ if (animate) {
+ val wasFullyInvisible = aodIcons!!.visibility != View.VISIBLE
+ if (aodIconsVisible) {
+ if (wasFullyInvisible) {
+ // No fading here, let's just appear the icons instead!
+ aodIcons!!.visibility = View.VISIBLE
+ aodIcons!!.alpha = 1.0f
+ appearAodIcons()
+ } else {
+ // Let's make sure the icon are translated to 0, since we cancelled it above
+ animateInAodIconTranslation()
+ // We were fading out, let's fade in instead
+ CrossFadeHelper.fadeIn(aodIcons)
+ }
+ } else {
+ // Let's make sure the icon are translated to 0, since we cancelled it above
+ animateInAodIconTranslation()
+ CrossFadeHelper.fadeOut(aodIcons)
+ }
+ } else {
+ aodIcons!!.alpha = 1.0f
+ aodIcons!!.translationY = 0f
+ aodIcons!!.visibility = if (visible) View.VISIBLE else View.INVISIBLE
+ }
+ }
+ }
+
+ companion object {
+ private const val AOD_ICONS_APPEAR_DURATION: Long = 200
+
+ @ColorInt private val DEFAULT_AOD_ICON_COLOR = -0x1
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
new file mode 100644
index 0000000..8293bb3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.icon.ui.viewbinder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import kotlinx.coroutines.DisposableHandle
+
+/** Binds a [NotificationIconContainer] to its [view model][NotificationIconContainerViewModel]. */
+object NotificationIconContainerViewBinder {
+ fun bind(
+ view: NotificationIconContainer,
+ viewModel: NotificationIconContainerViewModel,
+ ): DisposableHandle {
+ return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) {} }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
new file mode 100644
index 0000000..f68b0ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.icon.ui.viewmodel
+
+import javax.inject.Inject
+
+/** View-model for the row of notification icons displayed on the always-on display. */
+class NotificationIconContainerAlwaysOnDisplayViewModel @Inject constructor() :
+ NotificationIconContainerViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
new file mode 100644
index 0000000..933c76f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.icon.ui.viewmodel
+
+import javax.inject.Inject
+
+/** View-model for the overflow row of notification icons displayed in the notification shade. */
+class NotificationIconContainerShelfViewModel @Inject constructor() :
+ NotificationIconContainerViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
new file mode 100644
index 0000000..2217646
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.icon.ui.viewmodel
+
+import javax.inject.Inject
+
+/** View-model for the row of notification icons displayed in the status bar, */
+class NotificationIconContainerStatusBarViewModel @Inject constructor() :
+ NotificationIconContainerViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt
new file mode 100644
index 0000000..892b2be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.icon.ui.viewmodel
+
+/**
+ * View-model for the row of notification icons displayed in the NotificationShelf, StatusBar, and
+ * AOD.
+ */
+interface NotificationIconContainerViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
index b002330..c276827 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
@@ -20,7 +20,9 @@
import android.util.AttributeSet
import android.view.View
import android.widget.TextView
+import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.ImageFloatingTextView
+import com.android.internal.widget.MessagingLayout
import javax.inject.Inject
class PrecomputedTextViewFactory @Inject constructor() : NotifRemoteViewsFactory {
@@ -35,6 +37,10 @@
TextView::class.java.simpleName -> PrecomputedTextView(context, attrs)
ImageFloatingTextView::class.java.name ->
PrecomputedImageFloatingTextView(context, attrs)
+ MessagingLayout::class.java.name ->
+ MessagingLayout(context, attrs).apply { setPrecomputedTextEnabled(true) }
+ ConversationLayout::class.java.name ->
+ ConversationLayout(context, attrs).apply { setPrecomputedTextEnabled(true) }
else -> null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 22a87a7..b92c51f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -64,8 +64,10 @@
override fun setOnClickListener(listener: View.OnClickListener) = unsupported
- private val unsupported: Nothing
- get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled")
+ companion object {
+ val unsupported: Nothing
+ get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled")
+ }
}
/** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 4668aa4..6db8df9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -28,7 +28,6 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
-import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.content.res.Configuration;
@@ -150,6 +149,7 @@
public class NotificationStackScrollLayoutController {
private static final String TAG = "StackScrollerController";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
+ private static final String HIGH_PRIORITY = "high_priority";
private final boolean mAllowLongPress;
private final NotificationGutsManager mNotificationGutsManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 5c28be3..af09bf2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -25,7 +25,6 @@
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserHandle;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
import android.view.View;
@@ -276,19 +275,11 @@
void userActivity();
- boolean interceptMediaKey(KeyEvent event);
-
- boolean dispatchKeyEventPreIme(KeyEvent event);
-
- boolean onMenuPressed();
-
void endAffordanceLaunch();
/** Should the keyguard be hidden immediately in response to a back press/gesture. */
boolean shouldKeyguardHideImmediately();
- boolean onSpacePressed();
-
void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction,
Runnable cancelAction);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 6431ef9..2bc7b99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
@@ -33,12 +34,15 @@
import android.os.Vibrator;
import android.util.Log;
import android.util.Slog;
+import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.LetterboxDetails;
@@ -49,6 +53,7 @@
import com.android.systemui.camera.CameraIntents;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
@@ -107,6 +112,7 @@
private final Lazy<CameraLauncher> mCameraLauncherLazy;
private final QuickSettingsController mQsController;
private final QSHost mQSHost;
+ private final FeatureFlags mFeatureFlags;
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@@ -144,7 +150,8 @@
Lazy<CameraLauncher> cameraLauncherLazy,
UserTracker userTracker,
QSHost qsHost,
- ActivityStarter activityStarter) {
+ ActivityStarter activityStarter,
+ FeatureFlags featureFlags) {
mCentralSurfaces = centralSurfaces;
mQsController = quickSettingsController;
mContext = context;
@@ -171,6 +178,7 @@
mCameraLauncherLazy = cameraLauncherLazy;
mUserTracker = userTracker;
mQSHost = qsHost;
+ mFeatureFlags = featureFlags;
mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
@@ -314,7 +322,7 @@
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
if (mShadeViewController.isFullyCollapsed()) {
if (mVibrateOnOpening) {
- mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ vibrateOnNavigationKeyDown();
}
mShadeViewController.expand(true /* animate */);
mNotificationStackScrollLayoutController.setWillExpand(true);
@@ -587,4 +595,15 @@
}
return VibrationEffect.createWaveform(timings, /* repeat= */ -1);
}
+
+ @VisibleForTesting
+ void vibrateOnNavigationKeyDown() {
+ if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
+ mShadeViewController.performHapticFeedback(
+ HapticFeedbackConstants.GESTURE_START
+ );
+ } else {
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 8ffd43a..ccb87bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -89,7 +89,6 @@
import android.view.Display;
import android.view.IRemoteAnimationRunner;
import android.view.IWindowManager;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
@@ -2636,44 +2635,6 @@
}
@Override
- public boolean interceptMediaKey(KeyEvent event) {
- return mState == StatusBarState.KEYGUARD
- && mStatusBarKeyguardViewManager.interceptMediaKey(event);
- }
-
- /**
- * While IME is active and a BACK event is detected, check with
- * {@link StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event
- * should be handled before routing to IME, in order to prevent the user having to hit back
- * twice to exit bouncer.
- */
- @Override
- public boolean dispatchKeyEventPreIme(KeyEvent event) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_BACK:
- if (mState == StatusBarState.KEYGUARD
- && mStatusBarKeyguardViewManager.dispatchBackKeyEventPreIme()) {
- return mBackActionInteractor.onBackRequested();
- }
- }
- return false;
- }
-
- protected boolean shouldUnlockOnMenuPressed() {
- return mDeviceInteractive && mState != StatusBarState.SHADE
- && mStatusBarKeyguardViewManager.shouldDismissOnMenuPressed();
- }
-
- @Override
- public boolean onMenuPressed() {
- if (shouldUnlockOnMenuPressed()) {
- mShadeController.animateCollapseShadeForced();
- return true;
- }
- return false;
- }
-
- @Override
public void endAffordanceLaunch() {
releaseGestureWakeLock();
mCameraLauncherLazy.get().setLaunchingAffordance(false);
@@ -2692,15 +2653,6 @@
return (isScrimmedBouncer || isBouncerOverDream);
}
- @Override
- public boolean onSpacePressed() {
- if (mDeviceInteractive && mState != StatusBarState.SHADE) {
- mShadeController.animateCollapseShadeForced();
- return true;
- }
- return false;
- }
-
private void showBouncerOrLockScreenIfKeyguard() {
// If the keyguard is animating away, we aren't really the keyguard anymore and should not
// show the bouncer/lockscreen.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
index 0bf0f4b..d22ed38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
@@ -1,3 +1,18 @@
+/*
+ * 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.phone;
import android.content.Context;
@@ -43,6 +58,8 @@
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.wm.shell.bubbles.Bubbles;
+import org.jetbrains.annotations.NotNull;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -55,13 +72,13 @@
* normally reserved for notifications.
*/
@SysUISingleton
-public class NotificationIconAreaController implements
+public class LegacyNotificationIconAreaControllerImpl implements
+ NotificationIconAreaController,
DarkReceiver,
StatusBarStateController.StateListener,
NotificationWakeUpCoordinator.WakeUpListener,
DemoMode {
- public static final String HIGH_PRIORITY = "high_priority";
private static final long AOD_ICONS_APPEAR_DURATION = 200;
@ColorInt
private static final int DEFAULT_AOD_ICON_COLOR = 0xffffffff;
@@ -110,7 +127,7 @@
};
@Inject
- public NotificationIconAreaController(
+ public LegacyNotificationIconAreaControllerImpl(
Context context,
StatusBarStateController statusBarStateController,
NotificationWakeUpCoordinator wakeUpCoordinator,
@@ -192,7 +209,7 @@
}
}
- public void onDensityOrFontScaleChanged(Context context) {
+ public void onDensityOrFontScaleChanged(@NotNull Context context) {
updateIconLayoutParams(context);
}
@@ -493,7 +510,7 @@
mNotificationIcons.showIconIsolated(icon, animated);
}
- public void setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate) {
+ public void setIsolatedIconLocation(@NotNull Rect iconDrawingRect, boolean requireStateUpdate) {
mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt
new file mode 100644
index 0000000..0079f7c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.phone
+
+import android.content.Context
+import android.graphics.Rect
+import android.view.View
+import com.android.systemui.statusbar.NotificationShelfController
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.collection.ListEntry
+
+/**
+ * A controller for the space in the status bar to the left of the system icons. This area is
+ * normally reserved for notifications.
+ */
+interface NotificationIconAreaController {
+ /** Called by the Keyguard*ViewController whose view contains the aod icons. */
+ fun setupAodIcons(aodIcons: NotificationIconContainer)
+ fun setupShelf(notificationShelfController: NotificationShelfController)
+ fun setShelfIcons(icons: NotificationIconContainer)
+ fun onDensityOrFontScaleChanged(context: Context)
+
+ /** Returns the view that represents the notification area. */
+ fun getNotificationInnerAreaView(): View?
+
+ /** Updates the notifications with the given list of notifications to display. */
+ fun updateNotificationIcons(entries: List<@JvmSuppressWildcards ListEntry>)
+ fun updateAodNotificationIcons()
+ fun showIconIsolated(icon: StatusBarIconView?, animated: Boolean)
+ fun setIsolatedIconLocation(iconDrawingRect: Rect, requireStateUpdate: Boolean)
+ fun setAnimationsEnabled(enabled: Boolean)
+ fun onThemeChanged()
+ fun getHeight(): Int
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt
new file mode 100644
index 0000000..d1ddd51
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.phone
+
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconAreaControllerViewBinderWrapperImpl
+import dagger.Module
+import dagger.Provides
+import javax.inject.Provider
+
+@Module
+object NotificationIconAreaControllerModule {
+ @Provides
+ fun provideNotificationIconAreaControllerImpl(
+ featureFlags: FeatureFlags,
+ legacyProvider: Provider<LegacyNotificationIconAreaControllerImpl>,
+ newProvider: Provider<NotificationIconAreaControllerViewBinderWrapperImpl>,
+ ): NotificationIconAreaController =
+ if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ newProvider.get()
+ } else {
+ legacyProvider.get()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fc66138..62a8cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -709,6 +709,11 @@
}
}
+ public void onUnlockAnimationFinished() {
+ mAnimatingPanelExpansionOnUnlock = false;
+ applyAndDispatchState();
+ }
+
/**
* Set the amount of progress we are currently in if we're transitioning to the full shade.
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocation.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocation.kt
index 5ace226..32e5c35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocation.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocation.kt
@@ -24,4 +24,6 @@
KEYGUARD,
/** Quick settings (inside the shade). */
QS,
+ /** ShadeCarrierGroup (above QS status bar in expanded mode). */
+ SHADE_CARRIER_GROUP,
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 7fe01825..a6284e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -32,6 +32,8 @@
import android.view.View;
import android.view.ViewParent;
+import androidx.annotation.Nullable;
+
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -254,16 +256,16 @@
@Override
public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
- boolean appRequestedAuth,
+ boolean appRequestedAuth, @Nullable Integer actionIndex,
NotificationRemoteInputManager.ClickHandler defaultHandler) {
final boolean isActivity = pendingIntent.isActivity();
if (isActivity || appRequestedAuth) {
- mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent);
+ mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent, actionIndex);
final boolean afterKeyguardGone = mActivityIntentHelper
.wouldPendingLaunchResolverActivity(pendingIntent,
mLockscreenUserManager.getCurrentUserId());
mActivityStarter.dismissKeyguardThenExecute(() -> {
- mActionClickLogger.logKeyguardGone(pendingIntent);
+ mActionClickLogger.logKeyguardGone(pendingIntent, actionIndex);
try {
ActivityManager.getService().resumeAppSwitches();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 1bceb29..cd1afc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -223,10 +223,14 @@
}
.setCustomInterpolator(View.ALPHA, Interpolators.FAST_OUT_SLOW_IN),
true /* animate */)
- interactionJankMonitor.begin(
- notifShadeWindowControllerLazy.get().windowRootView,
- CUJ_SCREEN_OFF_SHOW_AOD
- )
+ val builder = InteractionJankMonitor.Configuration.Builder
+ .withView(
+ InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD,
+ notifShadeWindowControllerLazy.get().windowRootView
+ )
+ .setTag(statusBarStateControllerImpl.getClockId())
+
+ interactionJankMonitor.begin(builder)
}
override fun onStartedWakingUp() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
index 6e51ed0..c695773 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
@@ -18,6 +18,9 @@
import android.content.Context
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.shade.carrier.ShadeCarrierGroup
import javax.inject.Inject
/** All flagging methods related to the new status bar pipeline (see b/238425913). */
@@ -26,11 +29,19 @@
@Inject
constructor(
context: Context,
+ private val featureFlags: FeatureFlags,
) {
private val mobileSlot = context.getString(com.android.internal.R.string.status_bar_mobile)
private val wifiSlot = context.getString(com.android.internal.R.string.status_bar_wifi)
/**
+ * True if we should display the mobile icons in the [ShadeCarrierGroup] using the new status
+ * bar Data pipeline.
+ */
+ fun useNewShadeCarrierGroupMobileIcons(): Boolean =
+ featureFlags.isEnabled(Flags.NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS)
+
+ /**
* For convenience in the StatusBarIconController, we want to gate some actions based on slot
* name and the flag together.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
index 78231e2..99ed2d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
@@ -60,6 +60,19 @@
}
}
+ /** This name has been derived from SubscriptionModel. see [SubscriptionModel] */
+ data class SubscriptionDerived(override val name: String) : NetworkNameModel {
+ override fun logDiffs(prevVal: NetworkNameModel, row: TableRowLogger) {
+ if (prevVal !is SubscriptionDerived || prevVal.name != name) {
+ row.logChange(COL_NETWORK_NAME, "SubscriptionDerived($name)")
+ }
+ }
+
+ override fun logFull(row: TableRowLogger) {
+ row.logChange(COL_NETWORK_NAME, "SubscriptionDerived($name)")
+ }
+ }
+
/**
* This name has been derived from the sim via
* [android.telephony.TelephonyManager.getSimOperatorName].
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
index 16c4027..27f6df4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
@@ -34,4 +34,7 @@
/** Subscriptions in the same group may be filtered or treated as a single subscription */
val groupUuid: ParcelUuid? = null,
+
+ /** Text representing the name for this connection */
+ val carrierName: String,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index c1af6df..a89b1b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -115,10 +115,18 @@
*/
val cdmaRoaming: StateFlow<Boolean>
- /** The service provider name for this network connection, or the default name */
+ /** The service provider name for this network connection, or the default name. */
val networkName: StateFlow<NetworkNameModel>
/**
+ * The service provider name for this network connection, or the default name.
+ *
+ * TODO(b/296600321): De-duplicate this field with [networkName] after determining the data
+ * provided is identical
+ */
+ val carrierName: StateFlow<NetworkNameModel>
+
+ /**
* True if this type of connection is allowed while airplane mode is on, and false otherwise.
*/
val isAllowedDuringAirplaneMode: StateFlow<Boolean>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
index 17d20c2..c576b82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
@@ -184,7 +184,10 @@
override val cdmaRoaming = MutableStateFlow(false)
- override val networkName = MutableStateFlow(NetworkNameModel.IntentDerived("demo network"))
+ override val networkName = MutableStateFlow(NetworkNameModel.IntentDerived(DEMO_CARRIER_NAME))
+
+ override val carrierName =
+ MutableStateFlow(NetworkNameModel.SubscriptionDerived(DEMO_CARRIER_NAME))
override val isAllowedDuringAirplaneMode = MutableStateFlow(false)
@@ -200,6 +203,7 @@
// This is always true here, because we split out disabled states at the data-source level
dataEnabled.value = true
networkName.value = NetworkNameModel.IntentDerived(event.name)
+ carrierName.value = NetworkNameModel.SubscriptionDerived("${event.name} ${event.subId}")
_carrierId.value = event.carrierId ?: INVALID_SUBSCRIPTION_ID
@@ -227,6 +231,7 @@
// This is always true here, because we split out disabled states at the data-source level
dataEnabled.value = true
networkName.value = NetworkNameModel.IntentDerived(CARRIER_MERGED_NAME)
+ carrierName.value = NetworkNameModel.SubscriptionDerived(CARRIER_MERGED_NAME)
// TODO(b/276943904): is carrierId a thing with carrier merged networks?
_carrierId.value = INVALID_SUBSCRIPTION_ID
numberOfLevels.value = event.numberOfLevels
@@ -248,6 +253,7 @@
}
companion object {
+ private const val DEMO_CARRIER_NAME = "Demo Carrier"
private const val CARRIER_MERGED_NAME = "Carrier Merged Network"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index 0e4ceeb..ee13d93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -92,9 +92,12 @@
private fun maybeCreateSubscription(subId: Int) {
if (!subscriptionInfoCache.containsKey(subId)) {
- SubscriptionModel(subscriptionId = subId, isOpportunistic = false).also {
- subscriptionInfoCache[subId] = it
- }
+ SubscriptionModel(
+ subscriptionId = subId,
+ isOpportunistic = false,
+ carrierName = DEFAULT_CARRIER_NAME,
+ )
+ .also { subscriptionInfoCache[subId] = it }
_subscriptions.value = subscriptionInfoCache.values.toList()
}
@@ -327,6 +330,7 @@
private const val TAG = "DemoMobileConnectionsRepo"
private const val DEFAULT_SUB_ID = 1
+ private const val DEFAULT_CARRIER_NAME = "demo carrier"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index 65f4866..28be3be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -108,6 +108,8 @@
NetworkNameModel.SimDerived(telephonyManager.simOperatorName),
)
+ override val carrierName: StateFlow<NetworkNameModel> = networkName
+
override val numberOfLevels: StateFlow<Int> =
wifiRepository.wifiNetwork
.map {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index 8ba7d21..ee11c06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -22,6 +22,7 @@
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -47,6 +48,7 @@
override val subId: Int,
startingIsCarrierMerged: Boolean,
override val tableLogBuffer: TableLogBuffer,
+ subscriptionModel: StateFlow<SubscriptionModel?>,
private val defaultNetworkName: NetworkNameModel,
private val networkNameSeparator: String,
@Application scope: CoroutineScope,
@@ -80,6 +82,7 @@
mobileRepoFactory.build(
subId,
tableLogBuffer,
+ subscriptionModel,
defaultNetworkName,
networkNameSeparator,
)
@@ -287,6 +290,16 @@
)
.stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.networkName.value)
+ override val carrierName =
+ activeRepo
+ .flatMapLatest { it.carrierName }
+ .logDiffsForTable(
+ tableLogBuffer,
+ columnPrefix = "",
+ initialValue = activeRepo.value.carrierName.value,
+ )
+ .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.carrierName.value)
+
override val isAllowedDuringAirplaneMode =
activeRepo
.flatMapLatest { it.isAllowedDuringAirplaneMode }
@@ -307,6 +320,7 @@
fun build(
subId: Int,
startingIsCarrierMerged: Boolean,
+ subscriptionModel: StateFlow<SubscriptionModel?>,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
): FullMobileConnectionRepository {
@@ -317,6 +331,7 @@
subId,
startingIsCarrierMerged,
mobileLogger,
+ subscriptionModel,
defaultNetworkName,
networkNameSeparator,
scope,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index aadc975..1f1ac92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -43,6 +43,7 @@
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.toDataConnectionType
import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
@@ -80,6 +81,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
class MobileConnectionRepositoryImpl(
override val subId: Int,
+ subscriptionModel: StateFlow<SubscriptionModel?>,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
private val telephonyManager: TelephonyManager,
@@ -281,6 +283,14 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
+ override val carrierName =
+ subscriptionModel
+ .map {
+ it?.let { model -> NetworkNameModel.SubscriptionDerived(model.carrierName) }
+ ?: defaultNetworkName
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
+
/**
* There are a few cases where we will need to poll [TelephonyManager] so we can update some
* internal state where callbacks aren't provided. Any of those events should be merged into
@@ -350,11 +360,13 @@
fun build(
subId: Int,
mobileLogger: TableLogBuffer,
+ subscriptionModel: StateFlow<SubscriptionModel?>,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
): MobileConnectionRepository {
return MobileConnectionRepositoryImpl(
subId,
+ subscriptionModel,
defaultNetworkName,
networkNameSeparator,
telephonyManager.createForSubscriptionId(subId),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 54948a4..67b04db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -319,10 +319,17 @@
@VisibleForTesting fun getSubIdRepoCache() = subIdRepositoryCache
+ private fun subscriptionModelForSubId(subId: Int): StateFlow<SubscriptionModel?> {
+ return subscriptions
+ .map { list -> list.firstOrNull { model -> model.subscriptionId == subId } }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+ }
+
private fun createRepositoryForSubId(subId: Int): FullMobileConnectionRepository {
return fullMobileRepoFactory.build(
subId,
isCarrierMerged(subId),
+ subscriptionModelForSubId(subId),
defaultNetworkName,
networkNameSeparator,
)
@@ -373,6 +380,7 @@
subscriptionId = subscriptionId,
isOpportunistic = isOpportunistic,
groupUuid = groupUuid,
+ carrierName = carrierName.toString(),
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 1a13827..4cfde5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -92,6 +92,22 @@
*/
val networkName: StateFlow<NetworkNameModel>
+ /**
+ * Provider name for this network connection. The name can be one of 3 values:
+ * 1. The default network name, if one is configured
+ * 2. A name provided by the [SubscriptionModel] of this network connection
+ * 3. Or, in the case where the repository sends us the default network name, we check for an
+ * override in [connectionInfo.operatorAlphaShort], a value that is derived from
+ * [ServiceState]
+ *
+ * TODO(b/296600321): De-duplicate this field with [networkName] after determining the data
+ * provided is identical
+ */
+ val carrierName: StateFlow<String>
+
+ /** True if there is only one active subscription. */
+ val isSingleCarrier: StateFlow<Boolean>
+
/** True if this line of service is emergency-only */
val isEmergencyOnly: StateFlow<Boolean>
@@ -126,6 +142,7 @@
defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
override val alwaysShowDataRatIcon: StateFlow<Boolean>,
override val alwaysUseCdmaLevel: StateFlow<Boolean>,
+ override val isSingleCarrier: StateFlow<Boolean>,
override val mobileIsDefault: StateFlow<Boolean>,
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
@@ -171,6 +188,22 @@
connectionRepository.networkName.value
)
+ override val carrierName =
+ combine(connectionRepository.operatorAlphaShort, connectionRepository.carrierName) {
+ operatorAlphaShort,
+ networkName ->
+ if (networkName is NetworkNameModel.Default && operatorAlphaShort != null) {
+ operatorAlphaShort
+ } else {
+ networkName.name
+ }
+ }
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ connectionRepository.carrierName.value.name
+ )
+
/** What the mobile icon would be before carrierId overrides */
private val defaultNetworkType: StateFlow<MobileIconGroup> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index e90f40c7..d08808b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -76,6 +76,9 @@
/** True if the CDMA level should be preferred over the primary level. */
val alwaysUseCdmaLevel: StateFlow<Boolean>
+ /** True if there is only one active subscription. */
+ val isSingleCarrier: StateFlow<Boolean>
+
/** The icon mapping from network type to [MobileIconGroup] for the default subscription */
val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>
@@ -252,6 +255,17 @@
.mapLatest { it.alwaysShowCdmaRssi }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val isSingleCarrier: StateFlow<Boolean> =
+ mobileConnectionsRepo.subscriptions
+ .map { it.size == 1 }
+ .logDiffsForTable(
+ tableLogger,
+ columnPrefix = LOGGING_PREFIX,
+ columnName = "isSingleCarrier",
+ initialValue = false,
+ )
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
/** If there is no mapping in [defaultMobileIconMapping], then use this default icon group */
override val defaultMobileIconGroup: StateFlow<MobileIconGroup> =
mobileConnectionsRepo.defaultMobileIconGroup.stateIn(
@@ -298,6 +312,7 @@
activeDataConnectionHasDataEnabled,
alwaysShowDataRatIcon,
alwaysUseCdmaLevel,
+ isSingleCarrier,
mobileIsDefault,
defaultMobileIconMapping,
defaultMobileIconGroup,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
index d7fcf48..02e50a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
@@ -19,6 +19,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.shade.carrier.ShadeCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
@@ -49,6 +50,8 @@
private var isCollecting: Boolean = false
private var lastValue: List<Int>? = null
+ private var shadeCarrierGroupController: ShadeCarrierGroupController? = null
+
override fun start() {
// Start notifying the icon controller of subscriptions
scope.launch {
@@ -57,10 +60,16 @@
logger.logUiAdapterSubIdsSentToIconController(it)
lastValue = it
iconController.setNewMobileIconSubIds(it)
+ shadeCarrierGroupController?.updateModernMobileIcons(it)
}
}
}
+ /** Set the [ShadeCarrierGroupController] to notify of subscription updates */
+ fun setShadeCarrierGroupController(controller: ShadeCarrierGroupController) {
+ shadeCarrierGroupController = controller
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("isCollecting=$isCollecting")
pw.println("Last values sent to icon controller: $lastValue")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
index cea6654..2af6795b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
@@ -57,7 +57,7 @@
{
str1 = view.getIdForLogging()
str2 = viewModel.getIdForLogging()
- str3 = viewModel.locationName
+ str3 = viewModel.location.name
},
{ "New view binding. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
)
@@ -71,7 +71,7 @@
{
str1 = view.getIdForLogging()
str2 = viewModel.getIdForLogging()
- str3 = viewModel.locationName
+ str3 = viewModel.location.name
},
{ "Collection started. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
)
@@ -85,7 +85,7 @@
{
str1 = view.getIdForLogging()
str2 = viewModel.getIdForLogging()
- str3 = viewModel.locationName
+ str3 = viewModel.location.name
},
{ "Collection stopped. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index 55bc8d5..4b2fb43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -50,6 +50,7 @@
fun bind(
view: ViewGroup,
viewModel: LocationBasedMobileViewModel,
+ @StatusBarIconView.VisibleState initialVisibilityState: Int = STATE_HIDDEN,
logger: MobileViewLogger,
): ModernStatusBarViewBinding {
val mobileGroupView = view.requireViewById<ViewGroup>(R.id.mobile_group)
@@ -68,12 +69,12 @@
// TODO(b/238425913): We should log this visibility state.
@StatusBarIconView.VisibleState
- val visibilityState: MutableStateFlow<Int> = MutableStateFlow(STATE_HIDDEN)
+ val visibilityState: MutableStateFlow<Int> = MutableStateFlow(initialVisibilityState)
val iconTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
val decorTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
- var isCollecting: Boolean = false
+ var isCollecting = false
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt
new file mode 100644
index 0000000..081e101
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.pipeline.mobile.ui.binder
+
+import androidx.core.view.isVisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel
+import com.android.systemui.util.AutoMarqueeTextView
+import kotlinx.coroutines.launch
+
+object ShadeCarrierBinder {
+ /** Binds the view to the view-model, continuing to update the former based on the latter */
+ @JvmStatic
+ fun bind(
+ carrierTextView: AutoMarqueeTextView,
+ viewModel: ShadeCarrierGroupMobileIconViewModel,
+ ) {
+ carrierTextView.isVisible = true
+
+ carrierTextView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch { viewModel.carrierName.collect { carrierTextView.text = it } }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt
new file mode 100644
index 0000000..f407127
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.pipeline.mobile.ui.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.LinearLayout
+import com.android.systemui.R
+import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
+import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder
+import com.android.systemui.statusbar.pipeline.mobile.ui.binder.ShadeCarrierBinder
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel
+import com.android.systemui.util.AutoMarqueeTextView
+
+/**
+ * ViewGroup containing a mobile carrier name and icon in the Shade Header. Can be multiple
+ * instances as children under [ShadeCarrierGroup]
+ */
+class ModernShadeCarrierGroupMobileView(
+ context: Context,
+ attrs: AttributeSet?,
+) : LinearLayout(context, attrs) {
+
+ var subId: Int = -1
+
+ override fun toString(): String {
+ return "ModernShadeCarrierGroupMobileView(" +
+ "subId=$subId, " +
+ "viewString=${super.toString()}"
+ }
+
+ companion object {
+
+ /**
+ * Inflates a new instance of [ModernShadeCarrierGroupMobileView], binds it to [viewModel],
+ * and returns it.
+ */
+ @JvmStatic
+ fun constructAndBind(
+ context: Context,
+ logger: MobileViewLogger,
+ slot: String,
+ viewModel: ShadeCarrierGroupMobileIconViewModel,
+ ): ModernShadeCarrierGroupMobileView {
+ return (LayoutInflater.from(context).inflate(R.layout.shade_carrier_new, null)
+ as ModernShadeCarrierGroupMobileView)
+ .also {
+ it.subId = viewModel.subscriptionId
+
+ val iconView = it.requireViewById<ModernStatusBarMobileView>(R.id.mobile_combo)
+ iconView.initView(slot) {
+ MobileIconBinder.bind(iconView, viewModel, STATE_ICON, logger)
+ }
+ logger.logNewViewBinding(it, viewModel)
+
+ val textView = it.requireViewById<AutoMarqueeTextView>(R.id.mobile_carrier_text)
+ ShadeCarrierBinder.bind(textView, viewModel)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
index 4144293d..68d02de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
@@ -60,7 +60,9 @@
as ModernStatusBarMobileView)
.also {
it.subId = viewModel.subscriptionId
- it.initView(slot) { MobileIconBinder.bind(it, viewModel, logger) }
+ it.initView(slot) {
+ MobileIconBinder.bind(view = it, viewModel = viewModel, logger = logger)
+ }
logger.logNewViewBinding(it, viewModel)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
index a51982c..e7c311d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
@@ -18,7 +18,13 @@
import android.graphics.Color
import com.android.systemui.statusbar.phone.StatusBarLocation
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
/**
* A view model for an individual mobile icon that embeds the notion of a [StatusBarLocation]. This
@@ -26,12 +32,12 @@
*
* @param commonImpl for convenience, this class wraps a base interface that can provides all of the
* common implementations between locations. See [MobileIconViewModel]
- * @property locationName the name of the location of this VM, used for logging.
+ * @property location the [StatusBarLocation] of this VM.
* @property verboseLogger an optional logger to log extremely verbose view updates.
*/
abstract class LocationBasedMobileViewModel(
val commonImpl: MobileIconViewModelCommon,
- val locationName: String,
+ val location: StatusBarLocation,
val verboseLogger: VerboseMobileViewLogger?,
) : MobileIconViewModelCommon by commonImpl {
val defaultColor: Int = Color.WHITE
@@ -39,10 +45,12 @@
companion object {
fun viewModelForLocation(
commonImpl: MobileIconViewModelCommon,
+ interactor: MobileIconInteractor,
verboseMobileViewLogger: VerboseMobileViewLogger,
- loc: StatusBarLocation,
+ location: StatusBarLocation,
+ scope: CoroutineScope,
): LocationBasedMobileViewModel =
- when (loc) {
+ when (location) {
StatusBarLocation.HOME ->
HomeMobileIconViewModel(
commonImpl,
@@ -50,6 +58,12 @@
)
StatusBarLocation.KEYGUARD -> KeyguardMobileIconViewModel(commonImpl)
StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl)
+ StatusBarLocation.SHADE_CARRIER_GROUP ->
+ ShadeCarrierGroupMobileIconViewModel(
+ commonImpl,
+ interactor,
+ scope,
+ )
}
}
}
@@ -61,7 +75,7 @@
MobileIconViewModelCommon,
LocationBasedMobileViewModel(
commonImpl,
- locationName = "Home",
+ location = StatusBarLocation.HOME,
verboseMobileViewLogger,
)
@@ -71,18 +85,40 @@
MobileIconViewModelCommon,
LocationBasedMobileViewModel(
commonImpl,
- locationName = "QS",
+ location = StatusBarLocation.QS,
// Only do verbose logging for the Home location.
verboseLogger = null,
)
+class ShadeCarrierGroupMobileIconViewModel(
+ commonImpl: MobileIconViewModelCommon,
+ interactor: MobileIconInteractor,
+ scope: CoroutineScope,
+) :
+ MobileIconViewModelCommon,
+ LocationBasedMobileViewModel(
+ commonImpl,
+ location = StatusBarLocation.SHADE_CARRIER_GROUP,
+ // Only do verbose logging for the Home location.
+ verboseLogger = null,
+ ) {
+ private val isSingleCarrier = interactor.isSingleCarrier
+ val carrierName = interactor.carrierName
+
+ override val isVisible: StateFlow<Boolean> =
+ combine(super.isVisible, isSingleCarrier) { isVisible, isSingleCarrier ->
+ if (isSingleCarrier) false else isVisible
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), super.isVisible.value)
+}
+
class KeyguardMobileIconViewModel(
commonImpl: MobileIconViewModelCommon,
) :
MobileIconViewModelCommon,
LocationBasedMobileViewModel(
commonImpl,
- locationName = "Keyguard",
+ location = StatusBarLocation.KEYGUARD,
// Only do verbose logging for the Home location.
verboseLogger = null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 5cf887e..216afb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -22,6 +22,7 @@
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
@@ -58,6 +59,8 @@
private val statusBarPipelineFlags: StatusBarPipelineFlags,
) {
@VisibleForTesting val mobileIconSubIdCache = mutableMapOf<Int, MobileIconViewModel>()
+ @VisibleForTesting
+ val mobileIconInteractorSubIdCache = mutableMapOf<Int, MobileIconInteractor>()
val subscriptionIdsFlow: StateFlow<List<Int>> =
interactor.filteredSubscriptions
@@ -91,15 +94,17 @@
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
init {
- scope.launch { subscriptionIdsFlow.collect { removeInvalidModelsFromCache(it) } }
+ scope.launch { subscriptionIdsFlow.collect { invalidateCaches(it) } }
}
fun viewModelForSub(subId: Int, location: StatusBarLocation): LocationBasedMobileViewModel {
val common = commonViewModelForSub(subId)
return LocationBasedMobileViewModel.viewModelForLocation(
common,
+ mobileIconInteractorForSub(subId),
verboseLogger,
location,
+ scope,
)
}
@@ -107,7 +112,7 @@
return mobileIconSubIdCache[subId]
?: MobileIconViewModel(
subId,
- interactor.createMobileConnectionInteractorForSubId(subId),
+ mobileIconInteractorForSub(subId),
airplaneModeInteractor,
constants,
scope,
@@ -115,8 +120,20 @@
.also { mobileIconSubIdCache[subId] = it }
}
- private fun removeInvalidModelsFromCache(subIds: List<Int>) {
+ @VisibleForTesting
+ fun mobileIconInteractorForSub(subId: Int): MobileIconInteractor {
+ return mobileIconInteractorSubIdCache[subId]
+ ?: interactor.createMobileConnectionInteractorForSubId(subId).also {
+ mobileIconInteractorSubIdCache[subId] = it
+ }
+ }
+
+ private fun invalidateCaches(subIds: List<Int>) {
val subIdsToRemove = mobileIconSubIdCache.keys.filter { !subIds.contains(it) }
subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) }
+
+ mobileIconInteractorSubIdCache.keys
+ .filter { !subIds.contains(it) }
+ .forEach { subId -> mobileIconInteractorSubIdCache.remove(subId) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
index cd5b92c..00bd616 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
@@ -18,6 +18,7 @@
import android.graphics.Color
import com.android.systemui.statusbar.phone.StatusBarLocation
+import java.lang.IllegalArgumentException
/**
* A view model for a wifi icon in a specific location. This allows us to control parameters that
@@ -43,6 +44,8 @@
StatusBarLocation.HOME -> HomeWifiViewModel(commonImpl)
StatusBarLocation.KEYGUARD -> KeyguardWifiViewModel(commonImpl)
StatusBarLocation.QS -> QsWifiViewModel(commonImpl)
+ StatusBarLocation.SHADE_CARRIER_GROUP ->
+ throw IllegalArgumentException("invalid location for WifiViewModel: $location")
}
}
}
@@ -64,3 +67,11 @@
class QsWifiViewModel(
commonImpl: WifiViewModelCommon,
) : WifiViewModelCommon, LocationBasedWifiViewModel(commonImpl)
+
+/**
+ * A view model for the wifi icon in the shade carrier group (visible when quick settings is fully
+ * expanded, and in large screen shade). Currently unused.
+ */
+class ShadeCarrierGroupWifiViewModel(
+ commonImpl: WifiViewModelCommon,
+) : WifiViewModelCommon, LocationBasedWifiViewModel(commonImpl)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
index 4a31b86..eaae0f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
@@ -33,6 +33,7 @@
import com.android.systemui.Dependency
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.ShadeLogger
import com.android.systemui.util.ViewController
import com.android.systemui.util.time.SystemClock
import java.text.FieldPosition
@@ -83,6 +84,7 @@
private val systemClock: SystemClock,
private val broadcastDispatcher: BroadcastDispatcher,
private val shadeExpansionStateManager: ShadeExpansionStateManager,
+ private val shadeLogger: ShadeLogger,
private val timeTickHandler: Handler,
view: VariableDateView
) : ViewController<VariableDateView>(view) {
@@ -111,24 +113,29 @@
private val intentReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.action
+ if (
+ Intent.ACTION_LOCALE_CHANGED == action ||
+ Intent.ACTION_TIMEZONE_CHANGED == action
+ ) {
+ // need to get a fresh date format
+ dateFormat = null
+ shadeLogger.d("VariableDateViewController received intent to refresh date format")
+ }
+
+ val handler = mView.handler
+
// If the handler is null, it means we received a broadcast while the view has not
// finished being attached or in the process of being detached.
// In that case, do not post anything.
- val handler = mView.handler ?: return
- val action = intent.action
- if (
+ if (handler == null) {
+ shadeLogger.d("VariableDateViewController received intent but handler was null")
+ } else if (
Intent.ACTION_TIME_TICK == action ||
Intent.ACTION_TIME_CHANGED == action ||
Intent.ACTION_TIMEZONE_CHANGED == action ||
Intent.ACTION_LOCALE_CHANGED == action
) {
- if (
- Intent.ACTION_LOCALE_CHANGED == action ||
- Intent.ACTION_TIMEZONE_CHANGED == action
- ) {
- // need to get a fresh date format
- handler.post { dateFormat = null }
- }
handler.post(::updateClock)
}
}
@@ -231,6 +238,7 @@
private val systemClock: SystemClock,
private val broadcastDispatcher: BroadcastDispatcher,
private val shadeExpansionStateManager: ShadeExpansionStateManager,
+ private val shadeLogger: ShadeLogger,
@Named(Dependency.TIME_TICK_HANDLER_NAME) private val handler: Handler
) {
fun create(view: VariableDateView): VariableDateViewController {
@@ -238,6 +246,7 @@
systemClock,
broadcastDispatcher,
shadeExpansionStateManager,
+ shadeLogger,
handler,
view
)
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index aea3030..9dca013 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -109,7 +109,6 @@
private WallpaperManager mWallpaperManager;
private final WallpaperLocalColorExtractor mWallpaperLocalColorExtractor;
private SurfaceHolder mSurfaceHolder;
- private boolean mDrawn = false;
@VisibleForTesting
static final int MIN_SURFACE_WIDTH = 128;
@VisibleForTesting
@@ -240,7 +239,6 @@
private void drawFrameSynchronized() {
synchronized (mLock) {
- if (mDrawn) return;
drawFrameInternal();
}
}
@@ -278,7 +276,6 @@
Rect dest = mSurfaceHolder.getSurfaceFrame();
try {
canvas.drawBitmap(bitmap, null, dest, null);
- mDrawn = true;
} finally {
surface.unlockCanvasAndPost(canvas);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
index b349696..438f0f4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
@@ -18,6 +18,8 @@
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static com.android.systemui.flags.Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -37,6 +39,7 @@
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -59,8 +62,13 @@
@Mock
private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
@Mock
+ private ConnectedDisplayKeyguardPresentation.Factory
+ mConnectedDisplayKeyguardPresentationFactory;
+ @Mock
private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation;
@Mock
+ private ConnectedDisplayKeyguardPresentation mConnectedDisplayKeyguardPresentation;
+ @Mock
private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper;
@Mock
private KeyguardStateController mKeyguardStateController;
@@ -69,7 +77,7 @@
private Executor mBackgroundExecutor = Runnable::run;
private KeyguardDisplayManager mManager;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
-
+ private FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
// The default and secondary displays are both in the default group
private Display mDefaultDisplay;
private Display mSecondaryDisplay;
@@ -80,10 +88,14 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, false);
mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController,
mKeyguardStatusViewComponentFactory, mDisplayTracker, mMainExecutor,
- mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController));
+ mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController,
+ mConnectedDisplayKeyguardPresentationFactory, mFakeFeatureFlags));
doReturn(mKeyguardPresentation).when(mManager).createPresentation(any());
+ doReturn(mConnectedDisplayKeyguardPresentation).when(
+ mConnectedDisplayKeyguardPresentationFactory).create(any());
mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -138,4 +150,15 @@
when(mKeyguardStateController.isOccluded()).thenReturn(true);
verify(mManager, never()).createPresentation(eq(mSecondaryDisplay));
}
+
+ @Test
+ public void testShow_withClockPresentationFlagEnabled_presentationCreated() {
+ when(mManager.createPresentation(any())).thenCallRealMethod();
+ mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, true);
+ mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
+
+ mManager.show();
+
+ verify(mConnectedDisplayKeyguardPresentationFactory).create(eq(mSecondaryDisplay));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index ad4bd58..dff058c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -621,6 +621,51 @@
configurationListenerArgumentCaptor.value.onUiModeChanged()
verify(view).reloadColors()
}
+ @Test
+ fun onOrientationChanged_landscapeKeyguardFlagDisabled_blockReinflate() {
+ featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
+
+ // Run onOrientationChanged
+ val configurationListenerArgumentCaptor =
+ ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java)
+ underTest.onViewAttached()
+ verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture())
+ clearInvocations(viewFlipperController)
+ configurationListenerArgumentCaptor.value.onOrientationChanged(
+ Configuration.ORIENTATION_LANDSCAPE
+ )
+ // Verify view is reinflated when flag is on
+ verify(viewFlipperController, never()).clearViews()
+ verify(viewFlipperController, never())
+ .asynchronouslyInflateView(
+ eq(SecurityMode.PIN),
+ any(),
+ onViewInflatedCallbackArgumentCaptor.capture()
+ )
+ }
+
+ @Test
+ fun onOrientationChanged_landscapeKeyguardFlagEnabled_doesReinflate() {
+ featureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, true)
+
+ // Run onOrientationChanged
+ val configurationListenerArgumentCaptor =
+ ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java)
+ underTest.onViewAttached()
+ verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture())
+ clearInvocations(viewFlipperController)
+ configurationListenerArgumentCaptor.value.onOrientationChanged(
+ Configuration.ORIENTATION_LANDSCAPE
+ )
+ // Verify view is reinflated when flag is on
+ verify(viewFlipperController).clearViews()
+ verify(viewFlipperController)
+ .asynchronouslyInflateView(
+ eq(SecurityMode.PIN),
+ any(),
+ onViewInflatedCallbackArgumentCaptor.capture()
+ )
+ }
@Test
fun hasDismissActions() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 7d23c80..b8b0198 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -77,7 +77,7 @@
public void updatePosition_primaryClockAnimation() {
ClockController mockClock = mock(ClockController.class);
when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
- when(mockClock.getConfig()).thenReturn(new ClockConfig(false, true));
+ when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", false, true));
mController.updatePosition(10, 15, 20f, true);
@@ -92,7 +92,7 @@
public void updatePosition_alternateClockAnimation() {
ClockController mockClock = mock(ClockController.class);
when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
- when(mockClock.getConfig()).thenReturn(new ClockConfig(true, true));
+ when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", true, true));
mController.updatePosition(10, 15, 20f, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 4e52e64..9584d88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -39,13 +39,13 @@
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.FakePromptRepository
import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
-import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
import com.android.systemui.flags.FakeFeatureFlags
@@ -109,6 +109,7 @@
private val testScope = TestScope(StandardTestDispatcher())
private val fakeExecutor = FakeExecutor(FakeSystemClock())
private val biometricPromptRepository = FakePromptRepository()
+ private val fingerprintRepository = FakeFingerprintPropertyRepository()
private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
private val credentialInteractor = FakeCredentialInteractor()
private val bpCredentialInteractor = PromptCredentialInteractor(
@@ -118,10 +119,12 @@
)
private val promptSelectorInteractor by lazy {
PromptSelectorInteractorImpl(
+ fingerprintRepository,
biometricPromptRepository,
lockPatternUtils,
)
}
+
private val displayStateInteractor = DisplayStateInteractorImpl(
testScope.backgroundScope,
mContext,
@@ -129,9 +132,7 @@
rearDisplayStateRepository
)
- private val authBiometricFingerprintViewModel = AuthBiometricFingerprintViewModel(
- displayStateInteractor
- )
+
private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
private var authContainer: TestAuthContainerView? = null
@@ -524,10 +525,14 @@
userManager,
lockPatternUtils,
interactionJankMonitor,
- { authBiometricFingerprintViewModel },
{ promptSelectorInteractor },
{ bpCredentialInteractor },
- PromptViewModel(promptSelectorInteractor, vibrator, featureFlags),
+ PromptViewModel(
+ displayStateInteractor,
+ promptSelectorInteractor,
+ vibrator,
+ featureFlags
+ ),
{ credentialViewModel },
Handler(TestableLooper.get(this).looper),
fakeExecutor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 48e5131..6d71dd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -92,7 +92,6 @@
import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
-import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel;
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -177,8 +176,6 @@
@Mock
private PromptSelectorInteractor mPromptSelectionInteractor;
@Mock
- private AuthBiometricFingerprintViewModel mAuthBiometricFingerprintViewModel;
- @Mock
private CredentialViewModel mCredentialViewModel;
@Mock
private PromptViewModel mPromptViewModel;
@@ -1095,11 +1092,10 @@
mFingerprintManager, mFaceManager, () -> mUdfpsController,
() -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle,
mPanelInteractionDetector, mUserManager, mLockPatternUtils, mUdfpsLogger,
- mLogContextInteractor, () -> mAuthBiometricFingerprintViewModel,
- () -> mBiometricPromptCredentialInteractor, () -> mPromptSelectionInteractor,
- () -> mCredentialViewModel, () -> mPromptViewModel,
- mInteractionJankMonitor, mHandler,
- mBackgroundExecutor, mUdfpsUtils, mVibratorHelper);
+ mLogContextInteractor, () -> mBiometricPromptCredentialInteractor,
+ () -> mPromptSelectionInteractor, () -> mCredentialViewModel,
+ () -> mPromptViewModel, mInteractionJankMonitor, mHandler, mBackgroundExecutor,
+ mUdfpsUtils, mVibratorHelper);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index 81cbaea..4d5e1b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -23,6 +23,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.FakePromptRepository
import com.android.systemui.biometrics.domain.model.BiometricModalities
import com.android.systemui.biometrics.faceSensorPropertiesInternal
@@ -61,13 +62,15 @@
@Mock private lateinit var lockPatternUtils: LockPatternUtils
private val testScope = TestScope()
+ private val fingerprintRepository = FakeFingerprintPropertyRepository()
private val promptRepository = FakePromptRepository()
private lateinit var interactor: PromptSelectorInteractor
@Before
fun setup() {
- interactor = PromptSelectorInteractorImpl(promptRepository, lockPatternUtils)
+ interactor =
+ PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt
deleted file mode 100644
index 785f1be..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/AuthBiometricFingerprintViewModelTest.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.android.systemui.biometrics.ui.viewmodel
-
-import android.content.res.Configuration
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
-import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(JUnit4::class)
-class AuthBiometricFingerprintViewModelTest : SysuiTestCase() {
-
- private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
- private val testScope = TestScope(StandardTestDispatcher())
- private val fakeExecutor = FakeExecutor(FakeSystemClock())
-
- private lateinit var interactor: DisplayStateInteractor
- private lateinit var viewModel: AuthBiometricFingerprintViewModel
-
- @Before
- fun setup() {
- interactor =
- DisplayStateInteractorImpl(
- testScope.backgroundScope,
- mContext,
- fakeExecutor,
- rearDisplayStateRepository
- )
- viewModel = AuthBiometricFingerprintViewModel(interactor)
- }
-
- @Test
- fun iconUpdates_onConfigurationChanged() {
- testScope.runTest {
- runCurrent()
-
- val testConfig = Configuration()
- val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
- val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
- val currentIcon = collectLastValue(viewModel.iconAsset)
-
- testConfig.smallestScreenWidthDp = folded
- viewModel.onConfigurationChanged(testConfig)
- val foldedIcon = currentIcon()
-
- testConfig.smallestScreenWidthDp = unfolded
- viewModel.onConfigurationChanged(testConfig)
- val unfoldedIcon = currentIcon()
-
- assertThat(foldedIcon).isNotEqualTo(unfoldedIcon)
- }
- }
-}
-
-internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt
new file mode 100644
index 0000000..7697c09
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptFingerprintIconViewModelTest.kt
@@ -0,0 +1,94 @@
+package com.android.systemui.biometrics.ui.viewmodel
+
+import android.content.res.Configuration
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.data.repository.FakePromptRepository
+import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
+import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
+import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class PromptFingerprintIconViewModelTest : SysuiTestCase() {
+
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ @Mock private lateinit var lockPatternUtils: LockPatternUtils
+
+ private val fingerprintRepository = FakeFingerprintPropertyRepository()
+ private val promptRepository = FakePromptRepository()
+ private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+ private lateinit var promptSelectorInteractor: PromptSelectorInteractor
+ private lateinit var displayStateInteractor: DisplayStateInteractor
+ private lateinit var viewModel: PromptFingerprintIconViewModel
+
+ @Before
+ fun setup() {
+ promptSelectorInteractor =
+ PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
+ displayStateInteractor =
+ DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ rearDisplayStateRepository
+ )
+ viewModel = PromptFingerprintIconViewModel(displayStateInteractor, promptSelectorInteractor)
+ }
+
+ @Test
+ fun sfpsIconUpdates_onConfigurationChanged() {
+ testScope.runTest {
+ runCurrent()
+ configureFingerprintPropertyRepository(FingerprintSensorType.POWER_BUTTON)
+ val testConfig = Configuration()
+ val folded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP - 1
+ val unfolded = INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP + 1
+ val currentIcon = collectLastValue(viewModel.iconAsset)
+
+ testConfig.smallestScreenWidthDp = folded
+ viewModel.onConfigurationChanged(testConfig)
+ val foldedIcon = currentIcon()
+
+ testConfig.smallestScreenWidthDp = unfolded
+ viewModel.onConfigurationChanged(testConfig)
+ val unfoldedIcon = currentIcon()
+
+ assertThat(foldedIcon).isNotEqualTo(unfoldedIcon)
+ }
+ }
+
+ private fun configureFingerprintPropertyRepository(sensorType: FingerprintSensorType) {
+ fingerprintRepository.setProperties(0, SensorStrength.STRONG, sensorType, mapOf())
+ }
+}
+
+internal const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 4d19543..11b0b07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -24,7 +24,10 @@
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthBiometricView
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.FakePromptRepository
+import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository
+import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
import com.android.systemui.biometrics.domain.model.BiometricModalities
@@ -37,7 +40,9 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
@@ -69,8 +74,19 @@
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var vibrator: VibratorHelper
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
private val testScope = TestScope()
+ private val fingerprintRepository = FakeFingerprintPropertyRepository()
private val promptRepository = FakePromptRepository()
+ private val rearDisplayStateRepository = FakeRearDisplayStateRepository()
+
+ private val displayStateInteractor =
+ DisplayStateInteractorImpl(
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ rearDisplayStateRepository
+ )
private lateinit var selector: PromptSelectorInteractor
private lateinit var viewModel: PromptViewModel
@@ -78,10 +94,11 @@
@Before
fun setup() {
- selector = PromptSelectorInteractorImpl(promptRepository, lockPatternUtils)
+ selector =
+ PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
selector.resetPrompt()
- viewModel = PromptViewModel(selector, vibrator, featureFlags)
+ viewModel = PromptViewModel(displayStateInteractor, selector, vibrator, featureFlags)
featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
}
@@ -105,7 +122,7 @@
}
assertThat(message).isEqualTo(PromptMessage.Empty)
assertThat(size).isEqualTo(expectedSize)
- assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATING_ANIMATING_IN)
+ assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_IDLE)
val startMessage = "here we go"
viewModel.showAuthenticating(startMessage, isRetry = false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
similarity index 65%
rename from packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
index ff15cb3..14c5ec0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
@@ -29,6 +29,10 @@
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.GlobalSettings
import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.Serializable
+import java.io.StringWriter
+import java.util.function.Consumer
import org.junit.Assert
import org.junit.Before
import org.junit.Test
@@ -41,44 +45,30 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-import java.io.PrintWriter
-import java.io.Serializable
-import java.io.StringWriter
-import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/**
* NOTE: This test is for the version of FeatureFlagManager in src-debug, which allows overriding
* the default.
*/
@SmallTest
-class FeatureFlagsDebugTest : SysuiTestCase() {
- private lateinit var featureFlagsDebug: FeatureFlagsDebug
+class FeatureFlagsClassicDebugTest : SysuiTestCase() {
+ private lateinit var mFeatureFlagsClassicDebug: FeatureFlagsClassicDebug
- @Mock
- private lateinit var flagManager: FlagManager
- @Mock
- private lateinit var mockContext: Context
- @Mock
- private lateinit var globalSettings: GlobalSettings
- @Mock
- private lateinit var systemProperties: SystemPropertiesHelper
- @Mock
- private lateinit var resources: Resources
- @Mock
- private lateinit var restarter: Restarter
+ @Mock private lateinit var flagManager: FlagManager
+ @Mock private lateinit var mockContext: Context
+ @Mock private lateinit var globalSettings: GlobalSettings
+ @Mock private lateinit var systemProperties: SystemPropertiesHelper
+ @Mock private lateinit var resources: Resources
+ @Mock private lateinit var restarter: Restarter
private val flagMap = mutableMapOf<String, Flag<*>>()
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<String>
private val serverFlagReader = ServerFlagReaderFake()
- private val teamfoodableFlagA = UnreleasedFlag(
- name = "a", namespace = "test", teamfood = true
- )
- private val teamfoodableFlagB = ReleasedFlag(
- name = "b", namespace = "test", teamfood = true
- )
+ private val teamfoodableFlagA = UnreleasedFlag(name = "a", namespace = "test", teamfood = true)
+ private val teamfoodableFlagB = ReleasedFlag(name = "b", namespace = "test", teamfood = true)
@Before
fun setup() {
@@ -86,27 +76,23 @@
flagMap.put(Flags.TEAMFOOD.name, Flags.TEAMFOOD)
flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA)
flagMap.put(teamfoodableFlagB.name, teamfoodableFlagB)
- featureFlagsDebug = FeatureFlagsDebug(
- flagManager,
- mockContext,
- globalSettings,
- systemProperties,
- resources,
- serverFlagReader,
- flagMap,
- restarter
- )
- featureFlagsDebug.init()
+ mFeatureFlagsClassicDebug =
+ FeatureFlagsClassicDebug(
+ flagManager,
+ mockContext,
+ globalSettings,
+ systemProperties,
+ resources,
+ serverFlagReader,
+ flagMap,
+ restarter
+ )
+ mFeatureFlagsClassicDebug.init()
verify(flagManager).onSettingsChangedAction = any()
broadcastReceiver = withArgCaptor {
- verify(mockContext).registerReceiver(
- capture(), any(), nullable(), nullable(),
- any()
- )
+ verify(mockContext).registerReceiver(capture(), any(), nullable(), nullable(), any())
}
- clearCacheAction = withArgCaptor {
- verify(flagManager).clearCacheAction = capture()
- }
+ clearCacheAction = withArgCaptor { verify(flagManager).clearCacheAction = capture() }
whenever(flagManager.nameToSettingsKey(any())).thenAnswer { "key-${it.arguments[0]}" }
}
@@ -117,45 +103,29 @@
whenever(flagManager.readFlagValue<Boolean>(eq("4"), any())).thenReturn(false)
assertThat(
- featureFlagsDebug.isEnabled(
- ReleasedFlag(
- name = "2",
- namespace = "test"
- )
+ mFeatureFlagsClassicDebug.isEnabled(ReleasedFlag(name = "2", namespace = "test"))
)
- ).isTrue()
+ .isTrue()
assertThat(
- featureFlagsDebug.isEnabled(
- UnreleasedFlag(
- name = "3",
- namespace = "test"
- )
+ mFeatureFlagsClassicDebug.isEnabled(UnreleasedFlag(name = "3", namespace = "test"))
)
- ).isTrue()
+ .isTrue()
assertThat(
- featureFlagsDebug.isEnabled(
- ReleasedFlag(
- name = "4",
- namespace = "test"
- )
+ mFeatureFlagsClassicDebug.isEnabled(ReleasedFlag(name = "4", namespace = "test"))
)
- ).isFalse()
+ .isFalse()
assertThat(
- featureFlagsDebug.isEnabled(
- UnreleasedFlag(
- name = "5",
- namespace = "test"
- )
+ mFeatureFlagsClassicDebug.isEnabled(UnreleasedFlag(name = "5", namespace = "test"))
)
- ).isFalse()
+ .isFalse()
}
@Test
fun teamFoodFlag_False() {
- whenever(flagManager.readFlagValue<Boolean>(
- eq(Flags.TEAMFOOD.name), any())).thenReturn(false)
- assertThat(featureFlagsDebug.isEnabled(teamfoodableFlagA)).isFalse()
- assertThat(featureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
+ whenever(flagManager.readFlagValue<Boolean>(eq(Flags.TEAMFOOD.name), any()))
+ .thenReturn(false)
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isFalse()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isTrue()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
@@ -164,10 +134,10 @@
@Test
fun teamFoodFlag_True() {
- whenever(flagManager.readFlagValue<Boolean>(
- eq(Flags.TEAMFOOD.name), any())).thenReturn(true)
- assertThat(featureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
- assertThat(featureFlagsDebug.isEnabled(teamfoodableFlagB)).isTrue()
+ whenever(flagManager.readFlagValue<Boolean>(eq(Flags.TEAMFOOD.name), any()))
+ .thenReturn(true)
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isTrue()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
@@ -180,10 +150,10 @@
.thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.name), any()))
.thenReturn(false)
- whenever(flagManager.readFlagValue<Boolean>(
- eq(Flags.TEAMFOOD.name), any())).thenReturn(true)
- assertThat(featureFlagsDebug.isEnabled(teamfoodableFlagA)).isTrue()
- assertThat(featureFlagsDebug.isEnabled(teamfoodableFlagB)).isFalse()
+ whenever(flagManager.readFlagValue<Boolean>(eq(Flags.TEAMFOOD.name), any()))
+ .thenReturn(true)
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isFalse()
// Regular boolean flags should still test the same.
// Only our teamfoodableFlag should change.
@@ -201,25 +171,20 @@
whenever(flagManager.readFlagValue<Boolean>(eq("3"), any())).thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq("5"), any())).thenReturn(false)
- assertThat(
- featureFlagsDebug.isEnabled(
- ResourceBooleanFlag(
- "1",
- "test",
- 1001
- )
- )
- ).isFalse()
- assertThat(featureFlagsDebug.isEnabled(ResourceBooleanFlag("2", "test", 1002))).isTrue()
- assertThat(featureFlagsDebug.isEnabled(ResourceBooleanFlag("3", "test", 1003))).isTrue()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(ResourceBooleanFlag("1", "test", 1001)))
+ .isFalse()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(ResourceBooleanFlag("2", "test", 1002)))
+ .isTrue()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(ResourceBooleanFlag("3", "test", 1003)))
+ .isTrue()
Assert.assertThrows(NameNotFoundException::class.java) {
- featureFlagsDebug.isEnabled(ResourceBooleanFlag("4", "test", 1004))
+ mFeatureFlagsClassicDebug.isEnabled(ResourceBooleanFlag("4", "test", 1004))
}
// Test that resource is loaded (and validated) even when the setting is set.
// This prevents developers from not noticing when they reference an invalid resource.
Assert.assertThrows(NameNotFoundException::class.java) {
- featureFlagsDebug.isEnabled(ResourceBooleanFlag("5", "test", 1005))
+ mFeatureFlagsClassicDebug.isEnabled(ResourceBooleanFlag("5", "test", 1005))
}
}
@@ -232,29 +197,27 @@
return@thenAnswer it.getArgument(1)
}
- assertThat(featureFlagsDebug.isEnabled(SysPropBooleanFlag("a", "test"))).isFalse()
- assertThat(featureFlagsDebug.isEnabled(SysPropBooleanFlag("b", "test"))).isTrue()
- assertThat(featureFlagsDebug.isEnabled(SysPropBooleanFlag("c", "test", true))).isTrue()
- assertThat(
- featureFlagsDebug.isEnabled(
- SysPropBooleanFlag(
- "d",
- "test",
- false
- )
- )
- ).isFalse()
- assertThat(featureFlagsDebug.isEnabled(SysPropBooleanFlag("e", "test"))).isFalse()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(SysPropBooleanFlag("a", "test"))).isFalse()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(SysPropBooleanFlag("b", "test"))).isTrue()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(SysPropBooleanFlag("c", "test", true)))
+ .isTrue()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(SysPropBooleanFlag("d", "test", false)))
+ .isFalse()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(SysPropBooleanFlag("e", "test"))).isFalse()
}
@Test
fun readStringFlag() {
whenever(flagManager.readFlagValue<String>(eq("3"), any())).thenReturn("foo")
whenever(flagManager.readFlagValue<String>(eq("4"), any())).thenReturn("bar")
- assertThat(featureFlagsDebug.getString(StringFlag("1", "test", "biz"))).isEqualTo("biz")
- assertThat(featureFlagsDebug.getString(StringFlag("2", "test", "baz"))).isEqualTo("baz")
- assertThat(featureFlagsDebug.getString(StringFlag("3", "test", "buz"))).isEqualTo("foo")
- assertThat(featureFlagsDebug.getString(StringFlag("4", "test", "buz"))).isEqualTo("bar")
+ assertThat(mFeatureFlagsClassicDebug.getString(StringFlag("1", "test", "biz")))
+ .isEqualTo("biz")
+ assertThat(mFeatureFlagsClassicDebug.getString(StringFlag("2", "test", "baz")))
+ .isEqualTo("baz")
+ assertThat(mFeatureFlagsClassicDebug.getString(StringFlag("3", "test", "buz")))
+ .isEqualTo("foo")
+ assertThat(mFeatureFlagsClassicDebug.getString(StringFlag("4", "test", "buz")))
+ .isEqualTo("bar")
}
@Test
@@ -270,44 +233,23 @@
whenever(flagManager.readFlagValue<String>(eq("4"), any())).thenReturn("override4")
whenever(flagManager.readFlagValue<String>(eq("6"), any())).thenReturn("override6")
- assertThat(
- featureFlagsDebug.getString(
- ResourceStringFlag(
- "1",
- "test",
- 1001
- )
- )
- ).isEqualTo("")
- assertThat(
- featureFlagsDebug.getString(
- ResourceStringFlag(
- "2",
- "test",
- 1002
- )
- )
- ).isEqualTo("resource2")
- assertThat(
- featureFlagsDebug.getString(
- ResourceStringFlag(
- "3",
- "test",
- 1003
- )
- )
- ).isEqualTo("override3")
+ assertThat(mFeatureFlagsClassicDebug.getString(ResourceStringFlag("1", "test", 1001)))
+ .isEqualTo("")
+ assertThat(mFeatureFlagsClassicDebug.getString(ResourceStringFlag("2", "test", 1002)))
+ .isEqualTo("resource2")
+ assertThat(mFeatureFlagsClassicDebug.getString(ResourceStringFlag("3", "test", 1003)))
+ .isEqualTo("override3")
Assert.assertThrows(NullPointerException::class.java) {
- featureFlagsDebug.getString(ResourceStringFlag("4", "test", 1004))
+ mFeatureFlagsClassicDebug.getString(ResourceStringFlag("4", "test", 1004))
}
Assert.assertThrows(NameNotFoundException::class.java) {
- featureFlagsDebug.getString(ResourceStringFlag("5", "test", 1005))
+ mFeatureFlagsClassicDebug.getString(ResourceStringFlag("5", "test", 1005))
}
// Test that resource is loaded (and validated) even when the setting is set.
// This prevents developers from not noticing when they reference an invalid resource.
Assert.assertThrows(NameNotFoundException::class.java) {
- featureFlagsDebug.getString(ResourceStringFlag("6", "test", 1005))
+ mFeatureFlagsClassicDebug.getString(ResourceStringFlag("6", "test", 1005))
}
}
@@ -315,10 +257,10 @@
fun readIntFlag() {
whenever(flagManager.readFlagValue<Int>(eq("3"), any())).thenReturn(22)
whenever(flagManager.readFlagValue<Int>(eq("4"), any())).thenReturn(48)
- assertThat(featureFlagsDebug.getInt(IntFlag("1", "test", 12))).isEqualTo(12)
- assertThat(featureFlagsDebug.getInt(IntFlag("2", "test", 93))).isEqualTo(93)
- assertThat(featureFlagsDebug.getInt(IntFlag("3", "test", 8))).isEqualTo(22)
- assertThat(featureFlagsDebug.getInt(IntFlag("4", "test", 234))).isEqualTo(48)
+ assertThat(mFeatureFlagsClassicDebug.getInt(IntFlag("1", "test", 12))).isEqualTo(12)
+ assertThat(mFeatureFlagsClassicDebug.getInt(IntFlag("2", "test", 93))).isEqualTo(93)
+ assertThat(mFeatureFlagsClassicDebug.getInt(IntFlag("3", "test", 8))).isEqualTo(22)
+ assertThat(mFeatureFlagsClassicDebug.getInt(IntFlag("4", "test", 234))).isEqualTo(48)
}
@Test
@@ -334,17 +276,20 @@
whenever(flagManager.readFlagValue<Int>(eq("4"), any())).thenReturn(500)
whenever(flagManager.readFlagValue<Int>(eq("5"), any())).thenReturn(9519)
- assertThat(featureFlagsDebug.getInt(ResourceIntFlag("1", "test", 1001))).isEqualTo(88)
- assertThat(featureFlagsDebug.getInt(ResourceIntFlag("2", "test", 1002))).isEqualTo(61)
- assertThat(featureFlagsDebug.getInt(ResourceIntFlag("3", "test", 1003))).isEqualTo(20)
+ assertThat(mFeatureFlagsClassicDebug.getInt(ResourceIntFlag("1", "test", 1001)))
+ .isEqualTo(88)
+ assertThat(mFeatureFlagsClassicDebug.getInt(ResourceIntFlag("2", "test", 1002)))
+ .isEqualTo(61)
+ assertThat(mFeatureFlagsClassicDebug.getInt(ResourceIntFlag("3", "test", 1003)))
+ .isEqualTo(20)
Assert.assertThrows(NotFoundException::class.java) {
- featureFlagsDebug.getInt(ResourceIntFlag("4", "test", 1004))
+ mFeatureFlagsClassicDebug.getInt(ResourceIntFlag("4", "test", 1004))
}
// Test that resource is loaded (and validated) even when the setting is set.
// This prevents developers from not noticing when they reference an invalid resource.
Assert.assertThrows(NotFoundException::class.java) {
- featureFlagsDebug.getInt(ResourceIntFlag("5", "test", 1005))
+ mFeatureFlagsClassicDebug.getInt(ResourceIntFlag("5", "test", 1005))
}
}
@@ -424,11 +369,11 @@
whenever(flagManager.readFlagValue<String>(eq("1"), any())).thenReturn("original")
// gets the flag & cache it
- assertThat(featureFlagsDebug.getString(flag1)).isEqualTo("original")
+ assertThat(mFeatureFlagsClassicDebug.getString(flag1)).isEqualTo("original")
verify(flagManager, times(1)).readFlagValue(eq("1"), eq(StringFlagSerializer))
// hit the cache
- assertThat(featureFlagsDebug.getString(flag1)).isEqualTo("original")
+ assertThat(mFeatureFlagsClassicDebug.getString(flag1)).isEqualTo("original")
verifyNoMoreInteractions(flagManager)
// set the flag
@@ -436,7 +381,7 @@
verifyPutData("1", "{\"type\":\"string\",\"value\":\"new\"}", numReads = 2)
whenever(flagManager.readFlagValue<String>(eq("1"), any())).thenReturn("new")
- assertThat(featureFlagsDebug.getString(flag1)).isEqualTo("new")
+ assertThat(mFeatureFlagsClassicDebug.getString(flag1)).isEqualTo("new")
verify(flagManager, times(3)).readFlagValue(eq("1"), eq(StringFlagSerializer))
}
@@ -446,7 +391,7 @@
serverFlagReader.setFlagValue(flag.namespace, flag.name, false)
- assertThat(featureFlagsDebug.isEnabled(flag)).isFalse()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(flag)).isFalse()
}
@Test
@@ -454,32 +399,41 @@
val flag = UnreleasedFlag(name = "100", namespace = "test")
serverFlagReader.setFlagValue(flag.namespace, flag.name, true)
- assertThat(featureFlagsDebug.isEnabled(flag)).isTrue()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(flag)).isTrue()
}
@Test
fun serverSide_OverrideUncached_NoRestart() {
// No one has read the flag, so it's not in the cache.
serverFlagReader.setFlagValue(
- teamfoodableFlagA.namespace, teamfoodableFlagA.name, !teamfoodableFlagA.default)
+ teamfoodableFlagA.namespace,
+ teamfoodableFlagA.name,
+ !teamfoodableFlagA.default
+ )
verify(restarter, never()).restartSystemUI(anyString())
}
@Test
fun serverSide_Override_Restarts() {
// Read it to put it in the cache.
- featureFlagsDebug.isEnabled(teamfoodableFlagA)
+ mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)
serverFlagReader.setFlagValue(
- teamfoodableFlagA.namespace, teamfoodableFlagA.name, !teamfoodableFlagA.default)
+ teamfoodableFlagA.namespace,
+ teamfoodableFlagA.name,
+ !teamfoodableFlagA.default
+ )
verify(restarter).restartSystemUI(anyString())
}
@Test
fun serverSide_RedundantOverride_NoRestart() {
// Read it to put it in the cache.
- featureFlagsDebug.isEnabled(teamfoodableFlagA)
+ mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)
serverFlagReader.setFlagValue(
- teamfoodableFlagA.namespace, teamfoodableFlagA.name, teamfoodableFlagA.default)
+ teamfoodableFlagA.namespace,
+ teamfoodableFlagA.name,
+ teamfoodableFlagA.default
+ )
verify(restarter, never()).restartSystemUI(anyString())
}
@@ -500,13 +454,13 @@
.thenReturn("override7")
// WHEN the flags have been accessed
- assertThat(featureFlagsDebug.isEnabled(flag1)).isTrue()
- assertThat(featureFlagsDebug.isEnabled(flag2)).isTrue()
- assertThat(featureFlagsDebug.isEnabled(flag3)).isFalse()
- assertThat(featureFlagsDebug.getString(flag4)).isEmpty()
- assertThat(featureFlagsDebug.getString(flag5)).isEqualTo("flag5default")
- assertThat(featureFlagsDebug.getString(flag6)).isEqualTo("resource1006")
- assertThat(featureFlagsDebug.getString(flag7)).isEqualTo("override7")
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(flag1)).isTrue()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(flag2)).isTrue()
+ assertThat(mFeatureFlagsClassicDebug.isEnabled(flag3)).isFalse()
+ assertThat(mFeatureFlagsClassicDebug.getString(flag4)).isEmpty()
+ assertThat(mFeatureFlagsClassicDebug.getString(flag5)).isEqualTo("flag5default")
+ assertThat(mFeatureFlagsClassicDebug.getString(flag6)).isEqualTo("resource1006")
+ assertThat(mFeatureFlagsClassicDebug.getString(flag7)).isEqualTo("override7")
// THEN the dump contains the flags and the default values
val dump = dumpToString()
@@ -520,12 +474,15 @@
}
private fun verifyPutData(name: String, data: String, numReads: Int = 1) {
- inOrder(flagManager, globalSettings).apply {
- verify(flagManager, times(numReads)).readFlagValue(eq(name), any<FlagSerializer<*>>())
- verify(flagManager).nameToSettingsKey(eq(name))
- verify(globalSettings).putStringForUser(eq("key-$name"), eq(data), anyInt())
- verify(flagManager).dispatchListenersAndMaybeRestart(eq(name), any())
- }.verifyNoMoreInteractions()
+ inOrder(flagManager, globalSettings)
+ .apply {
+ verify(flagManager, times(numReads))
+ .readFlagValue(eq(name), any<FlagSerializer<*>>())
+ verify(flagManager).nameToSettingsKey(eq(name))
+ verify(globalSettings).putStringForUser(eq("key-$name"), eq(data), anyInt())
+ verify(flagManager).dispatchListenersAndMaybeRestart(eq(name), any())
+ }
+ .verifyNoMoreInteractions()
verifyNoMoreInteractions(flagManager, globalSettings)
}
@@ -545,7 +502,7 @@
private fun dumpToString(): String {
val sw = StringWriter()
val pw = PrintWriter(sw)
- featureFlagsDebug.dump(pw, emptyArray<String>())
+ mFeatureFlagsClassicDebug.dump(pw, emptyArray<String>())
pw.flush()
return sw.toString()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicReleaseTest.kt
similarity index 72%
rename from packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicReleaseTest.kt
index 16b4595..70d6dd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicReleaseTest.kt
@@ -26,16 +26,16 @@
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.never
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/**
* NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow
* overriding, and should never return any value other than the one provided as the default.
*/
@SmallTest
-class FeatureFlagsReleaseTest : SysuiTestCase() {
- private lateinit var featureFlagsRelease: FeatureFlagsRelease
+class FeatureFlagsClassicReleaseTest : SysuiTestCase() {
+ private lateinit var mFeatureFlagsClassicRelease: FeatureFlagsClassicRelease
@Mock private lateinit var mResources: Resources
@Mock private lateinit var mSystemProperties: SystemPropertiesHelper
@@ -43,21 +43,22 @@
private val flagMap = mutableMapOf<String, Flag<*>>()
private val serverFlagReader = ServerFlagReaderFake()
-
private val flagA = ReleasedFlag(name = "a", namespace = "test")
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
flagMap.put(flagA.name, flagA)
- featureFlagsRelease = FeatureFlagsRelease(
- mResources,
- mSystemProperties,
- serverFlagReader,
- flagMap,
- restarter)
+ mFeatureFlagsClassicRelease =
+ FeatureFlagsClassicRelease(
+ mResources,
+ mSystemProperties,
+ serverFlagReader,
+ flagMap,
+ restarter
+ )
- featureFlagsRelease.init()
+ mFeatureFlagsClassicRelease.init()
}
@Test
@@ -67,7 +68,7 @@
val flagNamespace = "test"
val flag = ResourceBooleanFlag(flagName, flagNamespace, flagResourceId)
whenever(mResources.getBoolean(flagResourceId)).thenReturn(true)
- assertThat(featureFlagsRelease.isEnabled(flag)).isTrue()
+ assertThat(mFeatureFlagsClassicRelease.isEnabled(flag)).isTrue()
}
@Test
@@ -77,16 +78,16 @@
whenever(mResources.getString(1003)).thenReturn(null)
whenever(mResources.getString(1004)).thenAnswer { throw NameNotFoundException() }
- assertThat(featureFlagsRelease.getString(
- ResourceStringFlag("1", "test", 1001))).isEqualTo("")
- assertThat(featureFlagsRelease.getString(
- ResourceStringFlag("2", "test", 1002))).isEqualTo("res2")
+ assertThat(mFeatureFlagsClassicRelease.getString(ResourceStringFlag("1", "test", 1001)))
+ .isEqualTo("")
+ assertThat(mFeatureFlagsClassicRelease.getString(ResourceStringFlag("2", "test", 1002)))
+ .isEqualTo("res2")
assertThrows(NullPointerException::class.java) {
- featureFlagsRelease.getString(ResourceStringFlag("3", "test", 1003))
+ mFeatureFlagsClassicRelease.getString(ResourceStringFlag("3", "test", 1003))
}
assertThrows(NameNotFoundException::class.java) {
- featureFlagsRelease.getString(ResourceStringFlag("4", "test", 1004))
+ mFeatureFlagsClassicRelease.getString(ResourceStringFlag("4", "test", 1004))
}
}
@@ -98,7 +99,7 @@
val flag = SysPropBooleanFlag(flagName, flagNamespace, flagDefault)
whenever(mSystemProperties.getBoolean(flagName, flagDefault)).thenReturn(flagDefault)
- assertThat(featureFlagsRelease.isEnabled(flag)).isEqualTo(flagDefault)
+ assertThat(mFeatureFlagsClassicRelease.isEnabled(flag)).isEqualTo(flagDefault)
}
@Test
@@ -107,7 +108,7 @@
serverFlagReader.setFlagValue(flag.namespace, flag.name, false)
- assertThat(featureFlagsRelease.isEnabled(flag)).isFalse()
+ assertThat(mFeatureFlagsClassicRelease.isEnabled(flag)).isFalse()
}
@Test
@@ -116,32 +117,29 @@
serverFlagReader.setFlagValue(flag.namespace, flag.name, true)
- assertThat(featureFlagsRelease.isEnabled(flag)).isFalse()
+ assertThat(mFeatureFlagsClassicRelease.isEnabled(flag)).isFalse()
}
@Test
fun serverSide_OverrideUncached_NoRestart() {
// No one has read the flag, so it's not in the cache.
- serverFlagReader.setFlagValue(
- flagA.namespace, flagA.name, !flagA.default)
+ serverFlagReader.setFlagValue(flagA.namespace, flagA.name, !flagA.default)
Mockito.verify(restarter, never()).restartSystemUI(Mockito.anyString())
}
@Test
fun serverSide_Override_Restarts() {
// Read it to put it in the cache.
- featureFlagsRelease.isEnabled(flagA)
- serverFlagReader.setFlagValue(
- flagA.namespace, flagA.name, !flagA.default)
+ mFeatureFlagsClassicRelease.isEnabled(flagA)
+ serverFlagReader.setFlagValue(flagA.namespace, flagA.name, !flagA.default)
Mockito.verify(restarter).restartSystemUI(Mockito.anyString())
}
@Test
fun serverSide_RedundantOverride_NoRestart() {
// Read it to put it in the cache.
- featureFlagsRelease.isEnabled(flagA)
- serverFlagReader.setFlagValue(
- flagA.namespace, flagA.name, flagA.default)
+ mFeatureFlagsClassicRelease.isEnabled(flagA)
+ serverFlagReader.setFlagValue(flagA.namespace, flagA.name, flagA.default)
Mockito.verify(restarter, never()).restartSystemUI(Mockito.anyString())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
index b02baa7..7c1325e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt
@@ -30,7 +30,7 @@
@SmallTest
class FlagCommandTest : SysuiTestCase() {
- @Mock private lateinit var featureFlags: FeatureFlagsDebug
+ @Mock private lateinit var featureFlags: FeatureFlagsClassicDebug
@Mock private lateinit var pw: PrintWriter
private val flagMap = mutableMapOf<String, Flag<*>>()
private val flagA = UnreleasedFlag("500", "test")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
new file mode 100644
index 0000000..632d149
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.keyevent.domain.interactor
+
+import android.view.KeyEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyEventInteractorTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ private lateinit var keyguardInteractorWithDependencies:
+ KeyguardInteractorFactory.WithDependencies
+ @Mock private lateinit var keyguardKeyEventInteractor: KeyguardKeyEventInteractor
+ @Mock private lateinit var backActionInteractor: BackActionInteractor
+
+ private lateinit var underTest: KeyEventInteractor
+
+ @Before
+ fun setup() {
+ keyguardInteractorWithDependencies = KeyguardInteractorFactory.create()
+ underTest =
+ KeyEventInteractor(
+ backActionInteractor,
+ keyguardKeyEventInteractor,
+ )
+ }
+
+ @Test
+ fun dispatchBackKey_notHandledByKeyguardKeyEventInteractor_handledByBackActionInteractor() {
+ val backKeyEventActionDown = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)
+ val backKeyEventActionUp = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)
+
+ // GIVEN back key ACTION_DOWN and ACTION_UP aren't handled by the keyguardKeyEventInteractor
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionDown))
+ .thenReturn(false)
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionUp))
+ .thenReturn(false)
+
+ // WHEN back key event ACTION_DOWN, the event is handled even though back isn't requested
+ assertThat(underTest.dispatchKeyEvent(backKeyEventActionDown)).isTrue()
+ // THEN back event isn't handled on ACTION_DOWN
+ verify(backActionInteractor, never()).onBackRequested()
+
+ // WHEN back key event ACTION_UP
+ assertThat(underTest.dispatchKeyEvent(backKeyEventActionUp)).isTrue()
+ // THEN back event is handled on ACTION_UP
+ verify(backActionInteractor).onBackRequested()
+ }
+
+ @Test
+ fun dispatchKeyEvent_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.dispatchKeyEvent(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun dispatchKeyEvent_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.dispatchKeyEvent(keyEvent)).isTrue()
+ }
+
+ @Test
+ fun interceptMediaKey_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.interceptMediaKey(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun interceptMediaKey_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.interceptMediaKey(keyEvent)).isTrue()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index dcaafe8..6fcf54c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.keyguard.data.repository
import android.graphics.Point
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
@@ -47,7 +47,7 @@
@SmallTest
@RoboPilotTest
@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class LightRevealScrimRepositoryTest : SysuiTestCase() {
private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
private lateinit var underTest: LightRevealScrimRepositoryImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
new file mode 100644
index 0000000..e0ae0c3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.media.AudioManager
+import android.media.session.MediaSessionLegacyHelper
+import android.view.KeyEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeController
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardKeyEventInteractorTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ private val actionDownVolumeDownKeyEvent =
+ KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN)
+ private val actionDownVolumeUpKeyEvent =
+ KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP)
+ private val backKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)
+ private val awakeWakefulnessMode =
+ WakefulnessModel(WakefulnessState.AWAKE, WakeSleepReason.OTHER, WakeSleepReason.OTHER)
+ private val asleepWakefulnessMode =
+ WakefulnessModel(WakefulnessState.ASLEEP, WakeSleepReason.OTHER, WakeSleepReason.OTHER)
+
+ private lateinit var keyguardInteractorWithDependencies:
+ KeyguardInteractorFactory.WithDependencies
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock private lateinit var shadeController: ShadeController
+ @Mock private lateinit var mediaSessionLegacyHelperWrapper: MediaSessionLegacyHelperWrapper
+ @Mock private lateinit var mediaSessionLegacyHelper: MediaSessionLegacyHelper
+ @Mock private lateinit var backActionInteractor: BackActionInteractor
+
+ private lateinit var underTest: KeyguardKeyEventInteractor
+
+ @Before
+ fun setup() {
+ whenever(mediaSessionLegacyHelperWrapper.getHelper(any()))
+ .thenReturn(mediaSessionLegacyHelper)
+ keyguardInteractorWithDependencies = KeyguardInteractorFactory.create()
+ underTest =
+ KeyguardKeyEventInteractor(
+ context,
+ statusBarStateController,
+ keyguardInteractorWithDependencies.keyguardInteractor,
+ statusBarKeyguardViewManager,
+ shadeController,
+ mediaSessionLegacyHelperWrapper,
+ backActionInteractor,
+ )
+ }
+
+ @Test
+ fun dispatchKeyEvent_volumeKey_dozing_handlesEvents() {
+ whenever(statusBarStateController.isDozing).thenReturn(true)
+
+ assertThat(underTest.dispatchKeyEvent(actionDownVolumeDownKeyEvent)).isTrue()
+ verify(mediaSessionLegacyHelper)
+ .sendVolumeKeyEvent(
+ eq(actionDownVolumeDownKeyEvent),
+ eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
+ eq(true)
+ )
+
+ assertThat(underTest.dispatchKeyEvent(actionDownVolumeUpKeyEvent)).isTrue()
+ verify(mediaSessionLegacyHelper)
+ .sendVolumeKeyEvent(
+ eq(actionDownVolumeUpKeyEvent),
+ eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
+ eq(true)
+ )
+ }
+
+ @Test
+ fun dispatchKeyEvent_volumeKey_notDozing_doesNotHandleEvents() {
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+
+ assertThat(underTest.dispatchKeyEvent(actionDownVolumeDownKeyEvent)).isFalse()
+ verify(mediaSessionLegacyHelper, never())
+ .sendVolumeKeyEvent(
+ eq(actionDownVolumeDownKeyEvent),
+ eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
+ eq(true)
+ )
+
+ assertThat(underTest.dispatchKeyEvent(actionDownVolumeUpKeyEvent)).isFalse()
+ verify(mediaSessionLegacyHelper, never())
+ .sendVolumeKeyEvent(
+ eq(actionDownVolumeUpKeyEvent),
+ eq(AudioManager.USE_DEFAULT_STREAM_TYPE),
+ eq(true)
+ )
+ }
+
+ @Test
+ fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_showsPrimaryBouncer() {
+ keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
+
+ verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU)
+ }
+
+ @Test
+ fun dispatchKeyEvent_menuActionUp_interactiveShadeLocked_collapsesShade() {
+ keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
+
+ verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU)
+ }
+
+ @Test
+ fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_doNothing() {
+ keyguardInteractorWithDependencies.repository.setWakefulnessModel(asleepWakefulnessMode)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
+
+ verifyActionsDoNothing(KeyEvent.KEYCODE_MENU)
+ }
+
+ @Test
+ fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_showsPrimaryBouncer() {
+ keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+ verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE)
+ }
+
+ @Test
+ fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() {
+ keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+
+ verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE)
+ }
+
+ @Test
+ fun dispatchKeyEvent_enterActionUp_interactiveKeyguard_showsPrimaryBouncer() {
+ keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+ verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER)
+ }
+
+ @Test
+ fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() {
+ keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+
+ verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER)
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_back_keyguard_onBackRequested() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.dispatchBackKeyEventPreIme()).thenReturn(true)
+
+ whenever(backActionInteractor.onBackRequested()).thenReturn(false)
+ assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isFalse()
+ verify(backActionInteractor).onBackRequested()
+ clearInvocations(backActionInteractor)
+
+ whenever(backActionInteractor.onBackRequested()).thenReturn(true)
+ assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isTrue()
+ verify(backActionInteractor).onBackRequested()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_back_keyguard_SBKVMdoesNotHandle_neverOnBackRequested() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.dispatchBackKeyEventPreIme()).thenReturn(false)
+ whenever(backActionInteractor.onBackRequested()).thenReturn(true)
+
+ assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isFalse()
+ verify(backActionInteractor, never()).onBackRequested()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_back_shade_neverOnBackRequested() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+ whenever(statusBarKeyguardViewManager.dispatchBackKeyEventPreIme()).thenReturn(true)
+ whenever(backActionInteractor.onBackRequested()).thenReturn(true)
+
+ assertThat(underTest.dispatchKeyEventPreIme(backKeyEvent)).isFalse()
+ verify(backActionInteractor, never()).onBackRequested()
+ }
+
+ @Test
+ fun interceptMediaKey_keyguard_SBKVMdoesNotHandle_doesNotHandleMediaKey() {
+ val keyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.interceptMediaKey(eq(keyEvent))).thenReturn(false)
+
+ assertThat(underTest.interceptMediaKey(keyEvent)).isFalse()
+ verify(statusBarKeyguardViewManager).interceptMediaKey(eq(keyEvent))
+ }
+
+ @Test
+ fun interceptMediaKey_keyguard_handleMediaKey() {
+ val keyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(statusBarKeyguardViewManager.interceptMediaKey(eq(keyEvent))).thenReturn(true)
+
+ assertThat(underTest.interceptMediaKey(keyEvent)).isTrue()
+ verify(statusBarKeyguardViewManager).interceptMediaKey(eq(keyEvent))
+ }
+
+ @Test
+ fun interceptMediaKey_shade_doesNotHandleMediaKey() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+
+ assertThat(
+ underTest.interceptMediaKey(
+ KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP)
+ )
+ )
+ .isFalse()
+ verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any())
+ }
+
+ private fun verifyActionUpCollapsesTheShade(keycode: Int) {
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
+
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
+ }
+
+ private fun verifyActionUpShowsPrimaryBouncer(keycode: Int) {
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true))
+ }
+
+ private fun verifyActionsDoNothing(keycode: Int) {
+ // action down: does nothing
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
+ verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+
+ // action up: doesNothing
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
+ verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index ef38d54..3576ec9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -281,11 +281,7 @@
underTest.onPreviewSlotSelected(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
)
- keyguardInteractor.previewMode.value =
- KeyguardInteractor.PreviewMode(
- isInPreviewMode = true,
- shouldHighlightSelectedAffordance = true,
- )
+ underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, true)
repository.setKeyguardShowing(false)
val latest = collectLastValue(underTest.startButton)
@@ -330,11 +326,7 @@
underTest.onPreviewSlotSelected(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
)
- keyguardInteractor.previewMode.value =
- KeyguardInteractor.PreviewMode(
- isInPreviewMode = true,
- shouldHighlightSelectedAffordance = true,
- )
+ underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, true)
repository.setKeyguardShowing(false)
val endButton = collectLastValue(underTest.endButton)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 05e933b..a14a1c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -45,7 +45,6 @@
private lateinit var testScope: TestScope
private lateinit var repository: FakeKeyguardRepository
private lateinit var keyguardInteractor: KeyguardInteractor
- @Mock private lateinit var keyguardQuickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel
@Before
fun setUp() {
@@ -63,10 +62,7 @@
keyguardInteractor = withDeps.keyguardInteractor
repository = withDeps.repository
- underTest = KeyguardRootViewModel(
- keyguardInteractor,
- keyguardQuickAffordancesCombinedViewModel,
- )
+ underTest = KeyguardRootViewModel(keyguardInteractor)
}
@Test
@@ -89,10 +85,7 @@
fun alpha_inPreviewMode_doesNotChange() =
testScope.runTest {
val value = collectLastValue(underTest.alpha)
- underTest.enablePreviewMode(
- initiallySelectedSlotId = null,
- shouldHighlightSelectedAffordance = false,
- )
+ underTest.enablePreviewMode()
Truth.assertThat(value()).isEqualTo(1f)
repository.setKeyguardAlpha(0.1f)
@@ -104,4 +97,4 @@
repository.setKeyguardAlpha(0f)
Truth.assertThat(value()).isEqualTo(1f)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 9781baa..64d3b82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -53,7 +53,6 @@
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.nano.SystemUIProtoDump;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.PluginManager;
@@ -65,6 +64,7 @@
import com.android.systemui.qs.external.TileLifecycleManager;
import com.android.systemui.qs.external.TileServiceKey;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
@@ -131,6 +131,8 @@
private FakeFeatureFlags mFeatureFlags;
+ private QSPipelineFlagsRepository mQSPipelineFlagsRepository;
+
private FakeExecutor mMainExecutor;
private QSTileHost mQSTileHost;
@@ -142,6 +144,7 @@
mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, false);
mFeatureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, false);
+ mQSPipelineFlagsRepository = new QSPipelineFlagsRepository(mFeatureFlags);
mMainExecutor = new FakeExecutor(new FakeSystemClock());
@@ -164,7 +167,7 @@
mQSTileHost = new TestQSTileHost(mContext, mDefaultFactory, mMainExecutor,
mPluginManager, mTunerService, () -> mAutoTiles, mShadeController,
mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
- mTileLifecycleManagerFactory, mUserFileManager, mFeatureFlags);
+ mTileLifecycleManagerFactory, mUserFileManager, mQSPipelineFlagsRepository);
mMainExecutor.runAllReady();
mSecureSettings.registerContentObserverForUser(SETTING, new ContentObserver(null) {
@@ -708,7 +711,7 @@
UserTracker userTracker, SecureSettings secureSettings,
CustomTileStatePersister customTileStatePersister,
TileLifecycleManager.Factory tileLifecycleManagerFactory,
- UserFileManager userFileManager, FeatureFlags featureFlags) {
+ UserFileManager userFileManager, QSPipelineFlagsRepository featureFlags) {
super(context, defaultFactory, mainExecutor, pluginManager,
tunerService, autoTiles, shadeController, qsLogger,
userTracker, secureSettings, customTileStatePersister,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
index 6c96576..a9f8ea0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
@@ -36,8 +36,8 @@
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -54,6 +54,7 @@
private const val TEST_SUBTITLE = "test_subtitle"
private const val TEST_CONTENT_DESCRIPTION = "test_content_description"
private const val TEST_STATE_DESCRIPTION = "test_state_description"
+ private const val TEST_DEFAULT_LABEL = "default_label"
private fun Tile.isEqualTo(other: Tile): Boolean {
return state == other.state &&
@@ -156,4 +157,14 @@
verify(editor).remove(KEY.toString())
}
+
+ @Test
+ fun testWithDefaultLabel_notStored() {
+ tile.setDefaultLabel(TEST_DEFAULT_LABEL)
+
+ `when`(sharedPreferences.getString(eq(KEY.toString()), any()))
+ .thenReturn(writeToString(tile))
+
+ assertThat(customTileStatePersister.readState(KEY)!!.label).isNull()
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 41240e5..244f627 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -25,6 +25,7 @@
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.os.Handler
+import android.os.Parcel
import android.service.quicksettings.IQSTileService
import android.service.quicksettings.Tile
import android.test.suitebuilder.annotation.SmallTest
@@ -33,6 +34,7 @@
import android.view.IWindowManager
import android.view.View
import com.android.internal.logging.MetricsLogger
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.view.LaunchableFrameLayout
@@ -47,6 +49,7 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
+import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertThrows
@@ -395,4 +398,45 @@
verify(tileServiceManager, never()).setBindRequested(true)
verify(tileService, never()).onStartListening()
}
+
+ @Test
+ fun testAlwaysUseDefaultLabelIfNoLabelIsSet() {
+ // Give it an icon to prevent issues
+ serviceInfo.icon = R.drawable.android
+
+ val label1 = "Label 1"
+ val label2 = "Label 2"
+
+ `when`(serviceInfo.loadLabel(any())).thenReturn(label1)
+ customTile.handleSetListening(true)
+ testableLooper.processAllMessages()
+ customTile.handleSetListening(false)
+ testableLooper.processAllMessages()
+
+ assertThat(customTile.state.label).isEqualTo(label1)
+
+ // Retrieve the tile as if bound (a separate copy)
+ val tile = copyTileUsingParcel(customTile.qsTile)
+
+ // Change the language
+ `when`(serviceInfo.loadLabel(any())).thenReturn(label2)
+
+ // Set the tile to listening and apply the tile (unmodified)
+ customTile.handleSetListening(true)
+ testableLooper.processAllMessages()
+ customTile.updateTileState(tile)
+ customTile.refreshState()
+ testableLooper.processAllMessages()
+
+ assertThat(customTile.state.label).isEqualTo(label2)
+ }
+
+ private fun copyTileUsingParcel(t: Tile): Tile {
+ val parcel = Parcel.obtain()
+ parcel.setDataPosition(0)
+ t.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+
+ return Tile.CREATOR.createFromParcel(parcel)
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 6689514..54a9360 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -42,6 +42,7 @@
import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
import com.android.systemui.qs.pipeline.domain.model.TileModel
+import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import com.android.systemui.qs.toProto
@@ -79,6 +80,7 @@
private val customTileAddedRepository: CustomTileAddedRepository =
FakeCustomTileAddedRepository()
private val featureFlags = FakeFeatureFlags()
+ private val pipelineFlags = QSPipelineFlagsRepository(featureFlags)
private val tileLifecycleManagerFactory = TLMFactory()
@Mock private lateinit var customTileStatePersister: CustomTileStatePersister
@@ -120,7 +122,7 @@
backgroundDispatcher = testDispatcher,
scope = testScope.backgroundScope,
logger = logger,
- featureFlags = featureFlags,
+ featureFlags = pipelineFlags,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
new file mode 100644
index 0000000..489221e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
@@ -0,0 +1,61 @@
+package com.android.systemui.qs.pipeline.shared
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameter
+import org.junit.runners.Parameterized.Parameters
+
+@SmallTest
+@RunWith(Parameterized::class)
+class QSPipelineFlagsRepositoryTest : SysuiTestCase() {
+ companion object {
+ @Parameters(
+ name =
+ """
+WHEN: qs_pipeline_new_host = {0}, qs_pipeline_auto_add = {1}
+THEN: pipelineNewHost = {2}, pipelineAutoAdd = {3}
+ """
+ )
+ @JvmStatic
+ fun data(): List<Array<Boolean>> =
+ (0 until 4).map { combination ->
+ val qs_pipeline_new_host = combination and 0b10 != 0
+ val qs_pipeline_auto_add = combination and 0b01 != 0
+ arrayOf(
+ qs_pipeline_new_host,
+ qs_pipeline_auto_add,
+ /* pipelineNewHost = */ qs_pipeline_new_host,
+ /* pipelineAutoAdd = */ qs_pipeline_new_host && qs_pipeline_auto_add,
+ )
+ }
+ }
+
+ @JvmField @Parameter(0) var qsPipelineNewHostFlag: Boolean = false
+ @JvmField @Parameter(1) var qsPipelineAutoAddFlag: Boolean = false
+ @JvmField @Parameter(2) var pipelineNewHostExpected: Boolean = false
+ @JvmField @Parameter(3) var pipelineAutoAddExpected: Boolean = false
+
+ private val featureFlags = FakeFeatureFlags()
+ private val pipelineFlags = QSPipelineFlagsRepository(featureFlags)
+
+ @Before
+ fun setUp() {
+ featureFlags.apply {
+ set(Flags.QS_PIPELINE_NEW_HOST, qsPipelineNewHostFlag)
+ set(Flags.QS_PIPELINE_AUTO_ADD, qsPipelineAutoAddFlag)
+ }
+ }
+
+ @Test
+ fun flagLogic() {
+ assertThat(pipelineFlags.pipelineHostEnabled).isEqualTo(pipelineNewHostExpected)
+ assertThat(pipelineFlags.pipelineAutoAddEnabled).isEqualTo(pipelineAutoAddExpected)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
index cbc3553..d1d3c17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
@@ -26,8 +26,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
@@ -74,8 +72,6 @@
private lateinit var backgroundDelayableExecutor: FakeExecutor
private lateinit var fontScalingTile: FontScalingTile
- val featureFlags = FakeFeatureFlags()
-
@Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable>
@Before
@@ -102,7 +98,6 @@
FakeSettings(),
FakeSettings(),
FakeSystemClock(),
- featureFlags,
userTracker,
backgroundDelayableExecutor,
)
@@ -117,18 +112,7 @@
}
@Test
- fun isAvailable_whenFlagIsFalse_returnsFalse() {
- featureFlags.set(Flags.ENABLE_FONT_SCALING_TILE, false)
-
- val isAvailable = fontScalingTile.isAvailable()
-
- assertThat(isAvailable).isFalse()
- }
-
- @Test
- fun isAvailable_whenFlagIsTrue_returnsTrue() {
- featureFlags.set(Flags.ENABLE_FONT_SCALING_TILE, true)
-
+ fun isAvailable_alwaysReturnsTrue() {
val isAvailable = fontScalingTile.isAvailable()
assertThat(isAvailable).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 3e20511..6098809 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -60,6 +60,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.wifi.WifiUtils;
+import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
@@ -190,6 +191,7 @@
public void setUp() {
mStaticMockSession = mockitoSession()
.mockStatic(SubscriptionManager.class)
+ .mockStatic(WifiDppIntentHelper.class)
.strictness(Strictness.LENIENT)
.startMocking();
MockitoAnnotations.initMocks(this);
@@ -230,6 +232,7 @@
mInternetDialogController.mActivityStarter = mActivityStarter;
mInternetDialogController.mWifiIconInjector = mWifiIconInjector;
mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, false);
+ mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, false);
mConfig = new Configuration(mContext.getResources().getConfiguration());
Configuration c2 = new Configuration(mConfig);
@@ -973,6 +976,29 @@
}
@Test
+ public void getConfiguratorQrCodeGeneratorIntentOrNull_wifiNotShareable_returnNull() {
+ mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, true);
+ when(mConnectedEntry.canShare()).thenReturn(false);
+ assertThat(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(
+ mConnectedEntry)).isNull();
+ }
+ @Test
+ public void getConfiguratorQrCodeGeneratorIntentOrNull_flagOff_returnNull() {
+ mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, false);
+ when(mConnectedEntry.canShare()).thenReturn(true);
+ assertThat(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(
+ mConnectedEntry)).isNull();
+ }
+
+ @Test
+ public void getConfiguratorQrCodeGeneratorIntentOrNull_wifiShareable() {
+ mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, true);
+ when(mConnectedEntry.canShare()).thenReturn(true);
+ assertThat(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(
+ mConnectedEntry)).isNotNull();
+ }
+
+ @Test
public void onStop_cleanUp() {
doReturn(SUB_ID).when(mTelephonyManager).getSubscriptionId();
assertThat(mInternetDialogController.mSubIdTelephonyManagerMap.get(SUB_ID)).isEqualTo(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index b59005a..2c74d4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -17,6 +17,7 @@
import android.app.AlertDialog;
import android.content.DialogInterface;
+import android.content.Intent;
import android.os.Handler;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
@@ -718,6 +719,26 @@
assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
}
+ @Test
+ public void updateDialog_shareWifiIntentNull_hideButton() {
+ when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
+ .thenReturn(null);
+
+ mInternetDialog.updateDialog(false);
+
+ assertThat(mInternetDialog.mShareWifiButton.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_shareWifiShareable_showButton() {
+ when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
+ .thenReturn(new Intent());
+
+ mInternetDialog.updateDialog(false);
+
+ assertThat(mInternetDialog.mShareWifiButton.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
boolean connectedWifiVisible) {
mEthernet.setVisibility(ethernetVisible ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index eb4ae1a..7aeafeb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -539,6 +539,23 @@
}
@Test
+ public void onKeyguardStatusViewHeightChange_animatesNextTopPaddingChangeForNSSL() {
+ ArgumentCaptor<View.OnLayoutChangeListener> captor =
+ ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
+ verify(mKeyguardStatusView).addOnLayoutChangeListener(captor.capture());
+ View.OnLayoutChangeListener listener = captor.getValue();
+
+ clearInvocations(mNotificationStackScrollLayoutController);
+
+ when(mKeyguardStatusView.getHeight()).thenReturn(0);
+ listener.onLayoutChange(mKeyguardStatusView, /* left= */ 0, /* top= */ 0, /* right= */
+ 0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */
+ 0, /* oldBottom = */ 200);
+
+ verify(mNotificationStackScrollLayoutController).animateNextTopPaddingChange();
+ }
+
+ @Test
public void testCanCollapsePanelOnTouch_trueForKeyGuard() {
mStatusBarStateController.setState(KEYGUARD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 1edeeff..3cce423 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -18,6 +18,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import android.view.KeyEvent
import android.view.MotionEvent
import android.view.ViewGroup
import androidx.test.filters.SmallTest
@@ -38,6 +39,7 @@
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -118,8 +120,10 @@
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
+ @Mock lateinit var keyEventInteractor: KeyEventInteractor
private val notificationExpansionRepository = NotificationExpansionRepository()
+ private lateinit var fakeClock: FakeSystemClock
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
private lateinit var interactionEventHandler: InteractionEventHandler
@@ -148,6 +152,7 @@
featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
testScope = TestScope()
+ fakeClock = FakeSystemClock()
underTest =
NotificationShadeWindowViewController(
lockscreenShadeTransitionController,
@@ -180,7 +185,7 @@
primaryBouncerToGoneTransitionViewModel,
notificationExpansionRepository,
featureFlags,
- FakeSystemClock(),
+ fakeClock,
BouncerMessageInteractor(
FakeBouncerMessageRepository(),
mock(BouncerMessageFactory::class.java),
@@ -188,7 +193,8 @@
CountDownTimerUtil(),
featureFlags
),
- BouncerLogger(logcatLogBuffer("BouncerLog"))
+ BouncerLogger(logcatLogBuffer("BouncerLog")),
+ keyEventInteractor,
)
underTest.setupExpandedStatusBar()
@@ -328,6 +334,33 @@
}
@Test
+ fun handleDispatchTouchEvent_launchAnimationRunningTimesOut() =
+ testScope.runTest {
+ // GIVEN touch dispatcher in a state that returns true
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()).thenReturn(
+ true
+ )
+ assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue()
+
+ // WHEN launch animation is running for 2 seconds
+ fakeClock.setUptimeMillis(10000)
+ underTest.setExpandAnimationRunning(true)
+ fakeClock.advanceTime(2000)
+
+ // THEN touch is ignored
+ assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isFalse()
+
+ // WHEN Launch animation is running for 6 seconds
+ fakeClock.advanceTime(4000)
+
+ // THEN move is ignored, down is handled, and window is notified
+ assertThat(interactionEventHandler.handleDispatchTouchEvent(MOVE_EVENT)).isFalse()
+ assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue()
+ verify(notificationShadeWindowController).setLaunchingActivity(false)
+ }
+
+ @Test
fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() {
// down event should be intercepted by keyguardViewManager
whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
@@ -345,8 +378,30 @@
verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
}
+ @Test
+ fun forwardsDispatchKeyEvent() {
+ val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)
+ interactionEventHandler.dispatchKeyEvent(keyEvent)
+ verify(keyEventInteractor).dispatchKeyEvent(keyEvent)
+ }
+
+ @Test
+ fun forwardsDispatchKeyEventPreIme() {
+ val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)
+ interactionEventHandler.dispatchKeyEventPreIme(keyEvent)
+ verify(keyEventInteractor).dispatchKeyEventPreIme(keyEvent)
+ }
+
+ @Test
+ fun forwardsInterceptMediaKey() {
+ val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP)
+ interactionEventHandler.interceptMediaKey(keyEvent)
+ verify(keyEventInteractor).interceptMediaKey(keyEvent)
+ }
+
companion object {
private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
private const val VIEW_BOTTOM = 100
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 829184c..66d48d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -38,6 +38,7 @@
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
@@ -198,7 +199,8 @@
CountDownTimerUtil(),
featureFlags
),
- BouncerLogger(logcatLogBuffer("BouncerLog"))
+ BouncerLogger(logcatLogBuffer("BouncerLog")),
+ Mockito.mock(KeyEventInteractor::class.java),
)
controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
index 31bfa3fd..5fa6b3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -50,6 +51,12 @@
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
+import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger;
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel;
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import com.android.systemui.utils.os.FakeHandler;
@@ -61,6 +68,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -95,6 +106,18 @@
private TestableLooper mTestableLooper;
@Mock
private ShadeCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
+ @Mock
+ private MobileUiAdapter mMobileUiAdapter;
+ @Mock
+ private MobileIconsViewModel mMobileIconsViewModel;
+ @Mock
+ private ShadeCarrierGroupMobileIconViewModel mShadeCarrierGroupMobileIconViewModel;
+ @Mock
+ private MobileViewLogger mMobileViewLogger;
+ @Mock
+ private MobileContextProvider mMobileContextProvider;
+ @Mock
+ private StatusBarPipelineFlags mStatusBarPipelineFlags;
private FakeSlotIndexResolver mSlotIndexResolver;
private ClickListenerTextView mNoCarrierTextView;
@@ -133,16 +156,35 @@
mSlotIndexResolver = new FakeSlotIndexResolver();
+ when(mMobileUiAdapter.getMobileIconsViewModel()).thenReturn(mMobileIconsViewModel);
+
mShadeCarrierGroupController = new ShadeCarrierGroupController.Builder(
- mActivityStarter, handler, TestableLooper.get(this).getLooper(),
- mNetworkController, mCarrierTextControllerBuilder, mContext, mCarrierConfigTracker,
- mSlotIndexResolver)
+ mActivityStarter,
+ handler,
+ TestableLooper.get(this).getLooper(),
+ mNetworkController,
+ mCarrierTextControllerBuilder,
+ mContext,
+ mCarrierConfigTracker,
+ mSlotIndexResolver,
+ mMobileUiAdapter,
+ mMobileContextProvider,
+ mStatusBarPipelineFlags
+ )
.setShadeCarrierGroup(mShadeCarrierGroup)
.build();
mShadeCarrierGroupController.setListening(true);
}
+ private void setupWithNewPipeline() {
+ when(mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()).thenReturn(true);
+ when(mMobileContextProvider.getMobileContextForSub(anyInt(), any())).thenReturn(mContext);
+ when(mMobileIconsViewModel.getLogger()).thenReturn(mMobileViewLogger);
+ when(mMobileIconsViewModel.viewModelForSub(anyInt(), any()))
+ .thenReturn(mShadeCarrierGroupMobileIconViewModel);
+ }
+
@Test
public void testInitiallyMultiCarrier() {
assertFalse(mShadeCarrierGroupController.isSingleCarrier());
@@ -406,6 +448,129 @@
verify(mOnSingleCarrierChangedListener, never()).onSingleCarrierChanged(anyBoolean());
}
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ @Test
+ public void testUpdateModernMobileIcons_addSubscription() {
+ setupWithNewPipeline();
+
+ mShadeCarrier1.setVisibility(View.GONE);
+ mShadeCarrier2.setVisibility(View.GONE);
+ mShadeCarrier3.setVisibility(View.GONE);
+
+ List<Integer> subIds = new ArrayList<>();
+ subIds.add(0);
+ mShadeCarrierGroupController.updateModernMobileIcons(subIds);
+
+ verify(mShadeCarrier1).addModernMobileView(any());
+ verify(mShadeCarrier2, never()).addModernMobileView(any());
+ verify(mShadeCarrier3, never()).addModernMobileView(any());
+
+ resetShadeCarriers();
+
+ subIds.add(1);
+ mShadeCarrierGroupController.updateModernMobileIcons(subIds);
+
+ verify(mShadeCarrier1, times(1)).removeModernMobileView();
+
+ verify(mShadeCarrier1).addModernMobileView(any());
+ verify(mShadeCarrier2).addModernMobileView(any());
+ verify(mShadeCarrier3, never()).addModernMobileView(any());
+ }
+
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ @Test
+ public void testUpdateModernMobileIcons_removeSubscription() {
+ setupWithNewPipeline();
+
+ List<Integer> subIds = new ArrayList<>();
+ subIds.add(0);
+ subIds.add(1);
+ mShadeCarrierGroupController.updateModernMobileIcons(subIds);
+
+ verify(mShadeCarrier1).addModernMobileView(any());
+ verify(mShadeCarrier2).addModernMobileView(any());
+ verify(mShadeCarrier3, never()).addModernMobileView(any());
+
+ resetShadeCarriers();
+
+ subIds.remove(1);
+ mShadeCarrierGroupController.updateModernMobileIcons(subIds);
+
+ verify(mShadeCarrier1, times(1)).removeModernMobileView();
+ verify(mShadeCarrier2, times(1)).removeModernMobileView();
+
+ verify(mShadeCarrier1).addModernMobileView(any());
+ verify(mShadeCarrier2, never()).addModernMobileView(any());
+ verify(mShadeCarrier3, never()).addModernMobileView(any());
+ }
+
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ @Test
+ public void testUpdateModernMobileIcons_removeSubscriptionOutOfOrder() {
+ setupWithNewPipeline();
+
+ List<Integer> subIds = new ArrayList<>();
+ subIds.add(0);
+ subIds.add(1);
+ subIds.add(2);
+ mShadeCarrierGroupController.updateModernMobileIcons(subIds);
+
+ verify(mShadeCarrier1).addModernMobileView(any());
+ verify(mShadeCarrier2).addModernMobileView(any());
+ verify(mShadeCarrier3).addModernMobileView(any());
+
+ resetShadeCarriers();
+
+ subIds.remove(1);
+ mShadeCarrierGroupController.updateModernMobileIcons(subIds);
+
+ verify(mShadeCarrier1).removeModernMobileView();
+ verify(mShadeCarrier2).removeModernMobileView();
+ verify(mShadeCarrier3).removeModernMobileView();
+
+ verify(mShadeCarrier1).addModernMobileView(any());
+ verify(mShadeCarrier2, never()).addModernMobileView(any());
+ verify(mShadeCarrier3).addModernMobileView(any());
+ }
+
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ @Test
+ public void testProcessSubIdList_moreSubsThanSimSlots_listLimitedToMax() {
+ setupWithNewPipeline();
+
+ List<Integer> subIds = Arrays.asList(0, 1, 2, 2);
+
+ assertThat(mShadeCarrierGroupController.processSubIdList(subIds).size()).isEqualTo(3);
+ }
+
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ @Test
+ public void testProcessSubIdList_invalidSimSlotIndexFilteredOut() {
+ setupWithNewPipeline();
+
+ List<Integer> subIds = Arrays.asList(0, 1, -1);
+
+ List<ShadeCarrierGroupController.IconData> processedSubs =
+ mShadeCarrierGroupController.processSubIdList(subIds);
+ assertThat(processedSubs).hasSize(2);
+ assertThat(processedSubs.get(0).subId).isNotEqualTo(-1);
+ assertThat(processedSubs.get(1).subId).isNotEqualTo(-1);
+ }
+
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ @Test
+ public void testProcessSubIdList_indexGreaterThanSimSlotsFilteredOut() {
+ setupWithNewPipeline();
+
+ List<Integer> subIds = Arrays.asList(0, 4);
+
+ List<ShadeCarrierGroupController.IconData> processedSubs =
+ mShadeCarrierGroupController.processSubIdList(subIds);
+ assertThat(processedSubs).hasSize(1);
+ assertThat(processedSubs.get(0).subId).isNotEqualTo(4);
+ }
+
+
@Test
public void testOnlyInternalViewsHaveClickableListener() {
ArgumentCaptor<View.OnClickListener> captor =
@@ -447,6 +612,12 @@
.isEqualTo(Settings.ACTION_WIRELESS_SETTINGS);
}
+ private void resetShadeCarriers() {
+ reset(mShadeCarrier1);
+ reset(mShadeCarrier2);
+ reset(mShadeCarrier3);
+ }
+
private class FakeSlotIndexResolver implements ShadeCarrierGroupController.SlotIndexResolver {
public boolean overrideInvalid;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 9495fdd..ecaf137 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -227,6 +227,25 @@
}
@Test
+ fun activeClockId_changeAfterPluginConnected() {
+ val plugin1 = FakeClockPlugin()
+ .addClock("clock_1", "clock 1")
+ .addClock("clock_2", "clock 2")
+
+ val plugin2 = FakeClockPlugin()
+ .addClock("clock_3", "clock 3", { mockClock })
+ .addClock("clock_4", "clock 4")
+
+ registry.applySettings(ClockSettings("clock_3", null))
+
+ pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle)
+ assertEquals(DEFAULT_CLOCK_ID, registry.activeClockId)
+
+ pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
+ assertEquals("clock_3", registry.activeClockId)
+ }
+
+ @Test
fun createDefaultClock_pluginDisconnected() {
val plugin1 = FakeClockPlugin()
.addClock("clock_1", "clock 1")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt
new file mode 100644
index 0000000..b8792a8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.icon.ui.viewbinder
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.NotificationListener
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.util.mockito.whenever
+import com.android.wm.shell.bubbles.Bubbles
+import java.util.Optional
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() {
+ @Mock private lateinit var notifListener: NotificationListener
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var wakeUpCoordinator: NotificationWakeUpCoordinator
+ @Mock private lateinit var keyguardBypassController: KeyguardBypassController
+ @Mock private lateinit var notifMediaManager: NotificationMediaManager
+ @Mock private lateinit var dozeParams: DozeParameters
+ @Mock private lateinit var sectionStyleProvider: SectionStyleProvider
+ @Mock private lateinit var darkIconDispatcher: DarkIconDispatcher
+ @Mock private lateinit var statusBarWindowController: StatusBarWindowController
+ @Mock private lateinit var screenOffAnimController: ScreenOffAnimationController
+ @Mock private lateinit var bubbles: Bubbles
+ @Mock private lateinit var demoModeController: DemoModeController
+ @Mock private lateinit var aodIcons: NotificationIconContainer
+ @Mock private lateinit var featureFlags: FeatureFlags
+
+ private val shelfViewModel = NotificationIconContainerShelfViewModel()
+ private val statusBarViewModel = NotificationIconContainerStatusBarViewModel()
+ private val aodViewModel = NotificationIconContainerAlwaysOnDisplayViewModel()
+
+ private lateinit var underTest: NotificationIconAreaControllerViewBinderWrapperImpl
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ NotificationIconAreaControllerViewBinderWrapperImpl(
+ mContext,
+ statusBarStateController,
+ wakeUpCoordinator,
+ keyguardBypassController,
+ notifMediaManager,
+ notifListener,
+ dozeParams,
+ sectionStyleProvider,
+ Optional.of(bubbles),
+ demoModeController,
+ darkIconDispatcher,
+ featureFlags,
+ statusBarWindowController,
+ screenOffAnimController,
+ shelfViewModel,
+ statusBarViewModel,
+ aodViewModel,
+ )
+ }
+
+ @Test
+ fun testNotificationIcons_settingHideIcons() {
+ underTest.settingsListener.onStatusBarIconsBehaviorChanged(true)
+ assertFalse(underTest.shouldShowLowPriorityIcons())
+ }
+
+ @Test
+ fun testNotificationIcons_settingShowIcons() {
+ underTest.settingsListener.onStatusBarIconsBehaviorChanged(false)
+ assertTrue(underTest.shouldShowLowPriorityIcons())
+ }
+
+ @Test
+ fun testAppearResetsTranslation() {
+ underTest.setupAodIcons(aodIcons)
+ whenever(dozeParams.shouldControlScreenOff()).thenReturn(false)
+ underTest.appearAodIcons()
+ verify(aodIcons).translationY = 0f
+ verify(aodIcons).alpha = 1.0f
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 8545b89..3ad3c15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -18,6 +18,8 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
+
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -29,8 +31,10 @@
import android.app.StatusBarManager;
import android.os.PowerManager;
import android.os.UserHandle;
+import android.os.VibrationEffect;
import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
+import android.view.HapticFeedbackConstants;
import android.view.WindowInsets;
import androidx.test.filters.SmallTest;
@@ -42,6 +46,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSHost;
@@ -98,6 +103,7 @@
@Mock private UserTracker mUserTracker;
@Mock private QSHost mQSHost;
@Mock private ActivityStarter mActivityStarter;
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
@@ -134,7 +140,8 @@
mCameraLauncherLazy,
mUserTracker,
mQSHost,
- mActivityStarter);
+ mActivityStarter,
+ mFeatureFlags);
when(mUserTracker.getUserHandle()).thenReturn(
UserHandle.of(ActivityManager.getCurrentUser()));
@@ -241,4 +248,24 @@
verifyZeroInteractions(mSystemBarAttributesListener);
}
+
+ @Test
+ public void vibrateOnNavigationKeyDown_oneWayHapticsDisabled_usesVibrate() {
+ mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
+
+ mSbcqCallbacks.vibrateOnNavigationKeyDown();
+
+ verify(mVibratorHelper).vibrate(VibrationEffect.EFFECT_TICK);
+ }
+
+ @Test
+ public void vibrateOnNavigationKeyDown_oneWayHapticsEnabled_usesPerformHapticFeedback() {
+ mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
+
+ mSbcqCallbacks.vibrateOnNavigationKeyDown();
+
+ verify(mShadeViewController).performHapticFeedback(
+ HapticFeedbackConstants.GESTURE_START
+ );
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
index 8e1dcf0..1b8cfd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
@@ -48,7 +48,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class NotificationIconAreaControllerTest extends SysuiTestCase {
+public class LegacyNotificationIconAreaControllerImplTest extends SysuiTestCase {
@Mock
private NotificationListener mListener;
@@ -70,7 +70,7 @@
StatusBarWindowController mStatusBarWindowController;
@Mock
ScreenOffAnimationController mScreenOffAnimationController;
- private NotificationIconAreaController mController;
+ private LegacyNotificationIconAreaControllerImpl mController;
@Mock
private Bubbles mBubbles;
@Mock private DemoModeController mDemoModeController;
@@ -82,7 +82,7 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mController = new NotificationIconAreaController(
+ mController = new LegacyNotificationIconAreaControllerImpl(
mContext,
mStatusBarStateController,
mWakeUpCoordinator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 0dc1d9a..6b3bd22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1802,6 +1802,15 @@
assertFalse(ScrimState.UNLOCKED.mAnimateChange);
}
+ @Test
+ public void testNotifScrimAlpha_1f_afterUnlockFinishedAndExpanded() {
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ when(mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()).thenReturn(true);
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ mScrimController.onUnlockAnimationFinished();
+ assertAlphaAfterExpansion(mNotificationsScrim, 1f, 1f);
+ }
+
private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
mScrimController.setRawPanelExpansionFraction(expansion);
finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index 50ee6a3..ff28753 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -52,12 +52,19 @@
override val cdmaRoaming = MutableStateFlow(false)
- override val networkName =
- MutableStateFlow<NetworkNameModel>(NetworkNameModel.Default("default"))
+ override val networkName: MutableStateFlow<NetworkNameModel> =
+ MutableStateFlow(NetworkNameModel.Default(DEFAULT_NETWORK_NAME))
+
+ override val carrierName: MutableStateFlow<NetworkNameModel> =
+ MutableStateFlow(NetworkNameModel.Default(DEFAULT_NETWORK_NAME))
override val isAllowedDuringAirplaneMode = MutableStateFlow(false)
fun setDataEnabled(enabled: Boolean) {
_dataEnabled.value = enabled
}
+
+ companion object {
+ const val DEFAULT_NETWORK_NAME = "default name"
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index 3591c17..99e4030 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -75,7 +75,11 @@
override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
return subIdRepos[subId]
- ?: FakeMobileConnectionRepository(subId, tableLogBuffer).also { subIdRepos[subId] = it }
+ ?: FakeMobileConnectionRepository(
+ subId,
+ tableLogBuffer,
+ )
+ .also { subIdRepos[subId] = it }
}
override val defaultDataSubRatConfig = MutableStateFlow(MobileMappings.Config())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 5a887eb..d005972 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -243,13 +243,29 @@
private val IMMEDIATE = Dispatchers.Main.immediate
private const val SUB_1_ID = 1
+ private const val SUB_1_NAME = "Carrier $SUB_1_ID"
private val SUB_1 =
- mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
- private val MODEL_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_1_ID)
+ whenever(it.carrierName).thenReturn(SUB_1_NAME)
+ }
+ private val MODEL_1 =
+ SubscriptionModel(
+ subscriptionId = SUB_1_ID,
+ carrierName = SUB_1_NAME,
+ )
private const val SUB_2_ID = 2
+ private const val SUB_2_NAME = "Carrier $SUB_2_ID"
private val SUB_2 =
- mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) }
- private val MODEL_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_2_ID)
+ whenever(it.carrierName).thenReturn(SUB_2_NAME)
+ }
+ private val MODEL_2 =
+ SubscriptionModel(
+ subscriptionId = SUB_2_ID,
+ carrierName = SUB_2_NAME,
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
index 7573b28..57f97ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
@@ -38,7 +38,6 @@
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -140,6 +139,7 @@
launch { conn.carrierNetworkChangeActive.collect {} }
launch { conn.isRoaming.collect {} }
launch { conn.networkName.collect {} }
+ launch { conn.carrierName.collect {} }
launch { conn.isEmergencyOnly.collect {} }
launch { conn.dataConnectionState.collect {} }
}
@@ -163,6 +163,8 @@
assertThat(conn.isRoaming.value).isEqualTo(model.roaming)
assertThat(conn.networkName.value)
.isEqualTo(NetworkNameModel.IntentDerived(model.name))
+ assertThat(conn.carrierName.value)
+ .isEqualTo(NetworkNameModel.SubscriptionDerived("${model.name} ${model.subId}"))
// TODO(b/261029387): check these once we start handling them
assertThat(conn.isEmergencyOnly.value).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
index efaf152..2712b70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
@@ -546,6 +546,7 @@
launch { conn.carrierNetworkChangeActive.collect {} }
launch { conn.isRoaming.collect {} }
launch { conn.networkName.collect {} }
+ launch { conn.carrierName.collect {} }
launch { conn.isEmergencyOnly.collect {} }
launch { conn.dataConnectionState.collect {} }
}
@@ -571,6 +572,8 @@
assertThat(conn.isRoaming.value).isEqualTo(model.roaming)
assertThat(conn.networkName.value)
.isEqualTo(NetworkNameModel.IntentDerived(model.name))
+ assertThat(conn.carrierName.value)
+ .isEqualTo(NetworkNameModel.SubscriptionDerived("${model.name} ${model.subId}"))
// TODO(b/261029387) check these once we start handling them
assertThat(conn.isEmergencyOnly.value).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index 3dd2eaf..9c0cb17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_EMERGENCY
@@ -43,6 +44,7 @@
import java.io.PrintWriter
import java.io.StringWriter
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -79,28 +81,51 @@
private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>()
private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>()
+ private val subscriptionModel =
+ MutableStateFlow(
+ SubscriptionModel(
+ subscriptionId = SUB_ID,
+ carrierName = DEFAULT_NAME,
+ )
+ )
+
private lateinit var mobileRepo: FakeMobileConnectionRepository
private lateinit var carrierMergedRepo: FakeMobileConnectionRepository
@Before
fun setUp() {
- mobileRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer)
+ mobileRepo =
+ FakeMobileConnectionRepository(
+ SUB_ID,
+ tableLogBuffer,
+ )
carrierMergedRepo =
- FakeMobileConnectionRepository(SUB_ID, tableLogBuffer).apply {
- // Mimicks the real carrier merged repository
- this.isAllowedDuringAirplaneMode.value = true
- }
+ FakeMobileConnectionRepository(
+ SUB_ID,
+ tableLogBuffer,
+ )
+ .apply {
+ // Mimicks the real carrier merged repository
+ this.isAllowedDuringAirplaneMode.value = true
+ }
whenever(
mobileFactory.build(
eq(SUB_ID),
any(),
- eq(DEFAULT_NAME),
+ any(),
+ eq(DEFAULT_NAME_MODEL),
eq(SEP),
)
)
.thenReturn(mobileRepo)
- whenever(carrierMergedFactory.build(eq(SUB_ID), any())).thenReturn(carrierMergedRepo)
+ whenever(
+ carrierMergedFactory.build(
+ eq(SUB_ID),
+ any(),
+ )
+ )
+ .thenReturn(carrierMergedRepo)
}
@Test
@@ -120,7 +145,8 @@
.build(
SUB_ID,
tableLogBuffer,
- DEFAULT_NAME,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
SEP,
)
}
@@ -138,7 +164,11 @@
assertThat(underTest.activeRepo.value).isEqualTo(mobileRepo)
assertThat(underTest.operatorAlphaShort.value).isEqualTo(nonCarrierMergedName)
- verify(carrierMergedFactory, never()).build(SUB_ID, tableLogBuffer)
+ verify(carrierMergedFactory, never())
+ .build(
+ SUB_ID,
+ tableLogBuffer,
+ )
}
@Test
@@ -348,7 +378,8 @@
factory.build(
SUB_ID,
startingIsCarrierMerged = false,
- DEFAULT_NAME,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
SEP,
)
@@ -356,7 +387,8 @@
factory.build(
SUB_ID,
startingIsCarrierMerged = false,
- DEFAULT_NAME,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
SEP,
)
@@ -388,7 +420,8 @@
factory.build(
SUB_ID,
startingIsCarrierMerged = false,
- DEFAULT_NAME,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
SEP,
)
@@ -397,7 +430,8 @@
factory.build(
SUB_ID,
startingIsCarrierMerged = true,
- DEFAULT_NAME,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
SEP,
)
@@ -623,7 +657,8 @@
SUB_ID,
startingIsCarrierMerged,
tableLogBuffer,
- DEFAULT_NAME,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
SEP,
testScope.backgroundScope,
mobileFactory,
@@ -639,8 +674,9 @@
val realRepo =
MobileConnectionRepositoryImpl(
SUB_ID,
- defaultNetworkName = NetworkNameModel.Default("default"),
- networkNameSeparator = SEP,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
+ SEP,
telephonyManager,
systemUiCarrierConfig = mock(),
fakeBroadcastDispatcher,
@@ -654,7 +690,8 @@
mobileFactory.build(
eq(SUB_ID),
any(),
- eq(DEFAULT_NAME),
+ any(),
+ eq(DEFAULT_NAME_MODEL),
eq(SEP),
)
)
@@ -677,7 +714,13 @@
testScope.backgroundScope,
wifiRepository,
)
- whenever(carrierMergedFactory.build(eq(SUB_ID), any())).thenReturn(realRepo)
+ whenever(
+ carrierMergedFactory.build(
+ eq(SUB_ID),
+ any(),
+ )
+ )
+ .thenReturn(realRepo)
return realRepo
}
@@ -690,7 +733,8 @@
private companion object {
const val SUB_ID = 42
- private val DEFAULT_NAME = NetworkNameModel.Default("default name")
+ private val DEFAULT_NAME = "default name"
+ private val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME)
private const val SEP = "-"
private const val BUFFER_SEPARATOR = "|"
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 1ff737b..e50e5e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -62,6 +62,7 @@
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.configWithOverride
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
@@ -78,6 +79,7 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -109,6 +111,14 @@
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
+ private val subscriptionModel: MutableStateFlow<SubscriptionModel?> =
+ MutableStateFlow(
+ SubscriptionModel(
+ subscriptionId = SUB_1_ID,
+ carrierName = DEFAULT_NAME,
+ )
+ )
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -119,7 +129,8 @@
underTest =
MobileConnectionRepositoryImpl(
SUB_1_ID,
- DEFAULT_NAME,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
SEP,
telephonyManager,
systemUiCarrierConfig,
@@ -179,6 +190,7 @@
// gsmLevel updates, no change to cdmaLevel
strength = signalStrength(gsmLevel = 3, cdmaLevel = 2, isGsm = true)
+ callback.onSignalStrengthsChanged(strength)
assertThat(latest).isEqualTo(2)
@@ -638,12 +650,51 @@
}
@Test
+ fun networkNameForSubId_updates() =
+ testScope.runTest {
+ var latest: NetworkNameModel? = null
+ val job = underTest.carrierName.onEach { latest = it }.launchIn(this)
+
+ subscriptionModel.value =
+ SubscriptionModel(
+ subscriptionId = SUB_1_ID,
+ carrierName = DEFAULT_NAME,
+ )
+
+ assertThat(latest?.name).isEqualTo(DEFAULT_NAME)
+
+ val updatedName = "Derived Carrier"
+ subscriptionModel.value =
+ SubscriptionModel(
+ subscriptionId = SUB_1_ID,
+ carrierName = updatedName,
+ )
+
+ assertThat(latest?.name).isEqualTo(updatedName)
+
+ job.cancel()
+ }
+
+ @Test
+ fun networkNameForSubId_defaultWhenSubscriptionModelNull() =
+ testScope.runTest {
+ var latest: NetworkNameModel? = null
+ val job = underTest.carrierName.onEach { latest = it }.launchIn(this)
+
+ subscriptionModel.value = null
+
+ assertThat(latest?.name).isEqualTo(DEFAULT_NAME)
+
+ job.cancel()
+ }
+
+ @Test
fun networkName_default() =
testScope.runTest {
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
- assertThat(latest).isEqualTo(DEFAULT_NAME)
+ assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
job.cancel()
}
@@ -701,7 +752,7 @@
fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intentWithoutInfo)
- assertThat(latest).isEqualTo(DEFAULT_NAME)
+ assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
job.cancel()
}
@@ -852,8 +903,9 @@
companion object {
private const val SUB_1_ID = 1
- private val DEFAULT_NAME = NetworkNameModel.Default("default name")
- private const val SEP = "-"
+ private val DEFAULT_NAME = "Fake Mobile Network"
+ private val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME)
+ private val SEP = "-"
private const val SPN = "testSpn"
private const val PLMN = "testPlmn"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index 4f15aed..ea60aa7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -36,6 +36,7 @@
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
@@ -47,6 +48,7 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -97,6 +99,7 @@
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var logger: MobileInputLogger
@Mock private lateinit var tableLogger: TableLogBuffer
+ @Mock private lateinit var subscriptionModel: StateFlow<SubscriptionModel?>
private val mobileMappings = FakeMobileMappingsProxy()
private val systemUiCarrierConfig =
@@ -113,11 +116,16 @@
MockitoAnnotations.initMocks(this)
whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
- connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogger)
+ connectionsRepo =
+ FakeMobileConnectionsRepository(
+ mobileMappings,
+ tableLogger,
+ )
underTest =
MobileConnectionRepositoryImpl(
SUB_1_ID,
+ subscriptionModel,
DEFAULT_NAME,
SEP,
telephonyManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index c8b6f13d..fd05cc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -1190,30 +1190,36 @@
companion object {
// Subscription 1
private const val SUB_1_ID = 1
+ private const val SUB_1_NAME = "Carrier $SUB_1_ID"
private val GROUP_1 = ParcelUuid(UUID.randomUUID())
private val SUB_1 =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_1_ID)
whenever(it.groupUuid).thenReturn(GROUP_1)
+ whenever(it.carrierName).thenReturn(SUB_1_NAME)
}
private val MODEL_1 =
SubscriptionModel(
subscriptionId = SUB_1_ID,
groupUuid = GROUP_1,
+ carrierName = SUB_1_NAME,
)
// Subscription 2
private const val SUB_2_ID = 2
+ private const val SUB_2_NAME = "Carrier $SUB_2_ID"
private val GROUP_2 = ParcelUuid(UUID.randomUUID())
private val SUB_2 =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_2_ID)
whenever(it.groupUuid).thenReturn(GROUP_2)
+ whenever(it.carrierName).thenReturn(SUB_2_NAME)
}
private val MODEL_2 =
SubscriptionModel(
subscriptionId = SUB_2_ID,
groupUuid = GROUP_2,
+ carrierName = SUB_2_NAME,
)
// Subs 3 and 4 are considered to be in the same group ------------------------------------
@@ -1242,9 +1248,14 @@
// Carrier merged subscription
private const val SUB_CM_ID = 5
+ private const val SUB_CM_NAME = "Carrier $SUB_CM_ID"
private val SUB_CM =
- mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_CM_ID) }
- private val MODEL_CM = SubscriptionModel(subscriptionId = SUB_CM_ID)
+ mock<SubscriptionInfo>().also {
+ whenever(it.subscriptionId).thenReturn(SUB_CM_ID)
+ whenever(it.carrierName).thenReturn(SUB_CM_NAME)
+ }
+ private val MODEL_CM =
+ SubscriptionModel(subscriptionId = SUB_CM_ID, carrierName = SUB_CM_NAME)
private val WIFI_INFO_CM =
mock<WifiInfo>().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index 8d1da69..a3df785 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -44,6 +44,8 @@
override val mobileIsDefault = MutableStateFlow(true)
+ override val isSingleCarrier = MutableStateFlow(true)
+
override val networkTypeIconGroup =
MutableStateFlow<NetworkTypeIconModel>(
NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
@@ -51,6 +53,8 @@
override val networkName = MutableStateFlow(NetworkNameModel.IntentDerived("demo mode"))
+ override val carrierName = MutableStateFlow("demo mode")
+
private val _isEmergencyOnly = MutableStateFlow(false)
override val isEmergencyOnly = _isEmergencyOnly
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index b2bbcfd..82b7ec4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -64,6 +64,8 @@
override val mobileIsDefault = MutableStateFlow(false)
+ override val isSingleCarrier = MutableStateFlow(true)
+
private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
override val defaultMobileIconMapping = _defaultMobileIconMapping
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 58d3804..e3c59ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.CarrierMergedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FIVE_G_OVERRIDE
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FOUR_G
@@ -40,6 +41,7 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -56,6 +58,15 @@
private lateinit var underTest: MobileIconInteractor
private val mobileMappingsProxy = FakeMobileMappingsProxy()
private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock())
+
+ private val subscriptionModel =
+ MutableStateFlow(
+ SubscriptionModel(
+ subscriptionId = SUB_1_ID,
+ carrierName = DEFAULT_NAME,
+ )
+ )
+
private val connectionRepository = FakeMobileConnectionRepository(SUB_1_ID, mock())
private val testDispatcher = UnconfinedTestDispatcher()
@@ -432,7 +443,7 @@
}
@Test
- fun networkName_usesOperatorAlphaShotWhenNonNullAndRepoIsDefault() =
+ fun networkName_usesOperatorAlphaShortWhenNonNullAndRepoIsDefault() =
testScope.runTest {
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
@@ -440,7 +451,7 @@
val testOperatorName = "operatorAlphaShort"
// Default network name, operator name is non-null, uses the operator name
- connectionRepository.networkName.value = DEFAULT_NAME
+ connectionRepository.networkName.value = DEFAULT_NAME_MODEL
connectionRepository.operatorAlphaShort.value = testOperatorName
assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived(testOperatorName))
@@ -448,10 +459,39 @@
// Default network name, operator name is null, uses the default
connectionRepository.operatorAlphaShort.value = null
+ assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
+
+ // Derived network name, operator name non-null, uses the derived name
+ connectionRepository.networkName.value = DERIVED_NAME_MODEL
+ connectionRepository.operatorAlphaShort.value = testOperatorName
+
+ assertThat(latest).isEqualTo(DERIVED_NAME_MODEL)
+
+ job.cancel()
+ }
+
+ @Test
+ fun networkNameForSubId_usesOperatorAlphaShortWhenNonNullAndRepoIsDefault() =
+ testScope.runTest {
+ var latest: String? = null
+ val job = underTest.carrierName.onEach { latest = it }.launchIn(this)
+
+ val testOperatorName = "operatorAlphaShort"
+
+ // Default network name, operator name is non-null, uses the operator name
+ connectionRepository.carrierName.value = DEFAULT_NAME_MODEL
+ connectionRepository.operatorAlphaShort.value = testOperatorName
+
+ assertThat(latest).isEqualTo(testOperatorName)
+
+ // Default network name, operator name is null, uses the default
+ connectionRepository.operatorAlphaShort.value = null
+
assertThat(latest).isEqualTo(DEFAULT_NAME)
// Derived network name, operator name non-null, uses the derived name
- connectionRepository.networkName.value = DERIVED_NAME
+ connectionRepository.carrierName.value =
+ NetworkNameModel.SubscriptionDerived(DERIVED_NAME)
connectionRepository.operatorAlphaShort.value = testOperatorName
assertThat(latest).isEqualTo(DERIVED_NAME)
@@ -460,6 +500,21 @@
}
@Test
+ fun isSingleCarrier_matchesParent() =
+ testScope.runTest {
+ var latest: Boolean? = null
+ val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+
+ mobileIconsInteractor.isSingleCarrier.value = true
+ assertThat(latest).isTrue()
+
+ mobileIconsInteractor.isSingleCarrier.value = false
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
fun isForceHidden_matchesParent() =
testScope.runTest {
var latest: Boolean? = null
@@ -494,6 +549,7 @@
mobileIconsInteractor.activeDataConnectionHasDataEnabled,
mobileIconsInteractor.alwaysShowDataRatIcon,
mobileIconsInteractor.alwaysUseCdmaLevel,
+ mobileIconsInteractor.isSingleCarrier,
mobileIconsInteractor.mobileIsDefault,
mobileIconsInteractor.defaultMobileIconMapping,
mobileIconsInteractor.defaultMobileIconGroup,
@@ -510,7 +566,9 @@
private const val SUB_1_ID = 1
- private val DEFAULT_NAME = NetworkNameModel.Default("test default name")
- private val DERIVED_NAME = NetworkNameModel.IntentDerived("test derived name")
+ private val DEFAULT_NAME = "test default name"
+ private val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME)
+ private val DERIVED_NAME = "test derived name"
+ private val DERIVED_NAME_MODEL = NetworkNameModel.IntentDerived(DERIVED_NAME)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 1fb76b0..3e6f909 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -527,6 +527,57 @@
}
@Test
+ fun isSingleCarrier_zeroSubscriptions_false() =
+ testScope.runTest {
+ var latest: Boolean? = true
+ val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+
+ connectionsRepository.setSubscriptions(emptyList())
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun isSingleCarrier_oneSubscription_true() =
+ testScope.runTest {
+ var latest: Boolean? = false
+ val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1))
+ assertThat(latest).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun isSingleCarrier_twoSubscriptions_false() =
+ testScope.runTest {
+ var latest: Boolean? = true
+ val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun isSingleCarrier_updates() =
+ testScope.runTest {
+ var latest: Boolean? = false
+ val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1))
+ assertThat(latest).isTrue()
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
fun mobileIsDefault_mobileFalseAndCarrierMergedFalse_false() =
testScope.runTest {
var latest: Boolean? = null
@@ -745,6 +796,7 @@
subscriptionId = subscriptionIds.first,
isOpportunistic = opportunistic.first,
groupUuid = groupUuid,
+ carrierName = "Carrier ${subscriptionIds.first}"
)
val sub2 =
@@ -752,6 +804,7 @@
subscriptionId = subscriptionIds.second,
isOpportunistic = opportunistic.second,
groupUuid = groupUuid,
+ carrierName = "Carrier ${opportunistic.second}"
)
return Pair(sub1, sub2)
@@ -760,11 +813,13 @@
companion object {
private const val SUB_1_ID = 1
- private val SUB_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
+ private val SUB_1 =
+ SubscriptionModel(subscriptionId = SUB_1_ID, carrierName = "Carrier $SUB_1_ID")
private val CONNECTION_1 = FakeMobileConnectionRepository(SUB_1_ID, mock())
private const val SUB_2_ID = 2
- private val SUB_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
+ private val SUB_2 =
+ SubscriptionModel(subscriptionId = SUB_2_ID, carrierName = "Carrier $SUB_2_ID")
private val CONNECTION_2 = FakeMobileConnectionRepository(SUB_2_ID, mock())
private const val SUB_3_ID = 3
@@ -773,6 +828,7 @@
subscriptionId = SUB_3_ID,
isOpportunistic = true,
groupUuid = ParcelUuid(UUID.randomUUID()),
+ carrierName = "Carrier $SUB_3_ID"
)
private val CONNECTION_3 = FakeMobileConnectionRepository(SUB_3_ID, mock())
@@ -782,6 +838,7 @@
subscriptionId = SUB_4_ID,
isOpportunistic = true,
groupUuid = ParcelUuid(UUID.randomUUID()),
+ carrierName = "Carrier $SUB_4_ID"
)
private val CONNECTION_4 = FakeMobileConnectionRepository(SUB_4_ID, mock())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index f0458fa..065dfba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -92,15 +92,31 @@
interactor.filteredSubscriptions.value =
listOf(
- SubscriptionModel(subscriptionId = 1, isOpportunistic = false),
+ SubscriptionModel(
+ subscriptionId = 1,
+ isOpportunistic = false,
+ carrierName = "Carrier 1",
+ ),
)
assertThat(latest).isEqualTo(listOf(1))
interactor.filteredSubscriptions.value =
listOf(
- SubscriptionModel(subscriptionId = 2, isOpportunistic = false),
- SubscriptionModel(subscriptionId = 5, isOpportunistic = true),
- SubscriptionModel(subscriptionId = 7, isOpportunistic = true),
+ SubscriptionModel(
+ subscriptionId = 2,
+ isOpportunistic = false,
+ carrierName = "Carrier 2",
+ ),
+ SubscriptionModel(
+ subscriptionId = 5,
+ isOpportunistic = true,
+ carrierName = "Carrier 5",
+ ),
+ SubscriptionModel(
+ subscriptionId = 7,
+ isOpportunistic = true,
+ carrierName = "Carrier 7",
+ ),
)
assertThat(latest).isEqualTo(listOf(2, 5, 7))
@@ -138,6 +154,33 @@
}
@Test
+ fun caching_mobileIconInteractorIsReusedForSameSubId() =
+ testScope.runTest {
+ val interactor1 = underTest.mobileIconInteractorForSub(1)
+ val interactor2 = underTest.mobileIconInteractorForSub(1)
+
+ assertThat(interactor1).isSameInstanceAs(interactor2)
+ }
+
+ @Test
+ fun caching_invalidInteractorssAreRemovedFromCacheWhenSubDisappears() =
+ testScope.runTest {
+ // Retrieve interactors to trigger caching
+ val interactor1 = underTest.mobileIconInteractorForSub(1)
+ val interactor2 = underTest.mobileIconInteractorForSub(2)
+
+ // Both impls are cached
+ assertThat(underTest.mobileIconInteractorSubIdCache)
+ .containsExactly(1, interactor1, 2, interactor2)
+
+ // SUB_1 is removed from the list...
+ interactor.filteredSubscriptions.value = listOf(SUB_2)
+
+ // ... and dropped from the cache
+ assertThat(underTest.mobileIconInteractorSubIdCache).containsExactly(2, interactor2)
+ }
+
+ @Test
fun firstMobileSubShowingNetworkTypeIcon_noSubs_false() =
testScope.runTest {
var latest: Boolean? = null
@@ -308,8 +351,23 @@
}
companion object {
- private val SUB_1 = SubscriptionModel(subscriptionId = 1, isOpportunistic = false)
- private val SUB_2 = SubscriptionModel(subscriptionId = 2, isOpportunistic = false)
- private val SUB_3 = SubscriptionModel(subscriptionId = 3, isOpportunistic = false)
+ private val SUB_1 =
+ SubscriptionModel(
+ subscriptionId = 1,
+ isOpportunistic = false,
+ carrierName = "Carrier 1",
+ )
+ private val SUB_2 =
+ SubscriptionModel(
+ subscriptionId = 2,
+ isOpportunistic = false,
+ carrierName = "Carrier 2",
+ )
+ private val SUB_3 =
+ SubscriptionModel(
+ subscriptionId = 3,
+ isOpportunistic = false,
+ carrierName = "Carrier 3",
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
index b698e70..b78e839 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -105,6 +106,7 @@
systemClock,
broadcastDispatcher,
shadeExpansionStateManager,
+ mock(),
testableHandler,
view
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 5f0011b..c12df98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -32,7 +32,6 @@
import static org.junit.Assume.assumeNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -500,123 +499,45 @@
@Test
public void ifPortraitHalfOpen_drawVerticallyTop() {
- DevicePostureController devicePostureController = mock(DevicePostureController.class);
- when(devicePostureController.getDevicePosture())
- .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
-
- VolumeDialogImpl dialog = new VolumeDialogImpl(
- getContext(),
- mVolumeDialogController,
- mAccessibilityMgr,
- mDeviceProvisionedController,
- mConfigurationController,
- mMediaOutputDialogFactory,
- mVolumePanelFactory,
- mActivityStarter,
- mInteractionJankMonitor,
- false,
- mCsdWarningDialogFactory,
- devicePostureController,
- mTestableLooper.getLooper(),
- mDumpManager,
- mFeatureFlags
- );
- dialog.init(0 , null);
-
- verify(devicePostureController).addCallback(any());
- dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
+ mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
mTestableLooper.processAllMessages(); // let dismiss() finish
setOrientation(Configuration.ORIENTATION_PORTRAIT);
// Call show() to trigger layout updates before verifying position
- dialog.show(SHOW_REASON_UNKNOWN);
+ mDialog.show(SHOW_REASON_UNKNOWN);
mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect
- int gravity = dialog.getWindowGravity();
+ int gravity = mDialog.getWindowGravity();
assertEquals(Gravity.TOP, gravity & Gravity.VERTICAL_GRAVITY_MASK);
-
- cleanUp(dialog);
}
@Test
public void ifPortraitAndOpen_drawCenterVertically() {
- DevicePostureController devicePostureController = mock(DevicePostureController.class);
- when(devicePostureController.getDevicePosture())
- .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
-
- VolumeDialogImpl dialog = new VolumeDialogImpl(
- getContext(),
- mVolumeDialogController,
- mAccessibilityMgr,
- mDeviceProvisionedController,
- mConfigurationController,
- mMediaOutputDialogFactory,
- mVolumePanelFactory,
- mActivityStarter,
- mInteractionJankMonitor,
- false,
- mCsdWarningDialogFactory,
- devicePostureController,
- mTestableLooper.getLooper(),
- mDumpManager,
- mFeatureFlags
- );
- dialog.init(0, null);
-
- verify(devicePostureController).addCallback(any());
- dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED);
+ mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED);
mTestableLooper.processAllMessages(); // let dismiss() finish
setOrientation(Configuration.ORIENTATION_PORTRAIT);
- dialog.show(SHOW_REASON_UNKNOWN);
+ mDialog.show(SHOW_REASON_UNKNOWN);
mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect
- int gravity = dialog.getWindowGravity();
+ int gravity = mDialog.getWindowGravity();
assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK);
-
- cleanUp(dialog);
}
@Test
public void ifLandscapeAndHalfOpen_drawCenterVertically() {
- DevicePostureController devicePostureController = mock(DevicePostureController.class);
- when(devicePostureController.getDevicePosture())
- .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
-
- VolumeDialogImpl dialog = new VolumeDialogImpl(
- getContext(),
- mVolumeDialogController,
- mAccessibilityMgr,
- mDeviceProvisionedController,
- mConfigurationController,
- mMediaOutputDialogFactory,
- mVolumePanelFactory,
- mActivityStarter,
- mInteractionJankMonitor,
- false,
- mCsdWarningDialogFactory,
- devicePostureController,
- mTestableLooper.getLooper(),
- mDumpManager,
- mFeatureFlags
- );
- dialog.init(0, null);
-
- verify(devicePostureController).addCallback(any());
- dialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
+ mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
mTestableLooper.processAllMessages(); // let dismiss() finish
setOrientation(Configuration.ORIENTATION_LANDSCAPE);
- dialog.show(SHOW_REASON_UNKNOWN);
+ mDialog.show(SHOW_REASON_UNKNOWN);
mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect
- int gravity = dialog.getWindowGravity();
+ int gravity = mDialog.getWindowGravity();
assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK);
-
- cleanUp(dialog);
}
@Test
@@ -627,31 +548,9 @@
@Test
public void dialogDestroy_removesPostureControllerCallback() {
- VolumeDialogImpl dialog = new VolumeDialogImpl(
- getContext(),
- mVolumeDialogController,
- mAccessibilityMgr,
- mDeviceProvisionedController,
- mConfigurationController,
- mMediaOutputDialogFactory,
- mVolumePanelFactory,
- mActivityStarter,
- mInteractionJankMonitor,
- false,
- mCsdWarningDialogFactory,
- mPostureController,
- mTestableLooper.getLooper(),
- mDumpManager,
- mFeatureFlags
- );
- dialog.init(0, null);
-
verify(mPostureController, never()).removeCallback(any());
- dialog.destroy();
-
+ mDialog.destroy();
verify(mPostureController).removeCallback(any());
-
- cleanUp(dialog);
}
private void setOrientation(int orientation) {
@@ -736,6 +635,8 @@
*/
private void assertRingerContainerDescribesItsState(int ringerMode,
RingerDrawerState drawerState) {
+ assumeHasDrawer();
+
State state = createShellState();
state.ringerModeInternal = ringerMode;
mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
new file mode 100644
index 0000000..94ed608
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.wmshell
+
+import android.content.ContentResolver
+import android.content.Context
+import android.content.SharedPreferences
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.model.SysUiStateTest
+import com.android.wm.shell.bubbles.Bubble
+import com.android.wm.shell.bubbles.BubbleEducationController
+import com.android.wm.shell.bubbles.PREF_MANAGED_EDUCATION
+import com.android.wm.shell.bubbles.PREF_STACK_EDUCATION
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class BubbleEducationControllerTest : SysUiStateTest() {
+ private val sharedPrefsEditor = Mockito.mock(SharedPreferences.Editor::class.java)
+ private val sharedPrefs = Mockito.mock(SharedPreferences::class.java)
+ private val context = Mockito.mock(Context::class.java)
+ private lateinit var sut: BubbleEducationController
+
+ @Before
+ fun setUp() {
+ Mockito.`when`(context.packageName).thenReturn("packageName")
+ Mockito.`when`(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs)
+ Mockito.`when`(context.contentResolver)
+ .thenReturn(Mockito.mock(ContentResolver::class.java))
+ Mockito.`when`(sharedPrefs.edit()).thenReturn(sharedPrefsEditor)
+ sut = BubbleEducationController(context)
+ }
+
+ @Test
+ fun testSeenStackEducation_read() {
+ Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ assertEquals(sut.hasSeenStackEducation, true)
+ Mockito.verify(sharedPrefs).getBoolean(PREF_STACK_EDUCATION, false)
+ }
+
+ @Test
+ fun testSeenStackEducation_write() {
+ sut.hasSeenStackEducation = true
+ Mockito.verify(sharedPrefsEditor).putBoolean(PREF_STACK_EDUCATION, true)
+ }
+
+ @Test
+ fun testSeenManageEducation_read() {
+ Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ assertEquals(sut.hasSeenManageEducation, true)
+ Mockito.verify(sharedPrefs).getBoolean(PREF_MANAGED_EDUCATION, false)
+ }
+
+ @Test
+ fun testSeenManageEducation_write() {
+ sut.hasSeenManageEducation = true
+ Mockito.verify(sharedPrefsEditor).putBoolean(PREF_MANAGED_EDUCATION, true)
+ }
+
+ @Test
+ fun testShouldShowStackEducation() {
+ val bubble = Mockito.mock(Bubble::class.java)
+ // When bubble is null
+ assertEquals(sut.shouldShowStackEducation(null), false)
+ // When bubble is not conversation
+ Mockito.`when`(bubble.isConversation).thenReturn(false)
+ assertEquals(sut.shouldShowStackEducation(bubble), false)
+ // When bubble is conversation and has seen stack edu
+ Mockito.`when`(bubble.isConversation).thenReturn(true)
+ Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ assertEquals(sut.shouldShowStackEducation(bubble), false)
+ // When bubble is conversation and has not seen stack edu
+ Mockito.`when`(bubble.isConversation).thenReturn(true)
+ Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(false)
+ assertEquals(sut.shouldShowStackEducation(bubble), true)
+ }
+
+ @Test
+ fun testShouldShowManageEducation() {
+ val bubble = Mockito.mock(Bubble::class.java)
+ // When bubble is null
+ assertEquals(sut.shouldShowManageEducation(null), false)
+ // When bubble is not conversation
+ Mockito.`when`(bubble.isConversation).thenReturn(false)
+ assertEquals(sut.shouldShowManageEducation(bubble), false)
+ // When bubble is conversation and has seen stack edu
+ Mockito.`when`(bubble.isConversation).thenReturn(true)
+ Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ assertEquals(sut.shouldShowManageEducation(bubble), false)
+ // When bubble is conversation and has not seen stack edu
+ Mockito.`when`(bubble.isConversation).thenReturn(true)
+ Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(false)
+ assertEquals(sut.shouldShowManageEducation(bubble), true)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index 013dbb4..43c9c99 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -18,7 +18,17 @@
import java.io.PrintWriter
-class FakeFeatureFlags : FeatureFlags {
+class FakeFeatureFlagsClassic : FakeFeatureFlags()
+
+@Deprecated(
+ message = "Use FakeFeatureFlagsClassic instead.",
+ replaceWith =
+ ReplaceWith(
+ "FakeFeatureFlagsClassic",
+ "com.android.systemui.flags.FakeFeatureFlagsClassic",
+ ),
+)
+open class FakeFeatureFlags : FeatureFlagsClassic {
private val booleanFlags = mutableMapOf<String, Boolean>()
private val stringFlags = mutableMapOf<String, String>()
private val intFlags = mutableMapOf<String, Int>()
@@ -66,12 +76,11 @@
* Set the given flag's default value if no other value has been set.
*
* REMINDER: You should always test your code with your flag in both configurations, so
- * generally you should be setting a particular value. This method should be reserved for
- * situations where the flag needs to be read (e.g. in the class constructor), but its
- * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
- * this method than to hard-code `false` or `true` because then at least if you're wrong,
- * and the flag value *does* matter, you'll notice when the flag is flipped and tests
- * start failing.
+ * generally you should be setting a particular value. This method should be reserved for
+ * situations where the flag needs to be read (e.g. in the class constructor), but its value
+ * shouldn't affect the actual test cases. In those cases, it's mildly safer to use this method
+ * than to hard-code `false` or `true` because then at least if you're wrong, and the flag value
+ * *does* matter, you'll notice when the flag is flipped and tests start failing.
*/
fun setDefault(flag: BooleanFlag) = booleanFlags.putIfAbsent(flag.name, flag.default)
@@ -79,12 +88,11 @@
* Set the given flag's default value if no other value has been set.
*
* REMINDER: You should always test your code with your flag in both configurations, so
- * generally you should be setting a particular value. This method should be reserved for
- * situations where the flag needs to be read (e.g. in the class constructor), but its
- * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
- * this method than to hard-code `false` or `true` because then at least if you're wrong,
- * and the flag value *does* matter, you'll notice when the flag is flipped and tests
- * start failing.
+ * generally you should be setting a particular value. This method should be reserved for
+ * situations where the flag needs to be read (e.g. in the class constructor), but its value
+ * shouldn't affect the actual test cases. In those cases, it's mildly safer to use this method
+ * than to hard-code `false` or `true` because then at least if you're wrong, and the flag value
+ * *does* matter, you'll notice when the flag is flipped and tests start failing.
*/
fun setDefault(flag: SysPropBooleanFlag) = booleanFlags.putIfAbsent(flag.name, flag.default)
@@ -123,10 +131,8 @@
}
override fun removeListener(listener: FlagListenable.Listener) {
- listenerflagNames.remove(listener)?.let {
- flagNames -> flagNames.forEach {
- id -> flagListeners[id]?.remove(listener)
- }
+ listenerflagNames.remove(listener)?.let { flagNames ->
+ flagNames.forEach { id -> flagListeners[id]?.remove(listener) }
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 819f8a1..7e09b5e 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -3958,14 +3958,14 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_APPWIDGET, null);
} catch (SecurityException se) {
- if (!isCallerBindAppWidgetWhiteListedLocked(packageName)) {
+ if (!isCallerBindAppWidgetAllowListedLocked(packageName)) {
return false;
}
}
return true;
}
- private boolean isCallerBindAppWidgetWhiteListedLocked(String packageName) {
+ private boolean isCallerBindAppWidgetAllowListedLocked(String packageName) {
final int userId = UserHandle.getCallingUserId();
final int packageUid = getUidForPackage(packageName, userId);
if (packageUid < 0) {
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionRendorInfoCallbackOnResultListener.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionRendorInfoCallbackOnResultListener.java
new file mode 100644
index 0000000..7351ef5
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionRendorInfoCallbackOnResultListener.java
@@ -0,0 +1,65 @@
+/*
+ * 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.autofill;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import java.lang.ref.WeakReference;
+import java.util.function.Consumer;
+
+final class InlineSuggestionRendorInfoCallbackOnResultListener implements
+ RemoteCallback.OnResultListener{
+ private static final String TAG = "InlineSuggestionRendorInfoCallbackOnResultListener";
+
+ private final int mRequestIdCopy;
+ private final AutofillId mFocusedId;
+ private final WeakReference<Session> mSessionWeakReference;
+ private final Consumer<InlineSuggestionsRequest> mInlineSuggestionsRequestConsumer;
+
+ InlineSuggestionRendorInfoCallbackOnResultListener(WeakReference<Session> sessionWeakReference,
+ int requestIdCopy,
+ Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer,
+ AutofillId focusedId) {
+ this.mRequestIdCopy = requestIdCopy;
+ this.mInlineSuggestionsRequestConsumer = inlineSuggestionsRequestConsumer;
+ this.mSessionWeakReference = sessionWeakReference;
+ this.mFocusedId = focusedId;
+ }
+ public void onResult(@Nullable Bundle result) {
+ Session session = this.mSessionWeakReference.get();
+ if (session == null) {
+ Slog.wtf(TAG, "Session is null before trying to call onResult");
+ return;
+ }
+ synchronized (session.mLock) {
+ if (session.mDestroyed) {
+ Slog.wtf(TAG, "Session is destroyed before trying to call onResult");
+ return;
+ }
+ session.mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
+ this.mFocusedId,
+ session.inlineSuggestionsRequestCacheDecorator(
+ this.mInlineSuggestionsRequestConsumer, this.mRequestIdCopy),
+ result);
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionRequestConsumer.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionRequestConsumer.java
new file mode 100644
index 0000000..a3efb25
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionRequestConsumer.java
@@ -0,0 +1,55 @@
+/*
+ * 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.autofill;
+
+import android.util.Slog;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import java.lang.ref.WeakReference;
+import java.util.function.Consumer;
+
+class InlineSuggestionRequestConsumer implements Consumer<InlineSuggestionsRequest> {
+
+ static final String TAG = "InlineSuggestionRequestConsumer";
+
+ private final WeakReference<Session.AssistDataReceiverImpl> mAssistDataReceiverWeakReference;
+ private final WeakReference<ViewState> mViewStateWeakReference;
+
+ InlineSuggestionRequestConsumer(WeakReference<Session.AssistDataReceiverImpl>
+ assistDataReceiverWeakReference,
+ WeakReference<ViewState> viewStateWeakReference) {
+ mAssistDataReceiverWeakReference = assistDataReceiverWeakReference;
+ mViewStateWeakReference = viewStateWeakReference;
+ }
+
+ @Override
+ public void accept(InlineSuggestionsRequest inlineSuggestionsRequest) {
+ Session.AssistDataReceiverImpl assistDataReceiver = mAssistDataReceiverWeakReference.get();
+ ViewState viewState = mViewStateWeakReference.get();
+ if (assistDataReceiver == null) {
+ Slog.wtf(TAG, "assistDataReceiver is null when accepting new inline suggestion"
+ + "requests");
+ return;
+ }
+
+ if (viewState == null) {
+ Slog.wtf(TAG, "view state is null when accepting new inline suggestion requests");
+ return;
+ }
+ assistDataReceiver.handleInlineSuggestionRequest(inlineSuggestionsRequest, viewState);
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1827a5b..1ae9125 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -326,7 +326,7 @@
* Id of the View currently being displayed.
*/
@GuardedBy("mLock")
- @Nullable private AutofillId mCurrentViewId;
+ private @Nullable AutofillId mCurrentViewId;
@GuardedBy("mLock")
private IAutoFillManagerClient mClient;
@@ -374,7 +374,7 @@
private Bundle mClientState;
@GuardedBy("mLock")
- private boolean mDestroyed;
+ boolean mDestroyed;
/**
* Helper used to handle state of Save UI when it must be hiding to show a custom description
@@ -453,7 +453,7 @@
private ArrayList<AutofillId> mAugmentedAutofillableIds;
@NonNull
- private final AutofillInlineSessionController mInlineSessionController;
+ final AutofillInlineSessionController mInlineSessionController;
/**
* Receiver of assist data from the app's {@link Activity}.
@@ -623,7 +623,7 @@
* TODO(b/151867668): improve how asynchronous data dependencies are handled, without using
* CountDownLatch.
*/
- private final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub {
+ final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub {
@GuardedBy("mLock")
private boolean mWaitForInlineRequest;
@GuardedBy("mLock")
@@ -638,18 +638,28 @@
mPendingFillRequest = null;
mWaitForInlineRequest = isInlineRequest;
mPendingInlineSuggestionsRequest = null;
- return isInlineRequest ? (inlineSuggestionsRequest) -> {
- synchronized (mLock) {
- if (!mWaitForInlineRequest || mPendingInlineSuggestionsRequest != null) {
- return;
- }
- mWaitForInlineRequest = inlineSuggestionsRequest != null;
- mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
- mWaitForInlineRequest = inlineSuggestionsRequest != null;
- maybeRequestFillFromServiceLocked();
- viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
+ if (isInlineRequest) {
+ WeakReference<AssistDataReceiverImpl> assistDataReceiverWeakReference =
+ new WeakReference<AssistDataReceiverImpl>(this);
+ WeakReference<ViewState> viewStateWeakReference =
+ new WeakReference<ViewState>(viewState);
+ return new InlineSuggestionRequestConsumer(assistDataReceiverWeakReference,
+ viewStateWeakReference);
+ }
+ return null;
+ }
+
+ void handleInlineSuggestionRequest(InlineSuggestionsRequest inlineSuggestionsRequest,
+ ViewState viewState) {
+ synchronized (mLock) {
+ if (!mWaitForInlineRequest || mPendingInlineSuggestionsRequest != null) {
+ return;
}
- } : null;
+ mWaitForInlineRequest = inlineSuggestionsRequest != null;
+ mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
+ maybeRequestFillFromServiceLocked();
+ viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
+ }
}
void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) {
@@ -1276,18 +1286,22 @@
viewState, /* isInlineRequest= */ true);
}
if (inlineSuggestionsRequestConsumer != null) {
- final AutofillId focusedId = mCurrentViewId;
final int requestIdCopy = requestId;
+ final AutofillId focusedId = mCurrentViewId;
+
+ WeakReference sessionWeakReference = new WeakReference<Session>(this);
+ InlineSuggestionRendorInfoCallbackOnResultListener
+ inlineSuggestionRendorInfoCallbackOnResultListener =
+ new InlineSuggestionRendorInfoCallbackOnResultListener(
+ sessionWeakReference,
+ requestIdCopy,
+ inlineSuggestionsRequestConsumer,
+ focusedId);
+ RemoteCallback inlineSuggestionRendorInfoCallback = new RemoteCallback(
+ inlineSuggestionRendorInfoCallbackOnResultListener, mHandler);
+
remoteRenderService.getInlineSuggestionsRendererInfo(
- new RemoteCallback((extras) -> {
- synchronized (mLock) {
- mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
- focusedId, inlineSuggestionsRequestCacheDecorator(
- inlineSuggestionsRequestConsumer, requestIdCopy),
- extras);
- }
- }, mHandler)
- );
+ inlineSuggestionRendorInfoCallback);
viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
} else if (mSessionFlags.mClientSuggestionsEnabled) {
@@ -5554,7 +5568,7 @@
}
@NonNull
- private Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestCacheDecorator(
+ Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestCacheDecorator(
@NonNull Consumer<InlineSuggestionsRequest> consumer, int requestId) {
return inlineSuggestionsRequest -> {
consumer.accept(inlineSuggestionsRequest);
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index b6baf7e..8cb7dea 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -133,7 +133,7 @@
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.backup.utils.BackupEligibilityRules;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import com.android.server.backup.utils.BackupObserverUtils;
import com.android.server.backup.utils.SparseArrayUtils;
@@ -1834,12 +1834,14 @@
*/
public int requestBackup(String[] packages, IBackupObserver observer,
IBackupManagerMonitor monitor, int flags) {
+ BackupManagerMonitorEventSender mBackupManagerMonitorEventSender =
+ getBMMEventSender(monitor);
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
if (packages == null || packages.length < 1) {
Slog.e(TAG, addUserIdToLogMessage(mUserId, "No packages named for backup request"));
BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
- monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
throw new IllegalArgumentException("No packages are provided for backup");
@@ -1857,7 +1859,7 @@
final int logTag = mSetupComplete
? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
: BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
- monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
+ mBackupManagerMonitorEventSender.monitorEvent(logTag, null,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
}
@@ -1875,7 +1877,7 @@
} catch (TransportNotRegisteredException | TransportNotAvailableException
| RemoteException e) {
BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
- monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
return BackupManager.ERROR_TRANSPORT_ABORTED;
@@ -3070,7 +3072,9 @@
/* caller */ "BMS.reportDelayedRestoreResult");
IBackupManagerMonitor monitor = transportClient.getBackupManagerMonitor();
- BackupManagerMonitorUtils.sendAgentLoggingResults(monitor, packageInfo, results,
+ BackupManagerMonitorEventSender mBackupManagerMonitorEventSender =
+ getBMMEventSender(monitor);
+ mBackupManagerMonitorEventSender.sendAgentLoggingResults(packageInfo, results,
BackupAnnotations.OperationType.RESTORE);
} catch (NameNotFoundException | TransportNotAvailableException
| TransportNotRegisteredException | RemoteException e) {
@@ -3194,6 +3198,11 @@
}
}
+ @VisibleForTesting
+ BackupManagerMonitorEventSender getBMMEventSender(IBackupManagerMonitor monitor) {
+ return new BackupManagerMonitorEventSender(monitor);
+ }
+
/** User-configurable enabling/disabling of backups. */
public void setBackupEnabled(boolean enable) {
setBackupEnabled(enable, /* persistToDisk */ true);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index ad29422..1271206 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -23,13 +23,11 @@
import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
import android.app.backup.BackupTransport;
import android.app.backup.FullBackupDataOutput;
-import android.app.backup.IBackupManagerMonitor;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -44,7 +42,7 @@
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.utils.BackupEligibilityRules;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import com.android.server.backup.utils.FullBackupUtils;
import java.io.File;
@@ -69,7 +67,7 @@
private final int mTransportFlags;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final BackupEligibilityRules mBackupEligibilityRules;
- @Nullable private final IBackupManagerMonitor mMonitor;
+ private final BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
class FullBackupRunner implements Runnable {
private final @UserIdInt int mUserId;
@@ -198,7 +196,7 @@
int opToken,
int transportFlags,
BackupEligibilityRules backupEligibilityRules,
- IBackupManagerMonitor monitor) {
+ BackupManagerMonitorEventSender backupManagerMonitorEventSender) {
this.backupManagerService = backupManagerService;
mOutput = output;
mPreflightHook = preflightHook;
@@ -213,7 +211,7 @@
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
mBackupEligibilityRules = backupEligibilityRules;
- mMonitor = monitor;
+ mBackupManagerMonitorEventSender = backupManagerMonitorEventSender;
}
public int preflightCheck() throws RemoteException {
@@ -270,7 +268,7 @@
result = BackupTransport.TRANSPORT_OK;
}
- BackupManagerMonitorUtils.monitorAgentLoggingResults(mMonitor, mPkg, mAgent);
+ mBackupManagerMonitorEventSender.monitorAgentLoggingResults(mPkg, mAgent);
} catch (IOException e) {
Slog.e(TAG, "Error backing up " + mPkg.packageName + ": " + e.getMessage());
result = BackupTransport.AGENT_ERROR;
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index cba1e29..dc67091 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -40,6 +40,7 @@
import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.utils.BackupEligibilityRules;
+import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import com.android.server.backup.utils.PasswordUtils;
import java.io.ByteArrayOutputStream;
@@ -421,7 +422,7 @@
mCurrentOpToken,
/*transportFlags=*/ 0,
mBackupEligibilityRules,
- /* monitor= */ null);
+ new BackupManagerMonitorEventSender(null));
sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
// Don't need to check preflight result as there is no preflight hook.
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 162046a..6aed9aa 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -54,7 +54,7 @@
import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.utils.BackupEligibilityRules;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import com.android.server.backup.utils.BackupObserverUtils;
import com.google.android.collect.Sets;
@@ -153,7 +153,6 @@
CountDownLatch mLatch;
FullBackupJob mJob; // if a scheduled job needs to be finished afterwards
IBackupObserver mBackupObserver;
- @Nullable private IBackupManagerMonitor mMonitor;
boolean mUserInitiated;
SinglePackageBackupRunner mBackupRunner;
private final int mBackupRunnerOpToken;
@@ -167,6 +166,7 @@
private final int mCurrentOpToken;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final BackupEligibilityRules mBackupEligibilityRules;
+ private BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
public PerformFullTransportBackupTask(UserBackupManagerService backupManagerService,
OperationStorage operationStorage,
@@ -185,11 +185,12 @@
mJob = runningJob;
mPackages = new ArrayList<>(whichPackages.length);
mBackupObserver = backupObserver;
- mMonitor = monitor;
mListener = (listener != null) ? listener : OnTaskFinishedListener.NOP;
mUserInitiated = userInitiated;
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken();
+ mBackupManagerMonitorEventSender =
+ new BackupManagerMonitorEventSender(monitor);
mAgentTimeoutParameters = Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
@@ -218,7 +219,7 @@
if (MORE_DEBUG) {
Slog.d(TAG, "Ignoring ineligible package " + pkg);
}
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_INELIGIBLE,
mCurrentPackage,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -233,7 +234,7 @@
Slog.d(TAG, "Ignoring full-data backup of key/value participant "
+ pkg);
}
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT,
mCurrentPackage,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -248,7 +249,7 @@
if (MORE_DEBUG) {
Slog.d(TAG, "Ignoring stopped package " + pkg);
}
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_STOPPED,
mCurrentPackage,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -260,7 +261,7 @@
mPackages.add(info);
} catch (NameNotFoundException e) {
Slog.i(TAG, "Requested package " + pkg + " not found; ignoring");
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_FOUND,
mCurrentPackage,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -356,8 +357,8 @@
} else {
monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
}
- mMonitor = BackupManagerMonitorUtils
- .monitorEvent(mMonitor, monitoringEvent, null,
+ mBackupManagerMonitorEventSender
+ .monitorEvent(monitoringEvent, null,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
null);
mUpdateSchedule = false;
@@ -369,7 +370,7 @@
if (transport == null) {
Slog.w(TAG, "Transport not present; full data backup not performed");
backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT,
mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
null);
@@ -378,9 +379,10 @@
// In some cases there may not be a monitor passed in when creating this task. So, if we
// don't have one already we ask the transport for a monitor.
- if (mMonitor == null) {
+ if (mBackupManagerMonitorEventSender.getMonitor() == null) {
try {
- mMonitor = transport.getBackupManagerMonitor();
+ mBackupManagerMonitorEventSender
+ .setMonitor(transport.getBackupManagerMonitor());
} catch (RemoteException e) {
Slog.i(TAG, "Failed to retrieve monitor from transport");
}
@@ -457,11 +459,11 @@
+ packageName + ": " + preflightResult
+ ", not running backup.");
}
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT,
mCurrentPackage,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- BackupManagerMonitorUtils.putMonitoringExtra(null,
+ mBackupManagerMonitorEventSender.putMonitoringExtra(null,
BackupManagerMonitor.EXTRA_LOG_PREFLIGHT_ERROR,
preflightResult));
backupPackageStatus = (int) preflightResult;
@@ -492,7 +494,7 @@
if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
Slog.w(TAG, "Package hit quota limit in-flight " + packageName
+ ": " + totalRead + " of " + quota);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT,
mCurrentPackage,
BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
@@ -647,11 +649,11 @@
} catch (Exception e) {
backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
Slog.w(TAG, "Exception trying full transport backup", e);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP,
mCurrentPackage,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- BackupManagerMonitorUtils.putMonitoringExtra(null,
+ mBackupManagerMonitorEventSender.putMonitoringExtra(null,
BackupManagerMonitor.EXTRA_LOG_EXCEPTION_FULL_BACKUP,
Log.getStackTraceString(e)));
@@ -885,7 +887,7 @@
mCurrentOpToken,
mTransportFlags,
mBackupEligibilityRules,
- mMonitor);
+ mBackupManagerMonitorEventSender);
try {
try {
if (!mIsCancelled) {
@@ -967,7 +969,7 @@
Slog.w(TAG, "Full backup cancel of " + mTarget.packageName);
}
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL,
mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
mIsCancelled = true;
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
index 4632cb0..20c8cf6 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
@@ -32,7 +32,7 @@
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.remote.RemoteResult;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import com.android.server.backup.utils.BackupObserverUtils;
import java.io.File;
@@ -65,21 +65,21 @@
private final UserBackupManagerService mBackupManagerService;
private final IBackupObserver mObserver;
- @Nullable private IBackupManagerMonitor mMonitor;
+ private final BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
KeyValueBackupReporter(
UserBackupManagerService backupManagerService,
IBackupObserver observer,
- @Nullable IBackupManagerMonitor monitor) {
+ BackupManagerMonitorEventSender backupManagerMonitorEventSender) {
mBackupManagerService = backupManagerService;
mObserver = observer;
- mMonitor = monitor;
+ mBackupManagerMonitorEventSender = backupManagerMonitorEventSender;
}
/** Returns the monitor or {@code null} if we lost connection to it. */
@Nullable
IBackupManagerMonitor getMonitor() {
- return mMonitor;
+ return mBackupManagerMonitorEventSender.getMonitor();
}
IBackupObserver getObserver() {
@@ -208,13 +208,11 @@
void onAgentIllegalKey(PackageInfo packageInfo, String key) {
String packageName = packageInfo.packageName;
EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, "bad key");
- mMonitor =
- BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY,
packageInfo,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- BackupManagerMonitorUtils.putMonitoringExtra(
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
null, BackupManagerMonitor.EXTRA_LOG_ILLEGAL_KEY, key));
BackupObserverUtils.sendBackupOnPackageResult(
mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE);
@@ -254,13 +252,11 @@
if (MORE_DEBUG) {
Slog.i(TAG, "No backup data written, not calling transport");
}
- mMonitor =
- BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
- BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND,
- packageInfo,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND,
+ packageInfo,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ null);
}
void onPackageBackupComplete(String packageName, long size) {
@@ -291,8 +287,7 @@
void onPackageBackupNonIncrementalRequired(PackageInfo packageInfo) {
Slog.i(TAG, "Transport lost data, retrying package");
- BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED,
packageInfo,
BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
@@ -335,28 +330,24 @@
EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
// Time-out used to be implemented as cancel w/ cancelAll = false.
// TODO: Change monitoring event to reflect time-out as an event itself.
- mMonitor =
- BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
- BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL,
- packageInfo,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
- BackupManagerMonitorUtils.putMonitoringExtra(
- null, BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL, false));
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL,
+ packageInfo,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ null, BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL, false));
}
void onAgentCancelled(@Nullable PackageInfo packageInfo) {
String packageName = getPackageName(packageInfo);
Slog.i(TAG, "Cancel backing up " + packageName);
EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
- mMonitor =
- BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
- BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL,
- packageInfo,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
- BackupManagerMonitorUtils.putMonitoringExtra(
- null, BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL, true));
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL,
+ packageInfo,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ null, BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL, true));
}
void onAgentResultError(@Nullable PackageInfo packageInfo) {
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 41e8092..3a6e1ca 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -68,7 +68,7 @@
import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.utils.BackupEligibilityRules;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import libcore.io.IoUtils;
@@ -225,7 +225,8 @@
boolean nonIncremental,
BackupEligibilityRules backupEligibilityRules) {
KeyValueBackupReporter reporter =
- new KeyValueBackupReporter(backupManagerService, observer, monitor);
+ new KeyValueBackupReporter(backupManagerService, observer,
+ new BackupManagerMonitorEventSender(monitor));
KeyValueBackupTask task =
new KeyValueBackupTask(
backupManagerService,
@@ -698,8 +699,9 @@
try {
extractAgentData(mCurrentPackage);
- BackupManagerMonitorUtils.monitorAgentLoggingResults(
- mReporter.getMonitor(), mCurrentPackage, mAgent);
+ BackupManagerMonitorEventSender mBackupManagerMonitorEventSender =
+ new BackupManagerMonitorEventSender(mReporter.getMonitor());
+ mBackupManagerMonitorEventSender.monitorAgentLoggingResults(mCurrentPackage, mAgent);
int status = sendDataToTransport(mCurrentPackage);
cleanUpAgentForTransportStatus(status);
} catch (AgentException | TaskException e) {
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 8cbb5dc..e04bf11 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -16,6 +16,8 @@
package com.android.server.backup.restore;
+import static android.app.backup.BackupAnnotations.OperationType.RESTORE;
+
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
@@ -30,6 +32,7 @@
import android.annotation.Nullable;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
+import android.app.backup.BackupAnnotations;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupManagerMonitor;
@@ -70,7 +73,7 @@
import com.android.server.backup.transport.BackupTransportClient;
import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import libcore.io.IoUtils;
@@ -84,7 +87,6 @@
import java.util.Set;
public class PerformUnifiedRestoreTask implements BackupRestoreTask {
-
private UserBackupManagerService backupManagerService;
private final OperationStorage mOperationStorage;
private final int mUserId;
@@ -98,8 +100,7 @@
// Restore observer; may be null
private IRestoreObserver mObserver;
- // BackuoManagerMonitor; may be null
- private IBackupManagerMonitor mMonitor;
+ private BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
// Token identifying the dataset to the transport
private long mToken;
@@ -181,6 +182,8 @@
mUserId = 0;
mBackupEligibilityRules = null;
this.backupManagerService = backupManagerService;
+ mBackupManagerMonitorEventSender =
+ new BackupManagerMonitorEventSender(/*monitor*/null);
}
// This task can assume that the wakelock is properly held for it and doesn't have to worry
@@ -208,7 +211,8 @@
mTransportConnection = transportConnection;
mObserver = observer;
- mMonitor = monitor;
+ mBackupManagerMonitorEventSender =
+ new BackupManagerMonitorEventSender(monitor);
mToken = restoreSetToken;
mPmToken = pmToken;
mTargetPackage = targetPackage;
@@ -410,8 +414,8 @@
// If the requester of the restore has not passed in a monitor, we ask the transport
// for one.
- if (mMonitor == null) {
- mMonitor = transport.getBackupManagerMonitor();
+ if (mBackupManagerMonitorEventSender.getMonitor() == null) {
+ mBackupManagerMonitorEventSender.setMonitor(transport.getBackupManagerMonitor());
}
mStatus = transport.startRestore(mToken, packages);
@@ -425,10 +429,12 @@
RestoreDescription desc = transport.nextRestorePackage();
if (desc == null) {
Slog.e(TAG, "No restore metadata available; halting");
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE,
mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
mStatus = BackupTransport.TRANSPORT_ERROR;
executeNextState(UnifiedRestoreState.FINAL);
return;
@@ -437,10 +443,12 @@
desc.getPackageName())) {
Slog.e(TAG, "Required package metadata but got "
+ desc.getPackageName());
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_NO_PM_METADATA_RECEIVED,
mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
mStatus = BackupTransport.TRANSPORT_ERROR;
executeNextState(UnifiedRestoreState.FINAL);
return;
@@ -472,10 +480,12 @@
// the restore operation.
if (!mPmAgent.hasMetadata()) {
Slog.e(TAG, "PM agent has no metadata, so not restoring");
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA,
mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
PACKAGE_MANAGER_SENTINEL,
"Package manager restore metadata missing");
@@ -492,10 +502,12 @@
} catch (Exception e) {
// If we lost the transport at any time, halt
Slog.e(TAG, "Unable to contact transport for restore: " + e.getMessage());
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_LOST_TRANSPORT,
null,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ monitoringExtras);
mStatus = BackupTransport.TRANSPORT_ERROR;
backupManagerService.getBackupHandler().removeMessages(
MSG_BACKUP_RESTORE_STEP, this);
@@ -552,11 +564,12 @@
// Whoops, we thought we could restore this package but it
// turns out not to be present. Skip it.
Slog.e(TAG, "Package not present: " + pkgName);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_PRESENT,
mCurrentPackage,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- null);
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
"Package missing on device");
nextState = UnifiedRestoreState.RUNNING_QUEUE;
@@ -572,13 +585,15 @@
String message = "Source version " + metaInfo.versionCode
+ " > installed version " + mCurrentPackage.getLongVersionCode();
Slog.w(TAG, "Package " + pkgName + ": " + message);
- Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
+ Bundle monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(
+ null,
BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
metaInfo.versionCode);
- monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(
+ monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(
monitoringExtras,
BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY, false);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
mCurrentPackage,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -593,13 +608,15 @@
+ " > installed version " + mCurrentPackage.getLongVersionCode()
+ " but restoreAnyVersion");
}
- Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
+ Bundle monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(
+ null,
BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
metaInfo.versionCode);
- monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(
+ monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(
monitoringExtras,
BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY, true);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
mCurrentPackage,
BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -652,9 +669,10 @@
Slog.i(TAG, "Data exists for package " + packageName
+ " but app has no agent; skipping");
}
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_APP_HAS_NO_AGENT, mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
"Package has no agent");
executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
@@ -665,9 +683,11 @@
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage, pmi)) {
Slog.w(TAG, "Signature mismatch restoring " + packageName);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_SIGNATURE_MISMATCH, mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
"Signature mismatch");
executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
@@ -681,9 +701,11 @@
mBackupEligibilityRules.getBackupDestination());
if (mAgent == null) {
Slog.w(TAG, "Can't find backup agent for " + packageName);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_CANT_FIND_AGENT, mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
"Restore agent missing");
executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
@@ -941,8 +963,9 @@
EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
mCurrentPackage.packageName);
- mEngine = new FullRestoreEngine(backupManagerService, mOperationStorage, this, null,
- mMonitor, mCurrentPackage, false, mEphemeralOpToken, false,
+ mEngine = new FullRestoreEngine(backupManagerService, mOperationStorage,
+ this, null, mBackupManagerMonitorEventSender.getMonitor(),
+ mCurrentPackage, false, mEphemeralOpToken, false,
mBackupEligibilityRules);
mEngineThread = new FullRestoreEngineThread(mEngine, mEnginePipes[0]);
@@ -1095,10 +1118,11 @@
if (DEBUG) {
Slog.w(TAG, "Full-data restore target timed out; shutting down");
}
-
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_TIMEOUT,
- mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
+ mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+ monitoringExtras);
mEngineThread.handleTimeout();
IoUtils.closeQuietly(mEnginePipes[1]);
@@ -1322,7 +1346,7 @@
// Ask the agent for logs after doRestoreFinished() has completed executing to allow
// it to finalize its logs.
- BackupManagerMonitorUtils.monitorAgentLoggingResults(mMonitor, mCurrentPackage,
+ mBackupManagerMonitorEventSender.monitorAgentLoggingResults(mCurrentPackage,
mAgent);
// Just go back to running the restore queue
@@ -1358,9 +1382,10 @@
public void handleCancel(boolean cancelAll) {
mOperationStorage.removeOperation(mEphemeralOpToken);
Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT,
- mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
+ mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
mCurrentPackage.packageName, "restore timeout");
// Handle like an agent that threw on invocation: wipe it and go on to the next
@@ -1433,4 +1458,10 @@
}
}
}
+
+ private Bundle addRestoreOperationTypeToEvent (@Nullable Bundle extra) {
+ return mBackupManagerMonitorEventSender.putMonitoringExtra(
+ extra,
+ BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE, RESTORE);
+ }
}
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
similarity index 83%
rename from services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java
rename to services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
index 439b836..f928680 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
@@ -25,7 +25,6 @@
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.IBackupAgent;
import android.app.backup.BackupAnnotations.OperationType;
@@ -44,9 +43,9 @@
import java.util.concurrent.TimeoutException;
/**
- * Utility methods to communicate with BackupManagerMonitor.
+ * Utility methods to log BackupManagerMonitor events.
*/
-public class BackupManagerMonitorUtils {
+public class BackupManagerMonitorEventSender {
/**
* Timeout for how long we wait before we give up on getting logs from a {@link IBackupAgent}.
* We expect this to be very fast since the agent immediately returns whatever logs have been
@@ -54,27 +53,36 @@
* for non-essential logs.
*/
private static final int AGENT_LOGGER_RESULTS_TIMEOUT_MILLIS = 500;
+ @Nullable private IBackupManagerMonitor mMonitor;
+
+ public BackupManagerMonitorEventSender(@Nullable IBackupManagerMonitor monitor) {
+ mMonitor = monitor;
+ }
+
+ public void setMonitor(IBackupManagerMonitor monitor) {
+ mMonitor = monitor;
+ }
+
+ public IBackupManagerMonitor getMonitor() {
+ return mMonitor;
+ }
/**
* Notifies monitor about the event.
*
* Calls {@link IBackupManagerMonitor#onEvent(Bundle)} with a bundle representing current event.
*
- * @param monitor - implementation of {@link IBackupManagerMonitor} to notify.
* @param id - event id.
* @param pkg - package event is related to.
* @param category - event category.
* @param extras - additional event data.
- * @return <code>monitor</code> if call succeeded and <code>null</code> otherwise.
*/
- @Nullable
- public static IBackupManagerMonitor monitorEvent(
- @Nullable IBackupManagerMonitor monitor,
+ public void monitorEvent(
int id,
PackageInfo pkg,
int category,
Bundle extras) {
- if (monitor != null) {
+ if (mMonitor != null) {
try {
Bundle bundle = new Bundle();
bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, id);
@@ -90,15 +98,14 @@
if (extras != null) {
bundle.putAll(extras);
}
- monitor.onEvent(bundle);
- return monitor;
+ mMonitor.onEvent(bundle);
} catch (RemoteException e) {
+ mMonitor = null;
if (DEBUG) {
Slog.w(TAG, "backup manager monitor went away");
}
}
}
- return null;
}
/**
@@ -108,17 +115,12 @@
* <p>Note that this method does two separate binder calls (one to the agent and one to the
* monitor).
*
- * @param monitor - implementation of {@link IBackupManagerMonitor} to notify.
* @param pkg - package the {@code agent} belongs to.
* @param agent - the {@link IBackupAgent} to retrieve logs from.
- * @return {@code null} if the monitor is null. {@code monitor} if we fail to retrieve the logs
- * from the {@code agent}. Otherwise, the result of {@link
- * #monitorEvent(IBackupManagerMonitor, int, PackageInfo, int, Bundle)}.
*/
- public static IBackupManagerMonitor monitorAgentLoggingResults(
- @Nullable IBackupManagerMonitor monitor, PackageInfo pkg, IBackupAgent agent) {
- if (monitor == null) {
- return null;
+ public void monitorAgentLoggingResults(PackageInfo pkg, IBackupAgent agent) {
+ if (mMonitor == null) {
+ return;
}
try {
@@ -127,7 +129,7 @@
AndroidFuture<Integer> operationTypeFuture = new AndroidFuture<>();
agent.getLoggerResults(resultsFuture);
agent.getOperationType(operationTypeFuture);
- return sendAgentLoggingResults(monitor, pkg,
+ sendAgentLoggingResults(pkg,
resultsFuture.get(AGENT_LOGGER_RESULTS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS),
operationTypeFuture.get(AGENT_LOGGER_RESULTS_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS));
@@ -136,18 +138,15 @@
} catch (Exception e) {
Slog.w(TAG, "Failed to retrieve logging results from agent", e);
}
- return monitor;
}
- public static IBackupManagerMonitor sendAgentLoggingResults(
- @NonNull IBackupManagerMonitor monitor, PackageInfo pkg, List<DataTypeResult> results,
+ public void sendAgentLoggingResults(PackageInfo pkg, List<DataTypeResult> results,
@OperationType int operationType) {
Bundle loggerResultsBundle = new Bundle();
loggerResultsBundle.putParcelableList(
EXTRA_LOG_AGENT_LOGGING_RESULTS, results);
loggerResultsBundle.putInt(EXTRA_LOG_OPERATION_TYPE, operationType);
- return monitorEvent(
- monitor,
+ monitorEvent(
LOG_EVENT_ID_AGENT_LOGGING_RESULTS,
pkg,
LOG_EVENT_CATEGORY_AGENT,
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 71ca8ca..78a9952 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -85,7 +85,8 @@
private final InputStream mInputStream;
private final BytesReadListener mBytesReadListener;
- private IBackupManagerMonitor mMonitor;
+
+ private BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
// Widget blob to be restored out-of-band.
private byte[] mWidgetData = null;
@@ -94,7 +95,7 @@
IBackupManagerMonitor monitor) {
mInputStream = inputStream;
mBytesReadListener = bytesReadListener;
- mMonitor = monitor;
+ mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(monitor);
}
/**
@@ -323,24 +324,22 @@
return sigs;
} else {
Slog.i(TAG, "Missing signature on backed-up package " + info.packageName);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
LOG_EVENT_ID_MISSING_SIGNATURE,
null,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- BackupManagerMonitorUtils.putMonitoringExtra(null,
+ mBackupManagerMonitorEventSender.putMonitoringExtra(null,
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName));
}
} else {
Slog.i(TAG, "Expected package " + info.packageName
+ " but restore manifest claims " + manifestPackage);
- Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
- EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
- monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(
+ Bundle monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(
+ null, EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
+ monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(
monitoringExtras,
EXTRA_LOG_MANIFEST_PACKAGE_NAME, manifestPackage);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE,
null,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -349,12 +348,11 @@
} else {
Slog.i(TAG, "Unknown restore manifest version " + version
+ " for package " + info.packageName);
- Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
- EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
- monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras,
- EXTRA_LOG_EVENT_PACKAGE_VERSION, version);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ Bundle monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(
+ null, EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
+ monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(
+ monitoringExtras, EXTRA_LOG_EVENT_PACKAGE_VERSION, version);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION,
null,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -363,12 +361,12 @@
}
} catch (NumberFormatException e) {
Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST,
null,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- BackupManagerMonitorUtils.putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME,
+ mBackupManagerMonitorEventSender.putMonitoringExtra(null,
+ EXTRA_LOG_EVENT_PACKAGE_NAME,
info.packageName));
} catch (IllegalArgumentException e) {
Slog.w(TAG, e.getMessage());
@@ -436,8 +434,7 @@
if ((pkgInfo.applicationInfo.flags
& ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) {
Slog.i(TAG, "Package has restoreAnyVersion; taking data");
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
LOG_EVENT_ID_RESTORE_ANY_VERSION,
pkgInfo,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -446,8 +443,7 @@
} else if (pkgInfo.getLongVersionCode() >= info.version) {
Slog.i(TAG, "Sig + version match; taking data");
policy = RestorePolicy.ACCEPT;
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
LOG_EVENT_ID_VERSIONS_MATCH,
pkgInfo,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -466,12 +462,11 @@
} else {
Slog.i(TAG, "Data requires newer version "
+ info.version + "; ignoring");
- mMonitor = BackupManagerMonitorUtils
- .monitorEvent(mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER,
pkgInfo,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- BackupManagerMonitorUtils
+ mBackupManagerMonitorEventSender
.putMonitoringExtra(
null,
EXTRA_LOG_OLD_VERSION,
@@ -484,8 +479,7 @@
Slog.w(TAG, "Restore manifest signatures do not match "
+ "installed application for "
+ info.packageName);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH,
pkgInfo,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -494,8 +488,7 @@
} else {
Slog.w(TAG, "Package " + info.packageName
+ " is system level with no agent");
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
LOG_EVENT_ID_SYSTEM_APP_NO_AGENT,
pkgInfo,
LOG_EVENT_CATEGORY_AGENT,
@@ -506,8 +499,7 @@
Slog.i(TAG,
"Restore manifest from " + info.packageName + " but allowBackup=false");
}
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE,
pkgInfo,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -526,14 +518,13 @@
} else {
policy = RestorePolicy.IGNORE;
}
- Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(
+ Bundle monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(
null,
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
- monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(
+ monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(
monitoringExtras,
EXTRA_LOG_POLICY_ALLOW_APKS, allowApks);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
LOG_EVENT_ID_APK_NOT_INSTALLED,
null,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -543,12 +534,11 @@
if (policy == RestorePolicy.ACCEPT_IF_APK && !info.hasApk) {
Slog.i(TAG, "Cannot restore package " + info.packageName
+ " without the matching .apk");
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK,
null,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- BackupManagerMonitorUtils.putMonitoringExtra(null,
+ mBackupManagerMonitorEventSender.putMonitoringExtra(null,
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName));
}
@@ -632,12 +622,11 @@
"Metadata mismatch: package " + info.packageName + " but widget data for "
+ pkg);
- Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
+ Bundle monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(null,
EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
- monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras,
- BackupManagerMonitor.EXTRA_LOG_WIDGET_PACKAGE_NAME, pkg);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(
+ monitoringExtras, BackupManagerMonitor.EXTRA_LOG_WIDGET_PACKAGE_NAME, pkg);
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH,
null,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -646,13 +635,12 @@
} else {
Slog.w(TAG, "Unsupported metadata version " + version);
- Bundle monitoringExtras = BackupManagerMonitorUtils
+ Bundle monitoringExtras = mBackupManagerMonitorEventSender
.putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME,
info.packageName);
- monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(monitoringExtras,
+ monitoringExtras = mBackupManagerMonitorEventSender.putMonitoringExtra(monitoringExtras,
EXTRA_LOG_EVENT_PACKAGE_VERSION, version);
- mMonitor = BackupManagerMonitorUtils.monitorEvent(
- mMonitor,
+ mBackupManagerMonitorEventSender.monitorEvent(
BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION,
null,
LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -810,7 +798,7 @@
}
public IBackupManagerMonitor getMonitor() {
- return mMonitor;
+ return mBackupManagerMonitorEventSender.getMonitor();
}
public byte[] getWidgetData() {
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index fd45d24..6964763 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -313,14 +313,14 @@
public void enableSystemDataSync(int associationId, int flags) {
AssociationInfo association = mAssociationStore.getAssociationById(associationId);
- AssociationInfo updated = AssociationInfo.builder(association)
+ AssociationInfo updated = (new AssociationInfo.Builder(association))
.setSystemDataSyncFlags(association.getSystemDataSyncFlags() | flags).build();
mAssociationStore.updateAssociation(updated);
}
public void disableSystemDataSync(int associationId, int flags) {
AssociationInfo association = mAssociationStore.getAssociationById(associationId);
- AssociationInfo updated = AssociationInfo.builder(association)
+ AssociationInfo updated = (new AssociationInfo.Builder(association))
.setSystemDataSyncFlags(association.getSystemDataSyncFlags() & (~flags)).build();
mAssociationStore.updateAssociation(updated);
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 996c68b..1ce7d96 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -891,7 +891,7 @@
}
// AssociationInfo class is immutable: create a new AssociationInfo object with updated
// timestamp.
- association = AssociationInfo.builder(association)
+ association = (new AssociationInfo.Builder(association))
.setLastTimeConnected(System.currentTimeMillis())
.build();
mAssociationStore.updateAssociation(association);
@@ -945,7 +945,7 @@
// AssociationInfo class is immutable: create a new AssociationInfo object with updated
// flag.
- association = AssociationInfo.builder(association)
+ association = (new AssociationInfo.Builder(association))
.setNotifyOnDeviceNearby(active)
.build();
// Do not need to call {@link BleCompanionDeviceScanner#restartScan()} since it will
@@ -1016,7 +1016,7 @@
@Override
public void setAssociationTag(int associationId, String tag) {
AssociationInfo association = getAssociationWithCallerChecks(associationId);
- association = AssociationInfo.builder(association).setTag(tag).build();
+ association = (new AssociationInfo.Builder(association)).setTag(tag).build();
mAssociationStore.updateAssociation(association);
}
@@ -1255,7 +1255,7 @@
*/
private void addToPendingRoleHolderRemoval(@NonNull AssociationInfo association) {
// First: set revoked flag.
- association = AssociationInfo.builder(association)
+ association = (new AssociationInfo.Builder(association))
.setRevoked(true)
.build();
diff --git a/services/companion/java/com/android/server/companion/virtual/Android.bp b/services/companion/java/com/android/server/companion/virtual/Android.bp
new file mode 100644
index 0000000..50a09b9
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/Android.bp
@@ -0,0 +1,12 @@
+java_aconfig_library {
+ name: "virtualdevice_flags_lib",
+ aconfig_declarations: "virtualdevice_flags",
+}
+
+aconfig_declarations {
+ name: "virtualdevice_flags",
+ package: "com.android.server.companion.virtual",
+ srcs: [
+ "flags.aconfig",
+ ],
+}
\ No newline at end of file
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index ee6c28e..d1be5a9 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -43,6 +43,7 @@
import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -125,6 +126,7 @@
private final VirtualDeviceManagerService mService;
private final PendingTrampolineCallback mPendingTrampolineCallback;
private final int mOwnerUid;
+ private final String mOwnerPackageName;
private int mDeviceId;
private @Nullable String mPersistentDeviceId;
// Thou shall not hold the mVirtualDeviceLock over the mInputController calls.
@@ -196,7 +198,7 @@
AssociationInfo associationInfo,
VirtualDeviceManagerService service,
IBinder token,
- int ownerUid,
+ AttributionSource attributionSource,
int deviceId,
CameraAccessController cameraAccessController,
PendingTrampolineCallback pendingTrampolineCallback,
@@ -209,7 +211,7 @@
associationInfo,
service,
token,
- ownerUid,
+ attributionSource,
deviceId,
/* inputController= */ null,
cameraAccessController,
@@ -227,7 +229,7 @@
AssociationInfo associationInfo,
VirtualDeviceManagerService service,
IBinder token,
- int ownerUid,
+ AttributionSource attributionSource,
int deviceId,
InputController inputController,
CameraAccessController cameraAccessController,
@@ -238,7 +240,8 @@
VirtualDeviceParams params,
DisplayManagerGlobal displayManager) {
super(PermissionEnforcer.fromContext(context));
- UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid);
+ mOwnerPackageName = attributionSource.getPackageName();
+ UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid());
mContext = context.createContextAsUser(ownerUserHandle, 0);
mAssociationInfo = associationInfo;
mPersistentDeviceId = PERSISTENT_ID_PREFIX_CDM_ASSOCIATION + associationInfo.getId();
@@ -247,7 +250,7 @@
mActivityListener = activityListener;
mSoundEffectListener = soundEffectListener;
mRunningAppsChangedCallback = runningAppsChangedCallback;
- mOwnerUid = ownerUid;
+ mOwnerUid = attributionSource.getUid();
mDeviceId = deviceId;
mAppToken = token;
mParams = params;
@@ -771,7 +774,9 @@
fout.println(" VirtualDevice: ");
fout.println(" mDeviceId: " + mDeviceId);
fout.println(" mAssociationId: " + mAssociationInfo.getId());
- fout.println(" mParams: " + mParams);
+ fout.println(" mOwnerPackageName: " + mOwnerPackageName);
+ fout.println(" mParams: ");
+ mParams.dump(fout, " ");
fout.println(" mVirtualDisplayIds: ");
synchronized (mVirtualDeviceLock) {
for (int i = 0; i < mVirtualDisplays.size(); i++) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 77508a8..e558498 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -35,6 +35,7 @@
import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.sensor.VirtualSensor;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.IVirtualDisplayCallback;
@@ -314,13 +315,15 @@
@Override // Binder call
public IVirtualDevice createVirtualDevice(
IBinder token,
- String packageName,
+ AttributionSource attributionSource,
int associationId,
@NonNull VirtualDeviceParams params,
@NonNull IVirtualDeviceActivityListener activityListener,
@NonNull IVirtualDeviceSoundEffectListener soundEffectListener) {
createVirtualDevice_enforcePermission();
+ attributionSource.enforceCallingUid();
final int callingUid = getCallingUid();
+ final String packageName = attributionSource.getPackageName();
if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) {
throw new SecurityException(
"Package name " + packageName + " does not belong to calling uid "
@@ -340,10 +343,9 @@
final int deviceId = sNextUniqueIndex.getAndIncrement();
final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
- VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
- associationInfo, VirtualDeviceManagerService.this, token, callingUid,
- deviceId, cameraAccessController,
- mPendingTrampolineCallback, activityListener,
+ VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), associationInfo,
+ VirtualDeviceManagerService.this, token, attributionSource, deviceId,
+ cameraAccessController, mPendingTrampolineCallback, activityListener,
soundEffectListener, runningAppsChangedCallback, params);
synchronized (mVirtualDeviceManagerLock) {
if (mVirtualDevices.size() == 0) {
diff --git a/services/companion/java/com/android/server/companion/virtual/flags.aconfig b/services/companion/java/com/android/server/companion/virtual/flags.aconfig
new file mode 100644
index 0000000..4fe4c87
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.companion.virtual"
+
+flag {
+ name: "dump_history"
+ namespace: "virtual_devices"
+ description: "This flag controls if a history of virtual devices is shown in dumpsys virtualdevices"
+ bug: "293114719"
+}
\ No newline at end of file
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 315972c..f594170 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -1225,15 +1225,13 @@
public ContentCaptureOptions getOptions(@UserIdInt int userId,
@NonNull String packageName) {
boolean isContentCaptureReceiverEnabled;
- boolean isContentProtectionReceiverEnabled;
+ boolean isContentProtectionReceiverEnabled =
+ isContentProtectionReceiverEnabled(userId, packageName);
ArraySet<ComponentName> whitelistedComponents = null;
synchronized (mGlobalWhitelistStateLock) {
isContentCaptureReceiverEnabled =
isContentCaptureReceiverEnabled(userId, packageName);
- isContentProtectionReceiverEnabled =
- isContentProtectionReceiverEnabled(userId, packageName);
-
if (!isContentCaptureReceiverEnabled) {
// Full package is not allowlisted: check individual components next
whitelistedComponents = getWhitelistedComponents(userId, packageName);
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index b67e627..d47a399 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -81,7 +81,7 @@
# when a notification has been clicked
27520 notification_clicked (key|3),(lifespan|1),(freshness|1),(exposure|1),(rank|1),(count|1)
# when a notification action button has been clicked
-27521 notification_action_clicked (key|3),(action_index|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1),(count|1)
+27521 notification_action_clicked (key|3),(piIdentifier|3),(pendingIntent|3),(action_index|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1),(count|1)
# when a notification has been canceled
27530 notification_canceled (key|3),(reason|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1),(count|1),(listener|3)
# replaces 27510 with a row per notification
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index f4f5c95..d256aea 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -580,7 +580,6 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_10,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_50,
- PackageHealthObserverImpact.USER_IMPACT_LEVEL_60,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100})
public @interface PackageHealthObserverImpact {
@@ -591,7 +590,6 @@
/* Actions having medium user impact, user of a device will likely notice. */
int USER_IMPACT_LEVEL_30 = 30;
int USER_IMPACT_LEVEL_50 = 50;
- int USER_IMPACT_LEVEL_60 = 60;
int USER_IMPACT_LEVEL_70 = 70;
/* Action has high user impact, a last resort, user of a device will be very frustrated. */
int USER_IMPACT_LEVEL_100 = 100;
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index c6e9a7d..7acca19 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -456,7 +456,13 @@
final List<SubscriptionInfo> subscriptionInfos = new ArrayList<>();
Binder.withCleanCallingIdentity(
() -> {
- subscriptionInfos.addAll(subMgr.getSubscriptionsInGroup(subscriptionGroup));
+ List<SubscriptionInfo> subsInGroup =
+ subMgr.getSubscriptionsInGroup(subscriptionGroup);
+ if (subsInGroup == null) {
+ logWtf("Received null from getSubscriptionsInGroup");
+ subsInGroup = Collections.emptyList();
+ }
+ subscriptionInfos.addAll(subsInGroup);
});
for (SubscriptionInfo info : subscriptionInfos) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 04ebb2b..610b8af 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -156,6 +156,7 @@
private final PowerProfile mPowerProfile;
private final CpuScalingPolicies mCpuScalingPolicies;
+ private final BatteryStatsImpl.BatteryStatsConfig mBatteryStatsConfig;
final BatteryStatsImpl mStats;
final CpuWakeupStats mCpuWakeupStats;
private final BatteryUsageStatsStore mBatteryUsageStatsStore;
@@ -374,22 +375,24 @@
mPowerProfile = new PowerProfile(context);
mCpuScalingPolicies = new CpuScalingPolicyReader().read();
- mStats = new BatteryStatsImpl(systemDir, handler, this,
+ final boolean resetOnUnplugHighBatteryLevel = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_batteryStatsResetOnUnplugHighBatteryLevel);
+ final boolean resetOnUnplugAfterSignificantCharge = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_batteryStatsResetOnUnplugAfterSignificantCharge);
+ final long powerStatsThrottlePeriodCpu = context.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodCpu);
+ mBatteryStatsConfig =
+ new BatteryStatsImpl.BatteryStatsConfig.Builder()
+ .setResetOnUnplugHighBatteryLevel(resetOnUnplugHighBatteryLevel)
+ .setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge)
+ .setPowerStatsThrottlePeriodCpu(powerStatsThrottlePeriodCpu)
+ .build();
+ mStats = new BatteryStatsImpl(mBatteryStatsConfig, systemDir, handler, this,
this, mUserManagerUserInfoProvider, mPowerProfile, mCpuScalingPolicies);
mWorker = new BatteryExternalStatsWorker(context, mStats);
mStats.setExternalStatsSyncLocked(mWorker);
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
-
- final boolean resetOnUnplugHighBatteryLevel = context.getResources().getBoolean(
- com.android.internal.R.bool.config_batteryStatsResetOnUnplugHighBatteryLevel);
- final boolean resetOnUnplugAfterSignificantCharge = context.getResources().getBoolean(
- com.android.internal.R.bool.config_batteryStatsResetOnUnplugAfterSignificantCharge);
- mStats.setBatteryStatsConfig(
- new BatteryStatsImpl.BatteryStatsConfig.Builder()
- .setResetOnUnplugHighBatteryLevel(resetOnUnplugHighBatteryLevel)
- .setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge)
- .build());
mStats.startTrackingSystemServerCpuTime();
if (BATTERY_USAGE_STORE_ENABLED) {
@@ -2591,6 +2594,7 @@
pw.println(" --proto: output as a binary protobuffer");
pw.println(" --model power-profile: use the power profile model"
+ " even if measured energy is available");
+ pw.println(" --sample: collect and dump a sample of stats for debugging purpose");
pw.println(" <package.name>: optional name of package to filter output by.");
pw.println(" -h: print this help text.");
pw.println("Battery stats (batterystats) commands:");
@@ -2623,6 +2627,14 @@
}
}
+ private void dumpStatsSample(PrintWriter pw) {
+ mStats.dumpStatsSample(pw);
+ }
+
+ private void dumpAggregatedStats(PrintWriter pw) {
+ mStats.dumpAggregatedStats(pw, /* startTime */ 0, /* endTime */0);
+ }
+
private void dumpMeasuredEnergyStats(PrintWriter pw) {
// Wait for the completion of pending works if there is any
awaitCompletion();
@@ -2864,6 +2876,12 @@
mCpuWakeupStats.dump(new IndentingPrintWriter(pw, " "),
SystemClock.elapsedRealtime());
return;
+ } else if ("--sample".equals(arg)) {
+ dumpStatsSample(pw);
+ return;
+ } else if ("--aggregated".equals(arg)) {
+ dumpAggregatedStats(pw);
+ return;
} else if ("-a".equals(arg)) {
flags |= BatteryStats.DUMP_VERBOSE;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
@@ -2924,6 +2942,7 @@
in.unmarshall(raw, 0, raw.length);
in.setDataPosition(0);
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
+ mBatteryStatsConfig,
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider, mPowerProfile,
mCpuScalingPolicies);
@@ -2965,6 +2984,7 @@
in.unmarshall(raw, 0, raw.length);
in.setDataPosition(0);
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
+ mBatteryStatsConfig,
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider, mPowerProfile,
mCpuScalingPolicies);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 0e4465d..1d09dce 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -87,7 +87,6 @@
DeviceConfig.NAMESPACE_CAMERA_NATIVE,
DeviceConfig.NAMESPACE_CONFIGURATION,
DeviceConfig.NAMESPACE_CONNECTIVITY,
- DeviceConfig.NAMESPACE_CORE_EXPERIMENTS_TEAM_INTERNAL,
DeviceConfig.NAMESPACE_EDGETPU_NATIVE,
DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
@@ -115,19 +114,29 @@
NAMESPACE_TETHERING_U_OR_LATER_NATIVE
};
+ // All the aconfig flags under the listed DeviceConfig scopes will be synced to native level.
+ @VisibleForTesting
+ static final String[] sDeviceConfigAconfigScopes = new String[] {
+ DeviceConfig.NAMESPACE_CORE_EXPERIMENTS_TEAM_INTERNAL,
+ };
+
private final String[] mGlobalSettings;
private final String[] mDeviceConfigScopes;
+ private final String[] mDeviceConfigAconfigScopes;
+
private final ContentResolver mContentResolver;
@VisibleForTesting
protected SettingsToPropertiesMapper(ContentResolver contentResolver,
String[] globalSettings,
- String[] deviceConfigScopes) {
+ String[] deviceConfigScopes,
+ String[] deviceConfigAconfigScopes) {
mContentResolver = contentResolver;
mGlobalSettings = globalSettings;
mDeviceConfigScopes = deviceConfigScopes;
+ mDeviceConfigAconfigScopes = deviceConfigAconfigScopes;
}
@VisibleForTesting
@@ -173,6 +182,36 @@
return;
}
setProperty(propertyName, properties.getString(key, null));
+
+ // for legacy namespaces, they can also be used for trunk stable
+ // purposes. so push flag also into trunk stable slot in sys prop,
+ // later all legacy usage will be refactored and the sync to old
+ // sys prop slot can be removed.
+ String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key);
+ if (aconfigPropertyName == null) {
+ log("unable to construct system property for " + scope + "/"
+ + key);
+ return;
+ }
+ setProperty(aconfigPropertyName, properties.getString(key, null));
+ }
+ });
+ }
+
+ for (String deviceConfigAconfigScope : mDeviceConfigAconfigScopes) {
+ DeviceConfig.addOnPropertiesChangedListener(
+ deviceConfigAconfigScope,
+ AsyncTask.THREAD_POOL_EXECUTOR,
+ (DeviceConfig.Properties properties) -> {
+ String scope = properties.getNamespace();
+ for (String key : properties.getKeyset()) {
+ String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key);
+ if (aconfigPropertyName == null) {
+ log("unable to construct system property for " + scope + "/"
+ + key);
+ return;
+ }
+ setProperty(aconfigPropertyName, properties.getString(key, null));
}
});
}
@@ -180,7 +219,10 @@
public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
SettingsToPropertiesMapper mapper = new SettingsToPropertiesMapper(
- contentResolver, sGlobalSettings, sDeviceConfigScopes);
+ contentResolver,
+ sGlobalSettings,
+ sDeviceConfigScopes,
+ sDeviceConfigAconfigScopes);
mapper.updatePropertiesFromSettings();
return mapper;
}
@@ -243,6 +285,28 @@
return propertyName;
}
+ /**
+ * system property name constructing rule for aconfig flags:
+ * "persist.device_config.aconfig_flags.[category_name].[flag_name]".
+ * If the name contains invalid characters or substrings for system property name,
+ * will return null.
+ * @param categoryName
+ * @param flagName
+ * @return
+ */
+ @VisibleForTesting
+ static String makeAconfigFlagPropertyName(String categoryName, String flagName) {
+ String propertyName = SYSTEM_PROPERTY_PREFIX + "aconfig_flags." +
+ categoryName + "." + flagName;
+
+ if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX)
+ || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) {
+ return null;
+ }
+
+ return propertyName;
+ }
+
private void setProperty(String key, String value) {
// Check if need to clear the property
if (value == null) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index d89171d..4a523af 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -391,7 +391,6 @@
final boolean wasBtScoRequested = isBluetoothScoRequested();
CommunicationRouteClient client;
-
// Save previous client route in case of failure to start BT SCO audio
AudioDeviceAttributes prevClientDevice = null;
boolean prevPrivileged = false;
@@ -1043,7 +1042,7 @@
synchronized (mBluetoothAudioStateLock) {
mBluetoothScoOn = on;
updateAudioHalBluetoothState();
- postUpdateCommunicationRouteClient(eventSource);
+ postUpdateCommunicationRouteClient(isBluetoothScoRequested(), eventSource);
}
}
@@ -1395,8 +1394,10 @@
MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset);
}
- /*package*/ void postUpdateCommunicationRouteClient(String eventSource) {
- sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE, eventSource);
+ /*package*/ void postUpdateCommunicationRouteClient(
+ boolean wasBtScoRequested, String eventSource) {
+ sendILMsgNoDelay(MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE,
+ wasBtScoRequested ? 1 : 0, eventSource);
}
/*package*/ void postSetCommunicationDeviceForClient(CommunicationDeviceInfo info) {
@@ -1708,7 +1709,8 @@
: AudioSystem.STREAM_DEFAULT);
if (btInfo.mProfile == BluetoothProfile.LE_AUDIO
|| btInfo.mProfile == BluetoothProfile.HEARING_AID) {
- onUpdateCommunicationRouteClient("setBluetoothActiveDevice");
+ onUpdateCommunicationRouteClient(isBluetoothScoRequested(),
+ "setBluetoothActiveDevice");
}
}
}
@@ -1762,9 +1764,11 @@
case MSG_I_SET_MODE_OWNER:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
+ boolean wasBtScoRequested = isBluetoothScoRequested();
mAudioModeOwner = (AudioModeInfo) msg.obj;
if (mAudioModeOwner.mMode != AudioSystem.MODE_RINGTONE) {
- onUpdateCommunicationRouteClient("setNewModeOwner");
+ onUpdateCommunicationRouteClient(
+ wasBtScoRequested, "setNewModeOwner");
}
}
}
@@ -1787,10 +1791,10 @@
}
break;
- case MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT:
+ case MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- onUpdateCommunicationRouteClient((String) msg.obj);
+ onUpdateCommunicationRouteClient(msg.arg1 == 1, (String) msg.obj);
}
}
break;
@@ -1971,7 +1975,7 @@
private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
private static final int MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT = 42;
- private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43;
+ private static final int MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43;
private static final int MSG_I_SCO_AUDIO_STATE_CHANGED = 44;
private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45;
@@ -2328,16 +2332,20 @@
*/
// @GuardedBy("mSetModeLock")
@GuardedBy("mDeviceStateLock")
- private void onUpdateCommunicationRouteClient(String eventSource) {
- updateCommunicationRoute(eventSource);
+ private void onUpdateCommunicationRouteClient(boolean wasBtScoRequested, String eventSource) {
CommunicationRouteClient crc = topCommunicationRouteClient();
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "onUpdateCommunicationRouteClient, crc: "
- + crc + " eventSource: " + eventSource);
+ Log.v(TAG, "onUpdateCommunicationRouteClient, crc: " + crc
+ + " wasBtScoRequested: " + wasBtScoRequested + " eventSource: " + eventSource);
}
if (crc != null) {
setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(),
BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource);
+ } else {
+ if (!isBluetoothScoRequested() && wasBtScoRequested) {
+ mBtHelper.stopBluetoothSco(eventSource);
+ }
+ updateCommunicationRoute(eventSource);
}
}
@@ -2401,7 +2409,11 @@
}
@GuardedBy("mDeviceStateLock")
- private boolean communnicationDeviceCompatOn() {
+ // LE Audio: For system server (Telecom) and APKs targeting S and above, we let the audio
+ // policy routing rules select the default communication device.
+ // For older APKs, we force LE Audio headset when connected as those APKs cannot select a LE
+ // Audiodevice explicitly.
+ private boolean communnicationDeviceLeAudioCompatOn() {
return mAudioModeOwner.mMode == AudioSystem.MODE_IN_COMMUNICATION
&& !(CompatChanges.isChangeEnabled(
USE_SET_COMMUNICATION_DEVICE, mAudioModeOwner.mUid)
@@ -2409,19 +2421,25 @@
}
@GuardedBy("mDeviceStateLock")
+ // Hearing Aid: For system server (Telecom) and IN_CALL mode we let the audio
+ // policy routing rules select the default communication device.
+ // For 3p apps and IN_COMMUNICATION mode we force Hearing aid when connected to maintain
+ // backwards compatibility
+ private boolean communnicationDeviceHaCompatOn() {
+ return mAudioModeOwner.mMode == AudioSystem.MODE_IN_COMMUNICATION
+ && !(mAudioModeOwner.mUid == android.os.Process.SYSTEM_UID);
+ }
+
+ @GuardedBy("mDeviceStateLock")
AudioDeviceAttributes getDefaultCommunicationDevice() {
- // For system server (Telecom) and APKs targeting S and above, we let the audio
- // policy routing rules select the default communication device.
- // For older APKs, we force Hearing Aid or LE Audio headset when connected as
- // those APKs cannot select a LE Audio or Hearing Aid device explicitly.
AudioDeviceAttributes device = null;
- if (communnicationDeviceCompatOn()) {
- // If both LE and Hearing Aid are active (thie should not happen),
- // priority to Hearing Aid.
+ // If both LE and Hearing Aid are active (thie should not happen),
+ // priority to Hearing Aid.
+ if (communnicationDeviceHaCompatOn()) {
device = mDeviceInventory.getDeviceOfType(AudioSystem.DEVICE_OUT_HEARING_AID);
- if (device == null) {
- device = mDeviceInventory.getDeviceOfType(AudioSystem.DEVICE_OUT_BLE_HEADSET);
- }
+ }
+ if (device == null && communnicationDeviceLeAudioCompatOn()) {
+ device = mDeviceInventory.getDeviceOfType(AudioSystem.DEVICE_OUT_BLE_HEADSET);
}
return device;
}
@@ -2431,6 +2449,7 @@
List<AudioRecordingConfiguration> recordConfigs) {
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
+ final boolean wasBtScoRequested = isBluetoothScoRequested();
boolean updateCommunicationRoute = false;
for (CommunicationRouteClient crc : mCommunicationRouteClients) {
boolean wasActive = crc.isActive();
@@ -2459,7 +2478,8 @@
}
}
if (updateCommunicationRoute) {
- postUpdateCommunicationRouteClient("updateCommunicationRouteClientsActivity");
+ postUpdateCommunicationRouteClient(
+ wasBtScoRequested, "updateCommunicationRouteClientsActivity");
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3f50dfd3..5f15995 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -43,6 +43,7 @@
import static com.android.server.utils.EventLogger.Event.ALOGW;
import android.Manifest;
+import android.annotation.EnforcePermission;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -7636,6 +7637,10 @@
throw new IllegalArgumentException("Illegal BluetoothProfile profile for device "
+ previousDevice + " -> " + newDevice + ". Got: " + profile);
}
+
+ sDeviceLogger.enqueue(new EventLogger.StringEvent("BlutoothActiveDeviceChanged for "
+ + BluetoothProfile.getProfileName(profile) + ", device update " + previousDevice
+ + " -> " + newDevice));
AudioDeviceBroker.BtDeviceChangedData data =
new AudioDeviceBroker.BtDeviceChangedData(newDevice, previousDevice, info,
"AudioService");
@@ -9685,6 +9690,9 @@
}
} else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+ sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "BluetoothAdapter ACTION_STATE_CHANGED with state " + state));
+
if (state == BluetoothAdapter.STATE_OFF ||
state == BluetoothAdapter.STATE_TURNING_OFF) {
mDeviceBroker.disconnectAllBluetoothProfiles();
@@ -9998,6 +10006,14 @@
return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName);
}
+ /** see {@link AudioManager#getFocusDuckedUidsForTest()} */
+ @Override
+ @EnforcePermission("android.permission.QUERY_AUDIO_STATE")
+ public @NonNull List<Integer> getFocusDuckedUidsForTest() {
+ super.getFocusDuckedUidsForTest_enforcePermission();
+ return mPlaybackMonitor.getFocusDuckedUids();
+ }
+
public void unregisterAudioFocusClient(String clientId) {
new MediaMetrics.Item(mMetricsId + "focus")
.set(MediaMetrics.Property.CLIENT_NAME, clientId)
@@ -10014,6 +10030,68 @@
return mMediaFocusControl.getFocusRampTimeMs(focusGain, attr);
}
+ /**
+ * Test method to return the duration of the fade out applied on the players of a focus loser
+ * @see AudioManager#getFocusFadeOutDurationForTest()
+ * @return the fade out duration, in ms
+ */
+ @EnforcePermission("android.permission.QUERY_AUDIO_STATE")
+ public long getFocusFadeOutDurationForTest() {
+ super.getFocusFadeOutDurationForTest_enforcePermission();
+ return mMediaFocusControl.getFocusFadeOutDurationForTest();
+ }
+
+ /**
+ * Test method to return the length of time after a fade out before the focus loser is unmuted
+ * (and is faded back in).
+ * @see AudioManager#getFocusUnmuteDelayAfterFadeOutForTest()
+ * @return the time gap after a fade out completion on focus loss, and fade in start, in ms
+ */
+ @Override
+ @EnforcePermission("android.permission.QUERY_AUDIO_STATE")
+ public long getFocusUnmuteDelayAfterFadeOutForTest() {
+ super.getFocusUnmuteDelayAfterFadeOutForTest_enforcePermission();
+ return mMediaFocusControl.getFocusUnmuteDelayAfterFadeOutForTest();
+ }
+
+ /**
+ * Test method to start preventing applications from requesting audio focus during a test,
+ * which could interfere with the testing of the functionality/behavior under test.
+ * Calling this method needs to be paired with a call to {@link #exitAudioFocusFreezeForTest}
+ * when the testing is done. If this is not the case (e.g. in case of a test crash),
+ * a death observer mechanism will ensure the system is not left in a bad state, but this should
+ * not be relied on when implementing tests.
+ * @see AudioManager#enterAudioFocusFreezeForTest(List)
+ * @param cb IBinder to track the death of the client of this method
+ * @param exemptedUids a list of UIDs that are exempt from the freeze. This would for instance
+ * be those of the test runner and other players used in the test
+ * @return true if the focus freeze mode is successfully entered, false if there was an issue,
+ * such as another freeze currently used.
+ */
+ @Override
+ @EnforcePermission("android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ public boolean enterAudioFocusFreezeForTest(IBinder cb, int[] exemptedUids) {
+ super.enterAudioFocusFreezeForTest_enforcePermission();
+ Objects.requireNonNull(exemptedUids);
+ Objects.requireNonNull(cb);
+ return mMediaFocusControl.enterAudioFocusFreezeForTest(cb, exemptedUids);
+ }
+
+ /**
+ * Test method to end preventing applications from requesting audio focus during a test.
+ * @see AudioManager#exitAudioFocusFreezeForTest()
+ * @param cb IBinder identifying the client of this method
+ * @return true if the focus freeze mode is successfully exited, false if there was an issue,
+ * such as the freeze already having ended, or not started.
+ */
+ @Override
+ @EnforcePermission("android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ public boolean exitAudioFocusFreezeForTest(IBinder cb) {
+ super.exitAudioFocusFreezeForTest_enforcePermission();
+ Objects.requireNonNull(cb);
+ return mMediaFocusControl.exitAudioFocusFreezeForTest(cb);
+ }
+
/** only public for mocking/spying, do not call outside of AudioService */
@VisibleForTesting
public boolean hasAudioFocusUsers() {
@@ -10021,6 +10099,7 @@
}
/** see {@link AudioManager#getFadeOutDurationOnFocusLossMillis(AudioAttributes)} */
+ @Override
public long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) {
if (!enforceQueryAudioStateForTest("fade out duration")) {
return 0;
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 3560797..b350363 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -329,7 +329,7 @@
default:
break;
}
- if(broadcast) {
+ if (broadcast) {
broadcastScoConnectionState(scoAudioState);
//FIXME: this is to maintain compatibility with deprecated intent
// AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
@@ -459,6 +459,8 @@
//@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onBtProfileDisconnected(int profile) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "BT profile " + BluetoothProfile.getProfileName(profile) + " disconnected"));
switch (profile) {
case BluetoothProfile.A2DP:
mA2dp = null;
@@ -487,6 +489,9 @@
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "BT profile " + BluetoothProfile.getProfileName(profile) + " connected to proxy "
+ + proxy));
if (profile == BluetoothProfile.HEADSET) {
onHeadsetProfileConnected((BluetoothHeadset) proxy);
return;
@@ -718,8 +723,10 @@
checkScoAudioState();
if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
// Make sure that the state transitions to CONNECTING even if we cannot initiate
- // the connection.
- broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+ // the connection except if already connected internally
+ if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) {
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+ }
switch (mScoAudioState) {
case SCO_STATE_INACTIVE:
mScoAudioMode = scoAudioMode;
@@ -775,7 +782,7 @@
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
break;
case SCO_STATE_ACTIVE_INTERNAL:
- Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
+ // Already in ACTIVE mode, simply return
break;
case SCO_STATE_ACTIVE_EXTERNAL:
/* Confirm SCO Audio connection to requesting app as it is already connected
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 88a4b05..010d5f4 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -43,7 +43,7 @@
public class FocusRequester {
// on purpose not using this classe's name, as it will only be used from MediaFocusControl
- private static final String TAG = "MediaFocusControl";
+ private static final String TAG = "FocusRequester";
private static final boolean DEBUG = false;
private AudioFocusDeathHandler mDeathHandler; // may be null
@@ -340,6 +340,9 @@
@GuardedBy("MediaFocusControl.mAudioFocusLock")
boolean handleFocusLossFromGain(int focusGain, final FocusRequester frWinner, boolean forceDuck)
{
+ if (DEBUG) {
+ Log.i(TAG, "handleFocusLossFromGain for " + mClientId + " gain:" + focusGain);
+ }
final int focusLoss = focusLossForGainRequest(focusGain);
handleFocusLoss(focusLoss, frWinner, forceDuck);
return (focusLoss == AudioManager.AUDIOFOCUS_LOSS);
@@ -378,6 +381,9 @@
@GuardedBy("MediaFocusControl.mAudioFocusLock")
void handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner, boolean forceDuck)
{
+ if (DEBUG) {
+ Log.i(TAG, "handleFocusLoss for " + mClientId + " loss:" + focusLoss);
+ }
try {
if (focusLoss != mFocusLossReceived) {
mFocusLossReceived = focusLoss;
@@ -427,6 +433,9 @@
toAudioFocusInfo(), true /* wasDispatched */);
mFocusLossWasNotified = true;
fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
+ } else if (DEBUG) {
+ Log.i(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
+ + " to " + mClientId + " no IAudioFocusDispatcher");
}
}
} catch (android.os.RemoteException e) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index b218096..65f6c9b 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -45,6 +45,7 @@
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
@@ -122,6 +123,23 @@
dumpMultiAudioFocus(pw);
}
+ /**
+ * Test method to return the duration of the fade out applied on the players of a focus loser
+ * @return the fade out duration in ms
+ */
+ public long getFocusFadeOutDurationForTest() {
+ return FadeOutManager.FADE_OUT_DURATION_MS;
+ }
+
+ /**
+ * Test method to return the length of time after a fade out before the focus loser is unmuted
+ * (and is faded back in).
+ * @return the time gap after a fade out completion on focus loss, and fade in start in ms
+ */
+ public long getFocusUnmuteDelayAfterFadeOutForTest() {
+ return FadeOutManager.DELAY_FADE_IN_OFFENDERS_MS;
+ }
+
//=================================================================
// PlayerFocusEnforcer implementation
@Override
@@ -304,17 +322,26 @@
@GuardedBy("mAudioFocusLock")
private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr,
boolean forceDuck) {
+ if (DEBUG) {
+ Log.i(TAG, "propagateFocusLossFromGain_syncAf gain:" + focusGain);
+ }
final List<String> clientsToRemove = new LinkedList<String>();
// going through the audio focus stack to signal new focus, traversing order doesn't
// matter as all entries respond to the same external focus gain
if (!mFocusStack.empty()) {
for (FocusRequester focusLoser : mFocusStack) {
+ if (DEBUG) {
+ Log.i(TAG, "propagateFocusLossFromGain_syncAf checking client:"
+ + focusLoser.getClientId());
+ }
final boolean isDefinitiveLoss =
focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
if (isDefinitiveLoss) {
clientsToRemove.add(focusLoser.getClientId());
}
}
+ } else if (DEBUG) {
+ Log.i(TAG, "propagateFocusLossFromGain_syncAf empty stack");
}
if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
@@ -370,6 +397,9 @@
@GuardedBy("mAudioFocusLock")
private void removeFocusStackEntry(String clientToRemove, boolean signal,
boolean notifyFocusFollowers) {
+ if (DEBUG) {
+ Log.i(TAG, "removeFocusStackEntry client:" + clientToRemove);
+ }
AudioFocusInfo abandonSource = null;
// is the current top of the focus stack abandoning focus? (because of request, not death)
if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove))
@@ -1000,6 +1030,24 @@
}
synchronized(mAudioFocusLock) {
+ // check whether a focus freeze is in place and filter
+ if (isFocusFrozenForTest()) {
+ int focusRequesterUid;
+ if ((flags & AudioManager.AUDIOFOCUS_FLAG_TEST)
+ == AudioManager.AUDIOFOCUS_FLAG_TEST) {
+ focusRequesterUid = testUid;
+ } else {
+ focusRequesterUid = Binder.getCallingUid();
+ }
+ if (isFocusFrozenForTestForUid(focusRequesterUid)) {
+ Log.i(TAG, "requestAudioFocus: focus frozen for test for uid:"
+ + focusRequesterUid);
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+ Log.i(TAG, "requestAudioFocus: focus frozen for test but uid:" + focusRequesterUid
+ + " is exempt");
+ }
+
if (mFocusStack.size() > MAX_STACK_SIZE) {
Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()");
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
@@ -1191,6 +1239,110 @@
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
+ /**
+ * Reference to the caller of {@link #enterAudioFocusFreezeForTest(IBinder, int[])}
+ * Will be null when there is no focus freeze for test
+ */
+ @GuardedBy("mAudioFocusLock")
+ @Nullable
+ private IBinder mFocusFreezerForTest = null;
+
+ /**
+ * The death handler for {@link #mFocusFreezerForTest}
+ * Will be null when there is no focus freeze for test
+ */
+ @GuardedBy("mAudioFocusLock")
+ @Nullable
+ private IBinder.DeathRecipient mFocusFreezerDeathHandler = null;
+
+ /**
+ * Array of UIDs exempt from focus freeze when focus is frozen for test, null during normal
+ * operations.
+ * Will be null when there is no focus freeze for test
+ */
+ @GuardedBy("mAudioFocusLock")
+ @Nullable
+ private int[] mFocusFreezeExemptUids = null;
+
+ @GuardedBy("mAudioFocusLock")
+ private boolean isFocusFrozenForTest() {
+ return (mFocusFreezerForTest != null);
+ }
+
+ /**
+ * Checks if the given UID can request focus when a focus freeze is in place for a test.
+ * Focus can be requested if focus is not frozen or if it's frozen but the UID is exempt.
+ * @param uidToCheck
+ * @return true if that UID is barred from requesting focus, false if its focus request
+ * can proceed being processed
+ */
+ @GuardedBy("mAudioFocusLock")
+ private boolean isFocusFrozenForTestForUid(int uidToCheck) {
+ if (isFocusFrozenForTest()) {
+ return false;
+ }
+ // check the list of exempts (array is not null because we're in a freeze for test
+ for (int uid : mFocusFreezeExemptUids) {
+ if (uid == uidToCheck) {
+ return false;
+ }
+ }
+ // uid was not found in the exempt list, its focus request is denied
+ return true;
+ }
+
+ protected boolean enterAudioFocusFreezeForTest(
+ @NonNull IBinder cb, @NonNull int[] exemptedUids) {
+ Log.i(TAG, "enterAudioFocusFreezeForTest UIDs exempt:" + Arrays.toString(exemptedUids));
+ synchronized (mAudioFocusLock) {
+ if (mFocusFreezerForTest != null) {
+ Log.e(TAG, "Error enterAudioFocusFreezeForTest: focus already frozen");
+ return false;
+ }
+ // new focus freeze, register death handler
+ try {
+ mFocusFreezerDeathHandler = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ Log.i(TAG, "Audio focus freezer died, exiting focus freeze for test");
+ releaseFocusFreeze();
+ }
+ };
+ cb.linkToDeath(mFocusFreezerDeathHandler, 0);
+ mFocusFreezerForTest = cb;
+ mFocusFreezeExemptUids = exemptedUids.clone();
+ } catch (RemoteException e) {
+ // client has already died!
+ mFocusFreezerForTest = null;
+ mFocusFreezeExemptUids = null;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected boolean exitAudioFocusFreezeForTest(@NonNull IBinder cb) {
+ synchronized (mAudioFocusLock) {
+ if (mFocusFreezerForTest != cb) {
+ Log.e(TAG, "Error exitAudioFocusFreezeForTest: "
+ + ((mFocusFreezerForTest == null)
+ ? "call to exit while not frozen"
+ : "call to exit not coming from freeze owner"));
+ return false;
+ }
+ mFocusFreezerForTest.unlinkToDeath(mFocusFreezerDeathHandler, 0);
+ releaseFocusFreeze();
+ }
+ return true;
+ }
+
+ private void releaseFocusFreeze() {
+ synchronized (mAudioFocusLock) {
+ mFocusFreezerDeathHandler = null;
+ mFocusFreezeExemptUids = null;
+ mFocusFreezerForTest = null;
+ }
+ }
protected void unregisterAudioFocusClient(String clientId) {
synchronized(mAudioFocusLock) {
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 23a0782..54fa6fb 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -1208,6 +1208,17 @@
}
}
+ protected @NonNull List<Integer> getFocusDuckedUids() {
+ final ArrayList<Integer> duckedUids;
+ synchronized (mPlayerLock) {
+ duckedUids = new ArrayList(mDuckingManager.mDuckers.keySet());
+ }
+ if (DEBUG) {
+ Log.i(TAG, "current ducked UIDs: " + duckedUids);
+ }
+ return duckedUids;
+ }
+
//=================================================================
// For logging
private static final class PlayerEvent extends EventLogger.Event {
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 0380756..97e5c6f 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -18,10 +18,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -53,9 +57,21 @@
@NonNull private final Map<Integer, AuthenticationStats> mUserAuthenticationStatsMap;
- @NonNull private AuthenticationStatsPersister mAuthenticationStatsPersister;
+ // TODO(b/295582896): Find a way to make this NonNull
+ @Nullable private AuthenticationStatsPersister mAuthenticationStatsPersister;
@NonNull private BiometricNotification mBiometricNotification;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(@NonNull Context context, @NonNull Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId != UserHandle.USER_NULL
+ && intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
+ onUserRemoved(userId);
+ }
+ }
+ };
+
public AuthenticationStatsCollector(@NonNull Context context, int modality,
@NonNull BiometricNotification biometricNotification) {
mContext = context;
@@ -64,6 +80,8 @@
mUserAuthenticationStatsMap = new HashMap<>();
mModality = modality;
mBiometricNotification = biometricNotification;
+
+ context.registerReceiver(mBroadcastReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
}
private void initializeUserAuthenticationStatsMap() {
@@ -75,8 +93,9 @@
/** Update total authentication and rejected attempts. */
public void authenticate(int userId, boolean authenticated) {
- // Initialize mUserAuthenticationStatsMap when authenticate to ensure SharedPreferences
- // are ready for application use and avoid ramdump issue.
+ // SharedPreference is not ready when starting system server, initialize
+ // mUserAuthenticationStatsMap in authentication to ensure SharedPreference
+ // is ready for application use.
if (mUserAuthenticationStatsMap.isEmpty()) {
initializeUserAuthenticationStatsMap();
}
@@ -146,6 +165,14 @@
}
}
+ private void onUserRemoved(final int userId) {
+ if (mAuthenticationStatsPersister == null) {
+ initializeUserAuthenticationStatsMap();
+ }
+ mUserAuthenticationStatsMap.remove(userId);
+ mAuthenticationStatsPersister.removeFrrStats(userId);
+ }
+
/**
* Only being used in tests. Callers should not make any changes to the returned
* authentication stats.
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
index 96150a6..21e93a8 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
@@ -94,12 +94,35 @@
}
/**
+ * Remove frr data for a specific user.
+ */
+ public void removeFrrStats(int userId) {
+ try {
+ // Copy into a new HashSet to allow modification.
+ Set<String> frrStatsSet = new HashSet<>(readFrrStats());
+
+ // Remove the old authentication stat for the user if it exists.
+ for (Iterator<String> iterator = frrStatsSet.iterator(); iterator.hasNext();) {
+ String frrStats = iterator.next();
+ JSONObject frrStatJson = new JSONObject(frrStats);
+ if (getValue(frrStatJson, USER_ID).equals(String.valueOf(userId))) {
+ iterator.remove();
+ break;
+ }
+ }
+
+ mSharedPreferences.edit().putStringSet(KEY, frrStatsSet).apply();
+ } catch (JSONException ignored) {
+ }
+ }
+
+ /**
* Persist frr data for a specific user.
*/
public void persistFrrStats(int userId, int totalAttempts, int rejectedAttempts,
int enrollmentNotifications, int modality) {
try {
- // Copy into a new HashSet to avoid iterator exception.
+ // Copy into a new HashSet to allow modification.
Set<String> frrStatsSet = new HashSet<>(readFrrStats());
// Remove the old authentication stat for the user if it exists.
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
index 230ba63..2ff695d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
@@ -87,11 +87,11 @@
public static void showFaceEnrollNotification(@NonNull Context context) {
final String name =
- context.getString(R.string.face_recalibrate_notification_name);
+ context.getString(R.string.device_unlock_notification_name);
final String title =
- context.getString(R.string.fingerprint_setup_notification_title);
+ context.getString(R.string.alternative_unlock_setup_notification_title);
final String content =
- context.getString(R.string.face_setup_notification_title);
+ context.getString(R.string.alternative_face_setup_notification_content);
final Intent intent = new Intent(FACE_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
@@ -112,11 +112,11 @@
public static void showFingerprintEnrollNotification(@NonNull Context context) {
final String name =
- context.getString(R.string.fingerprint_recalibrate_notification_name);
+ context.getString(R.string.device_unlock_notification_name);
final String title =
- context.getString(R.string.fingerprint_setup_notification_title);
+ context.getString(R.string.alternative_unlock_setup_notification_title);
final String content =
- context.getString(R.string.fingerprint_setup_notification_content);
+ context.getString(R.string.alternative_fp_setup_notification_content);
final Intent intent = new Intent(FINGERPRINT_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index d57dc47..8642fb8 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -16,6 +16,9 @@
package com.android.server.display;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Point;
@@ -132,12 +135,15 @@
/**
* Returns the default size of the surface associated with the display, or null if the surface
* is not provided for layer mirroring by SurfaceFlinger. For non virtual displays, this will
- * be the actual display device's size.
+ * be the actual display device's size, reflecting the current rotation.
*/
@Nullable
public Point getDisplaySurfaceDefaultSizeLocked() {
DisplayDeviceInfo displayDeviceInfo = getDisplayDeviceInfoLocked();
- return new Point(displayDeviceInfo.width, displayDeviceInfo.height);
+ final boolean isRotated = mCurrentOrientation == ROTATION_90
+ || mCurrentOrientation == ROTATION_270;
+ return isRotated ? new Point(displayDeviceInfo.height, displayDeviceInfo.width)
+ : new Point(displayDeviceInfo.width, displayDeviceInfo.height);
}
/**
@@ -358,7 +364,7 @@
}
boolean isRotated = (mCurrentOrientation == Surface.ROTATION_90
- || mCurrentOrientation == Surface.ROTATION_270);
+ || mCurrentOrientation == ROTATION_270);
DisplayDeviceInfo info = getDisplayDeviceInfoLocked();
viewport.deviceWidth = isRotated ? info.height : info.width;
viewport.deviceHeight = isRotated ? info.width : info.height;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index c131226..01eceda 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -2093,7 +2093,7 @@
/** Loads the refresh rate profiles. */
private void loadRefreshRateZoneProfiles(RefreshRateConfigs refreshRateConfigs) {
- if (refreshRateConfigs == null) {
+ if (refreshRateConfigs == null || refreshRateConfigs.getRefreshRateZoneProfiles() == null) {
return;
}
for (RefreshRateZone zone :
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 4ad26c4..7ea576d 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -63,10 +63,15 @@
// high errors. This default is introduced to provide a fixed display color
// temperature when sensor readings become unreliable.
private final float mLowLightAmbientColorTemperature;
+ // As above, but used when in strong mode (idle screen brightness mode).
+ private final float mLowLightAmbientColorTemperatureStrong;
+
// In high brightness conditions certain color temperatures can cause peak display
// brightness to drop. This fixed color temperature can be used to compensate for
// this effect.
private final float mHighLightAmbientColorTemperature;
+ // As above, but used when in strong mode (idle screen brightness mode).
+ private final float mHighLightAmbientColorTemperatureStrong;
private final boolean mLightModeAllowed;
@@ -97,9 +102,11 @@
// ambient color temperature to the defaults. A piecewise linear relationship
// between low light brightness and low light bias.
private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSpline;
+ private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSplineStrong;
// A piecewise linear relationship between high light brightness and high light bias.
private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSpline;
+ private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSplineStrong;
private float mLatestAmbientColorTemperature;
private float mLatestAmbientBrightness;
@@ -134,17 +141,29 @@
* @param lowLightAmbientBrightnesses
* The ambient brightness used to map the ambient brightnesses to the biases used to
* interpolate to lowLightAmbientColorTemperature.
+ * @param lowLightAmbientBrightnessesStrong
+ * The ambient brightness used to map the ambient brightnesses to the biases used to
+ * interpolate to lowLightAmbientColorTemperature.
* @param lowLightAmbientBiases
* The biases used to map the ambient brightnesses to the biases used to interpolate to
* lowLightAmbientColorTemperature.
+ * @param lowLightAmbientBiasesStrong
+ * The biases used to map the ambient brightnesses to the biases used to interpolate to
+ * lowLightAmbientColorTemperature.
* @param lowLightAmbientColorTemperature
* The ambient color temperature to which we interpolate to based on the low light curve.
* @param highLightAmbientBrightnesses
* The ambient brightness used to map the ambient brightnesses to the biases used to
* interpolate to highLightAmbientColorTemperature.
+ * @param highLightAmbientBrightnessesStrong
+ * The ambient brightness used to map the ambient brightnesses to the biases used to
+ * interpolate to highLightAmbientColorTemperature.
* @param highLightAmbientBiases
* The biases used to map the ambient brightnesses to the biases used to interpolate to
* highLightAmbientColorTemperature.
+ * @param highLightAmbientBiasesStrong
+ * The biases used to map the ambient brightnesses to the biases used to interpolate to
+ * highLightAmbientColorTemperature.
* @param highLightAmbientColorTemperature
* The ambient color temperature to which we interpolate to based on the high light curve.
* @param ambientColorTemperatures
@@ -170,11 +189,17 @@
@NonNull AmbientFilter colorTemperatureFilter,
@NonNull DisplayWhiteBalanceThrottler throttler,
float[] lowLightAmbientBrightnesses,
+ float[] lowLightAmbientBrightnessesStrong,
float[] lowLightAmbientBiases,
+ float[] lowLightAmbientBiasesStrong,
float lowLightAmbientColorTemperature,
+ float lowLightAmbientColorTemperatureStrong,
float[] highLightAmbientBrightnesses,
+ float[] highLightAmbientBrightnessesStrong,
float[] highLightAmbientBiases,
+ float[] highLightAmbientBiasesStrong,
float highLightAmbientColorTemperature,
+ float highLightAmbientColorTemperatureStrong,
float[] ambientColorTemperatures,
float[] displayColorTemperatures,
float[] strongAmbientColorTemperatures,
@@ -188,7 +213,9 @@
mColorTemperatureFilter = colorTemperatureFilter;
mThrottler = throttler;
mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature;
+ mLowLightAmbientColorTemperatureStrong = lowLightAmbientColorTemperatureStrong;
mHighLightAmbientColorTemperature = highLightAmbientColorTemperature;
+ mHighLightAmbientColorTemperatureStrong = highLightAmbientColorTemperatureStrong;
mAmbientColorTemperature = -1.0f;
mPendingAmbientColorTemperature = -1.0f;
mLastAmbientColorTemperature = -1.0f;
@@ -214,6 +241,23 @@
}
try {
+ mLowLightAmbientBrightnessToBiasSplineStrong = new Spline.LinearSpline(
+ lowLightAmbientBrightnessesStrong, lowLightAmbientBiasesStrong);
+ } catch (Exception e) {
+ Slog.e(TAG, "failed to create strong low light ambient brightness to bias spline.", e);
+ mLowLightAmbientBrightnessToBiasSplineStrong = null;
+ }
+ if (mLowLightAmbientBrightnessToBiasSplineStrong != null) {
+ if (mLowLightAmbientBrightnessToBiasSplineStrong.interpolate(0.0f) != 0.0f
+ || mLowLightAmbientBrightnessToBiasSplineStrong.interpolate(
+ Float.POSITIVE_INFINITY) != 1.0f) {
+ Slog.d(TAG, "invalid strong low light ambient brightness to bias spline, "
+ + "bias must begin at 0.0 and end at 1.0.");
+ mLowLightAmbientBrightnessToBiasSplineStrong = null;
+ }
+ }
+
+ try {
mHighLightAmbientBrightnessToBiasSpline = new Spline.LinearSpline(
highLightAmbientBrightnesses, highLightAmbientBiases);
} catch (Exception e) {
@@ -230,6 +274,23 @@
}
}
+ try {
+ mHighLightAmbientBrightnessToBiasSplineStrong = new Spline.LinearSpline(
+ highLightAmbientBrightnessesStrong, highLightAmbientBiasesStrong);
+ } catch (Exception e) {
+ Slog.e(TAG, "failed to create strong high light ambient brightness to bias spline.", e);
+ mHighLightAmbientBrightnessToBiasSplineStrong = null;
+ }
+ if (mHighLightAmbientBrightnessToBiasSplineStrong != null) {
+ if (mHighLightAmbientBrightnessToBiasSplineStrong.interpolate(0.0f) != 0.0f
+ || mHighLightAmbientBrightnessToBiasSplineStrong.interpolate(
+ Float.POSITIVE_INFINITY) != 1.0f) {
+ Slog.d(TAG, "invalid strong high light ambient brightness to bias spline, "
+ + "bias must begin at 0.0 and end at 1.0.");
+ mHighLightAmbientBrightnessToBiasSplineStrong = null;
+ }
+ }
+
if (mLowLightAmbientBrightnessToBiasSpline != null &&
mHighLightAmbientBrightnessToBiasSpline != null) {
if (lowLightAmbientBrightnesses[lowLightAmbientBrightnesses.length - 1] >
@@ -241,6 +302,18 @@
}
}
+ if (mLowLightAmbientBrightnessToBiasSplineStrong != null
+ && mHighLightAmbientBrightnessToBiasSplineStrong != null) {
+ if (lowLightAmbientBrightnessesStrong[lowLightAmbientBrightnessesStrong.length - 1]
+ > highLightAmbientBrightnessesStrong[0]) {
+ Slog.d(TAG,
+ "invalid strong low light and high light ambient brightness to bias "
+ + "spline combination, defined domains must not intersect.");
+ mLowLightAmbientBrightnessToBiasSplineStrong = null;
+ mHighLightAmbientBrightnessToBiasSplineStrong = null;
+ }
+ }
+
try {
mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
ambientColorTemperatures, displayColorTemperatures);
@@ -365,7 +438,11 @@
mColorTemperatureFilter.dump(writer);
mThrottler.dump(writer);
writer.println(" mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature);
+ writer.println(" mLowLightAmbientColorTemperatureStrong="
+ + mLowLightAmbientColorTemperatureStrong);
writer.println(" mHighLightAmbientColorTemperature=" + mHighLightAmbientColorTemperature);
+ writer.println(" mHighLightAmbientColorTemperatureStrong="
+ + mHighLightAmbientColorTemperatureStrong);
writer.println(" mAmbientColorTemperature=" + mAmbientColorTemperature);
writer.println(" mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature);
writer.println(" mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
@@ -377,8 +454,12 @@
+ mStrongAmbientToDisplayColorTemperatureSpline);
writer.println(" mLowLightAmbientBrightnessToBiasSpline="
+ mLowLightAmbientBrightnessToBiasSpline);
+ writer.println(" mLowLightAmbientBrightnessToBiasSplineStrong="
+ + mLowLightAmbientBrightnessToBiasSplineStrong);
writer.println(" mHighLightAmbientBrightnessToBiasSpline="
+ mHighLightAmbientBrightnessToBiasSpline);
+ writer.println(" mHighLightAmbientBrightnessToBiasSplineStrong="
+ + mHighLightAmbientBrightnessToBiasSplineStrong);
}
@Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
@@ -400,6 +481,17 @@
*/
public void updateAmbientColorTemperature() {
final long time = System.currentTimeMillis();
+ final float lowLightAmbientColorTemperature = mStrongModeEnabled
+ ? mLowLightAmbientColorTemperatureStrong : mLowLightAmbientColorTemperature;
+ final float highLightAmbientColorTemperature = mStrongModeEnabled
+ ? mHighLightAmbientColorTemperatureStrong : mHighLightAmbientColorTemperature;
+ final Spline.LinearSpline lowLightAmbientBrightnessToBiasSpline = mStrongModeEnabled
+ ? mLowLightAmbientBrightnessToBiasSplineStrong
+ : mLowLightAmbientBrightnessToBiasSpline;
+ final Spline.LinearSpline highLightAmbientBrightnessToBiasSpline = mStrongModeEnabled
+ ? mHighLightAmbientBrightnessToBiasSplineStrong
+ : mHighLightAmbientBrightnessToBiasSpline;
+
float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
mLatestAmbientColorTemperature = ambientColorTemperature;
@@ -423,19 +515,19 @@
mLatestAmbientBrightness = ambientBrightness;
if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
- && mLowLightAmbientBrightnessToBiasSpline != null) {
- float bias = mLowLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
+ && lowLightAmbientBrightnessToBiasSpline != null) {
+ float bias = lowLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
ambientColorTemperature =
bias * ambientColorTemperature + (1.0f - bias)
- * mLowLightAmbientColorTemperature;
+ * lowLightAmbientColorTemperature;
mLatestLowLightBias = bias;
}
if (ambientColorTemperature != -1.0f && ambientBrightness != -1.0f
- && mHighLightAmbientBrightnessToBiasSpline != null) {
- float bias = mHighLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
+ && highLightAmbientBrightnessToBiasSpline != null) {
+ float bias = highLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness);
ambientColorTemperature =
(1.0f - bias) * ambientColorTemperature + bias
- * mHighLightAmbientColorTemperature;
+ * highLightAmbientColorTemperature;
mLatestHighLightBias = bias;
}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index 62f813f..39e6b3f 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -70,21 +70,39 @@
final float[] displayWhiteBalanceLowLightAmbientBrightnesses = getFloatArray(resources,
com.android.internal.R.array
.config_displayWhiteBalanceLowLightAmbientBrightnesses);
+ final float[] displayWhiteBalanceLowLightAmbientBrightnessesStrong = getFloatArray(
+ resources, com.android.internal.R.array
+ .config_displayWhiteBalanceLowLightAmbientBrightnessesStrong);
final float[] displayWhiteBalanceLowLightAmbientBiases = getFloatArray(resources,
com.android.internal.R.array
.config_displayWhiteBalanceLowLightAmbientBiases);
+ final float[] displayWhiteBalanceLowLightAmbientBiasesStrong = getFloatArray(resources,
+ com.android.internal.R.array
+ .config_displayWhiteBalanceLowLightAmbientBiasesStrong);
final float lowLightAmbientColorTemperature = getFloat(resources,
com.android.internal.R.dimen
.config_displayWhiteBalanceLowLightAmbientColorTemperature);
+ final float lowLightAmbientColorTemperatureStrong = getFloat(resources,
+ com.android.internal.R.dimen
+ .config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong);
final float[] displayWhiteBalanceHighLightAmbientBrightnesses = getFloatArray(resources,
com.android.internal.R.array
.config_displayWhiteBalanceHighLightAmbientBrightnesses);
+ final float[] displayWhiteBalanceHighLightAmbientBrightnessesStrong = getFloatArray(
+ resources, com.android.internal.R.array
+ .config_displayWhiteBalanceHighLightAmbientBrightnessesStrong);
final float[] displayWhiteBalanceHighLightAmbientBiases = getFloatArray(resources,
com.android.internal.R.array
.config_displayWhiteBalanceHighLightAmbientBiases);
+ final float[] displayWhiteBalanceHighLightAmbientBiasesStrong = getFloatArray(resources,
+ com.android.internal.R.array
+ .config_displayWhiteBalanceHighLightAmbientBiasesStrong);
final float highLightAmbientColorTemperature = getFloat(resources,
com.android.internal.R.dimen
.config_displayWhiteBalanceHighLightAmbientColorTemperature);
+ final float highLightAmbientColorTemperatureStrong = getFloat(resources,
+ com.android.internal.R.dimen
+ .config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong);
final float[] ambientColorTemperatures = getFloatArray(resources,
com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
final float[] displayColorTemperatures = getFloatArray(resources,
@@ -100,9 +118,15 @@
final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController(
brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
throttler, displayWhiteBalanceLowLightAmbientBrightnesses,
- displayWhiteBalanceLowLightAmbientBiases, lowLightAmbientColorTemperature,
+ displayWhiteBalanceLowLightAmbientBrightnessesStrong,
+ displayWhiteBalanceLowLightAmbientBiases,
+ displayWhiteBalanceLowLightAmbientBiasesStrong, lowLightAmbientColorTemperature,
+ lowLightAmbientColorTemperatureStrong,
displayWhiteBalanceHighLightAmbientBrightnesses,
- displayWhiteBalanceHighLightAmbientBiases, highLightAmbientColorTemperature,
+ displayWhiteBalanceHighLightAmbientBrightnessesStrong,
+ displayWhiteBalanceHighLightAmbientBiases,
+ displayWhiteBalanceHighLightAmbientBiasesStrong, highLightAmbientColorTemperature,
+ highLightAmbientColorTemperatureStrong,
ambientColorTemperatures, displayColorTemperatures, strongAmbientColorTemperatures,
strongDisplayColorTemperatures, lightModeAllowed);
brightnessSensor.setCallbacks(controller);
diff --git a/services/core/java/com/android/server/input/FocusEventDebugView.java b/services/core/java/com/android/server/input/FocusEventDebugView.java
index 02e0eb0..4b8fabde 100644
--- a/services/core/java/com/android/server/input/FocusEventDebugView.java
+++ b/services/core/java/com/android/server/input/FocusEventDebugView.java
@@ -24,11 +24,13 @@
import android.annotation.AnyThread;
import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
import android.graphics.Typeface;
-import android.util.Log;
+import android.util.DisplayMetrics;
import android.util.Pair;
import android.util.Slog;
import android.util.TypedValue;
@@ -38,22 +40,29 @@
import android.view.MotionEvent;
import android.view.RoundedCorner;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.animation.AccelerateInterpolator;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
/**
* Displays focus events, such as physical keyboard KeyEvents and non-pointer MotionEvents on
* the screen.
*/
-class FocusEventDebugView extends LinearLayout {
+class FocusEventDebugView extends RelativeLayout {
private static final String TAG = FocusEventDebugView.class.getSimpleName();
@@ -66,9 +75,11 @@
private static final int KEY_VIEW_VERTICAL_PADDING_DP = 8;
private static final int KEY_VIEW_MIN_WIDTH_DP = 32;
private static final int KEY_VIEW_TEXT_SIZE_SP = 12;
+ private static final double ROTATY_GRAPH_HEIGHT_FRACTION = 0.5;
private final InputManagerService mService;
private final int mOuterPadding;
+ private final DisplayMetrics mDm;
// Tracks all keys that are currently pressed/down.
private final Map<Pair<Integer /*deviceId*/, Integer /*scanCode*/>, PressedKeyView>
@@ -80,18 +91,29 @@
private PressedKeyContainer mPressedKeyContainer;
@Nullable
private PressedKeyContainer mPressedModifierContainer;
+ private final Supplier<RotaryInputValueView> mRotaryInputValueViewFactory;
+ @Nullable
+ private RotaryInputValueView mRotaryInputValueView;
+ private final Supplier<RotaryInputGraphView> mRotaryInputGraphViewFactory;
+ @Nullable
+ private RotaryInputGraphView mRotaryInputGraphView;
- FocusEventDebugView(Context c, InputManagerService service) {
+ @VisibleForTesting
+ FocusEventDebugView(Context c, InputManagerService service,
+ Supplier<RotaryInputValueView> rotaryInputValueViewFactory,
+ Supplier<RotaryInputGraphView> rotaryInputGraphViewFactory) {
super(c);
setFocusableInTouchMode(true);
mService = service;
- final var dm = mContext.getResources().getDisplayMetrics();
- mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, dm);
+ mRotaryInputValueViewFactory = rotaryInputValueViewFactory;
+ mRotaryInputGraphViewFactory = rotaryInputGraphViewFactory;
+ mDm = mContext.getResources().getDisplayMetrics();
+ mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, mDm);
+ }
- setOrientation(HORIZONTAL);
- setLayoutDirection(LAYOUT_DIRECTION_RTL);
- setGravity(Gravity.START | Gravity.BOTTOM);
+ FocusEventDebugView(Context c, InputManagerService service) {
+ this(c, service, () -> new RotaryInputValueView(c), () -> new RotaryInputGraphView(c));
}
@Override
@@ -100,13 +122,13 @@
final RoundedCorner bottomLeft =
insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
- if (bottomLeft != null) {
+ if (bottomLeft != null && !insets.isRound()) {
paddingBottom = bottomLeft.getRadius();
}
final RoundedCorner bottomRight =
insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
- if (bottomRight != null) {
+ if (bottomRight != null && !insets.isRound()) {
paddingBottom = Math.max(paddingBottom, bottomRight.getRadius());
}
@@ -151,7 +173,7 @@
}
mPressedKeyContainer = new PressedKeyContainer(mContext);
- mPressedKeyContainer.setOrientation(HORIZONTAL);
+ mPressedKeyContainer.setOrientation(LinearLayout.HORIZONTAL);
mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM);
mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR);
final var scroller = new HorizontalScrollView(mContext);
@@ -160,15 +182,23 @@
scroller.addOnLayoutChangeListener(
(view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT));
scroller.setHorizontalFadingEdgeEnabled(true);
- addView(scroller, new LayoutParams(0, WRAP_CONTENT, 1));
+ LayoutParams scrollerLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ scrollerLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ scrollerLayoutParams.addRule(ALIGN_PARENT_RIGHT);
+ addView(scroller, scrollerLayoutParams);
mPressedModifierContainer = new PressedKeyContainer(mContext);
- mPressedModifierContainer.setOrientation(VERTICAL);
+ mPressedModifierContainer.setOrientation(LinearLayout.VERTICAL);
mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM);
- addView(mPressedModifierContainer, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ LayoutParams modifierLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ modifierLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ modifierLayoutParams.addRule(ALIGN_PARENT_LEFT);
+ modifierLayoutParams.addRule(LEFT_OF, scroller.getId());
+ addView(mPressedModifierContainer, modifierLayoutParams);
}
- private void handleUpdateShowRotaryInput(boolean enabled) {
+ @VisibleForTesting
+ void handleUpdateShowRotaryInput(boolean enabled) {
if (enabled == showRotaryInput()) {
return;
}
@@ -176,10 +206,26 @@
if (!enabled) {
mFocusEventDebugGlobalMonitor.dispose();
mFocusEventDebugGlobalMonitor = null;
+ removeView(mRotaryInputValueView);
+ mRotaryInputValueView = null;
+ removeView(mRotaryInputGraphView);
+ mRotaryInputGraphView = null;
return;
}
mFocusEventDebugGlobalMonitor = new FocusEventDebugGlobalMonitor(this, mService);
+
+ mRotaryInputValueView = mRotaryInputValueViewFactory.get();
+ LayoutParams valueLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ valueLayoutParams.addRule(CENTER_HORIZONTAL);
+ valueLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ addView(mRotaryInputValueView, valueLayoutParams);
+
+ mRotaryInputGraphView = mRotaryInputGraphViewFactory.get();
+ LayoutParams graphLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
+ (int) (ROTATY_GRAPH_HEIGHT_FRACTION * mDm.heightPixels));
+ graphLayoutParams.addRule(CENTER_IN_PARENT);
+ addView(mRotaryInputGraphView, graphLayoutParams);
}
/** Report a key event to the debug view. */
@@ -242,14 +288,15 @@
keyEvent.recycle();
}
- private void handleRotaryInput(MotionEvent motionEvent) {
+ @VisibleForTesting
+ void handleRotaryInput(MotionEvent motionEvent) {
if (!showRotaryInput()) {
return;
}
float scrollAxisValue = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL);
- // TODO(b/286086154): replace log with visualization.
- Log.d(TAG, "ROTARY INPUT: " + String.valueOf(scrollAxisValue));
+ mRotaryInputValueView.updateValue(scrollAxisValue);
+ mRotaryInputGraphView.addValue(scrollAxisValue, motionEvent.getEventTime());
motionEvent.recycle();
}
@@ -308,7 +355,14 @@
/** Determine whether to show rotary input by checking one of the rotary-related objects. */
private boolean showRotaryInput() {
- return mFocusEventDebugGlobalMonitor != null;
+ return mRotaryInputValueView != null;
+ }
+
+ /**
+ * Converts a dimension in scaled pixel units to integer display pixels.
+ */
+ private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) {
+ return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm);
}
private static class PressedKeyView extends TextView {
@@ -419,4 +473,376 @@
invalidate();
}
}
+
+ // TODO(b/286086154): move RotaryInputGraphView and RotaryInputValueView to a subpackage.
+
+ /** Draws the most recent rotary input value and indicates whether the source is active. */
+ @VisibleForTesting
+ static class RotaryInputValueView extends TextView {
+
+ private static final int INACTIVE_TEXT_COLOR = 0xffff00ff;
+ private static final int ACTIVE_TEXT_COLOR = 0xff420f28;
+ private static final int TEXT_SIZE_SP = 8;
+ private static final int SIDE_PADDING_SP = 4;
+ /** Determines how long the active status lasts. */
+ private static final int ACTIVE_STATUS_DURATION = 250 /* milliseconds */;
+ private static final ColorFilter ACTIVE_BACKGROUND_FILTER =
+ new ColorMatrixColorFilter(new float[]{
+ 0, 0, 0, 0, 255, // red
+ 0, 0, 0, 0, 0, // green
+ 0, 0, 0, 0, 255, // blue
+ 0, 0, 0, 0, 200 // alpha
+ });
+
+ private final Runnable mUpdateActivityStatusCallback = () -> updateActivityStatus(false);
+ private final float mScaledVerticalScrollFactor;
+
+ @VisibleForTesting
+ RotaryInputValueView(Context c) {
+ super(c);
+
+ DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
+ mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor();
+
+ setText(getFormattedValue(0));
+ setTextColor(INACTIVE_TEXT_COLOR);
+ setTextSize(applyDimensionSp(TEXT_SIZE_SP, dm));
+ setPaddingRelative(applyDimensionSp(SIDE_PADDING_SP, dm), 0,
+ applyDimensionSp(SIDE_PADDING_SP, dm), 0);
+ setTypeface(null, Typeface.BOLD);
+ setBackgroundResource(R.drawable.focus_event_rotary_input_background);
+ }
+
+ void updateValue(float value) {
+ removeCallbacks(mUpdateActivityStatusCallback);
+
+ setText(getFormattedValue(value * mScaledVerticalScrollFactor));
+
+ updateActivityStatus(true);
+ postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION);
+ }
+
+ @VisibleForTesting
+ void updateActivityStatus(boolean active) {
+ if (active) {
+ setTextColor(ACTIVE_TEXT_COLOR);
+ getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER);
+ } else {
+ setTextColor(INACTIVE_TEXT_COLOR);
+ getBackground().clearColorFilter();
+ }
+ }
+
+ private static String getFormattedValue(float value) {
+ return String.format("%s%.1f", value < 0 ? "-" : "+", Math.abs(value));
+ }
+ }
+
+ /**
+ * Shows a graph with the rotary input values as a function of time.
+ * The graph gets reset if no action is received for a certain amount of time.
+ */
+ @VisibleForTesting
+ static class RotaryInputGraphView extends View {
+
+ private static final int FRAME_COLOR = 0xbf741b47;
+ private static final int FRAME_WIDTH_SP = 2;
+ private static final int FRAME_BORDER_GAP_SP = 10;
+ private static final int FRAME_TEXT_SIZE_SP = 10;
+ private static final int FRAME_TEXT_OFFSET_SP = 2;
+ private static final int GRAPH_COLOR = 0xffff00ff;
+ private static final int GRAPH_LINE_WIDTH_SP = 1;
+ private static final int GRAPH_POINT_RADIUS_SP = 4;
+ private static final long MAX_SHOWN_TIME_INTERVAL = TimeUnit.SECONDS.toMillis(5);
+ private static final float DEFAULT_FRAME_CENTER_POSITION = 0;
+ private static final int MAX_GRAPH_VALUES_SIZE = 400;
+ /** Maximum time between values so that they are considered part of the same gesture. */
+ private static final long MAX_GESTURE_TIME = TimeUnit.SECONDS.toMillis(1);
+
+ private final DisplayMetrics mDm;
+ /**
+ * Distance in position units (amount scrolled in display pixels) from the center to the
+ * top/bottom frame lines.
+ */
+ private final float mFrameCenterToBorderDistance;
+ private final float mScaledVerticalScrollFactor;
+ private final Locale mDefaultLocale;
+ private final Paint mFramePaint = new Paint();
+ private final Paint mFrameTextPaint = new Paint();
+ private final Paint mGraphLinePaint = new Paint();
+ private final Paint mGraphPointPaint = new Paint();
+
+ private final CyclicBuffer mGraphValues = new CyclicBuffer(MAX_GRAPH_VALUES_SIZE);
+ /** Position at which graph values are placed at the center of the graph. */
+ private float mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION;
+
+ @VisibleForTesting
+ RotaryInputGraphView(Context c) {
+ super(c);
+
+ mDm = mContext.getResources().getDisplayMetrics();
+ // This makes the center-to-border distance equivalent to the display height, meaning
+ // that the total height of the graph is equivalent to 2x the display height.
+ mFrameCenterToBorderDistance = mDm.heightPixels;
+ mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor();
+ mDefaultLocale = Locale.getDefault();
+
+ mFramePaint.setColor(FRAME_COLOR);
+ mFramePaint.setStrokeWidth(applyDimensionSp(FRAME_WIDTH_SP, mDm));
+
+ mFrameTextPaint.setColor(GRAPH_COLOR);
+ mFrameTextPaint.setTextSize(applyDimensionSp(FRAME_TEXT_SIZE_SP, mDm));
+
+ mGraphLinePaint.setColor(GRAPH_COLOR);
+ mGraphLinePaint.setStrokeWidth(applyDimensionSp(GRAPH_LINE_WIDTH_SP, mDm));
+ mGraphLinePaint.setStrokeCap(Paint.Cap.ROUND);
+ mGraphLinePaint.setStrokeJoin(Paint.Join.ROUND);
+
+ mGraphPointPaint.setColor(GRAPH_COLOR);
+ mGraphPointPaint.setStrokeWidth(applyDimensionSp(GRAPH_POINT_RADIUS_SP, mDm));
+ mGraphPointPaint.setStrokeCap(Paint.Cap.ROUND);
+ mGraphPointPaint.setStrokeJoin(Paint.Join.ROUND);
+ }
+
+ /**
+ * Reads new scroll axis value and updates the list accordingly. Old positions are
+ * kept at the front (what you would get with getFirst), while the recent positions are
+ * kept at the back (what you would get with getLast). Also updates the frame center
+ * position to handle out-of-bounds cases.
+ */
+ void addValue(float scrollAxisValue, long eventTime) {
+ // Remove values that are too old.
+ while (mGraphValues.getSize() > 0
+ && (eventTime - mGraphValues.getFirst().mTime) > MAX_SHOWN_TIME_INTERVAL) {
+ mGraphValues.removeFirst();
+ }
+
+ // If there are no recent values, reset the frame center.
+ if (mGraphValues.getSize() == 0) {
+ mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION;
+ }
+
+ // Handle new value. We multiply the scroll axis value by the scaled scroll factor to
+ // get the amount of pixels to be scrolled. We also compute the accumulated position
+ // by adding the current value to the last one (if not empty).
+ final float displacement = scrollAxisValue * mScaledVerticalScrollFactor;
+ final float prevPos = (mGraphValues.getSize() == 0 ? 0 : mGraphValues.getLast().mPos);
+ final float pos = prevPos + displacement;
+
+ mGraphValues.add(pos, eventTime);
+
+ // The difference between the distance of the most recent position from the center
+ // frame (pos - mFrameCenterPosition) and the maximum allowed distance from the center
+ // frame (mFrameCenterToBorderDistance).
+ final float verticalDiff = Math.abs(pos - mFrameCenterPosition)
+ - mFrameCenterToBorderDistance;
+ // If needed, translate frame.
+ if (verticalDiff > 0) {
+ final int sign = pos - mFrameCenterPosition < 0 ? -1 : 1;
+ // Here, we update the center frame position by the exact amount needed for us to
+ // stay within the maximum allowed distance from the center frame.
+ mFrameCenterPosition += sign * verticalDiff;
+ }
+
+ // Redraw canvas.
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // Note: vertical coordinates in Canvas go from top to bottom,
+ // that is bottomY > middleY > topY.
+ final int verticalMargin = applyDimensionSp(FRAME_BORDER_GAP_SP, mDm);
+ final int topY = verticalMargin;
+ final int bottomY = getHeight() - verticalMargin;
+ final int middleY = (topY + bottomY) / 2;
+
+ // Note: horizontal coordinates in Canvas go from left to right,
+ // that is rightX > leftX.
+ final int leftX = 0;
+ final int rightX = getWidth();
+
+ // Draw the frame, which includes 3 lines that show the maximum,
+ // minimum and middle positions of the graph.
+ canvas.drawLine(leftX, topY, rightX, topY, mFramePaint);
+ canvas.drawLine(leftX, middleY, rightX, middleY, mFramePaint);
+ canvas.drawLine(leftX, bottomY, rightX, bottomY, mFramePaint);
+
+ // Draw the position that each frame line corresponds to.
+ final int frameTextOffset = applyDimensionSp(FRAME_TEXT_OFFSET_SP, mDm);
+ canvas.drawText(
+ String.format(mDefaultLocale, "%.1f",
+ mFrameCenterPosition + mFrameCenterToBorderDistance),
+ leftX,
+ topY - frameTextOffset, mFrameTextPaint
+ );
+ canvas.drawText(
+ String.format(mDefaultLocale, "%.1f", mFrameCenterPosition),
+ leftX,
+ middleY - frameTextOffset, mFrameTextPaint
+ );
+ canvas.drawText(
+ String.format(mDefaultLocale, "%.1f",
+ mFrameCenterPosition - mFrameCenterToBorderDistance),
+ leftX,
+ bottomY - frameTextOffset, mFrameTextPaint
+ );
+
+ // If there are no graph values to be drawn, stop here.
+ if (mGraphValues.getSize() == 0) {
+ return;
+ }
+
+ // Draw the graph using the times and positions.
+ // We start at the most recent value (which should be drawn at the right) and move
+ // to the older values (which should be drawn to the left of more recent ones). Negative
+ // indices are handled by circuling back to the end of the buffer.
+ final long mostRecentTime = mGraphValues.getLast().mTime;
+ float prevCoordX = 0;
+ float prevCoordY = 0;
+ float prevAge = 0;
+ for (Iterator<GraphValue> iter = mGraphValues.reverseIterator(); iter.hasNext();) {
+ final GraphValue value = iter.next();
+
+ final int age = (int) (mostRecentTime - value.mTime);
+ final float pos = value.mPos;
+
+ // We get the horizontal coordinate in time units from left to right with
+ // (MAX_SHOWN_TIME_INTERVAL - age). Then, we rescale it to match the canvas
+ // units by dividing it by the time-domain length (MAX_SHOWN_TIME_INTERVAL)
+ // and by multiplying it by the canvas length (rightX - leftX). Finally, we
+ // offset the coordinate by adding it to leftX.
+ final float coordX = leftX + ((float) (MAX_SHOWN_TIME_INTERVAL - age)
+ / MAX_SHOWN_TIME_INTERVAL) * (rightX - leftX);
+
+ // We get the vertical coordinate in position units from middle to top with
+ // (pos - mFrameCenterPosition). Then, we rescale it to match the canvas
+ // units by dividing it by half of the position-domain length
+ // (mFrameCenterToBorderDistance) and by multiplying it by half of the canvas
+ // length (middleY - topY). Finally, we offset the coordinate by subtracting
+ // it from middleY (we can't "add" here because the coordinate grows from top
+ // to bottom).
+ final float coordY = middleY - ((pos - mFrameCenterPosition)
+ / mFrameCenterToBorderDistance) * (middleY - topY);
+
+ // Draw a point for this value.
+ canvas.drawPoint(coordX, coordY, mGraphPointPaint);
+
+ // If this value is part of the same gesture as the previous one, draw a line
+ // between them. We ignore the first value (with age = 0).
+ if (age != 0 && (age - prevAge) <= MAX_GESTURE_TIME) {
+ canvas.drawLine(prevCoordX, prevCoordY, coordX, coordY, mGraphLinePaint);
+ }
+
+ prevCoordX = coordX;
+ prevCoordY = coordY;
+ prevAge = age;
+ }
+ }
+
+ @VisibleForTesting
+ float getFrameCenterPosition() {
+ return mFrameCenterPosition;
+ }
+
+ /**
+ * Holds data needed to draw each entry in the graph.
+ */
+ private static class GraphValue {
+ /** Position. */
+ float mPos;
+ /** Time when this value was added. */
+ long mTime;
+
+ GraphValue(float pos, long time) {
+ this.mPos = pos;
+ this.mTime = time;
+ }
+ }
+
+ /**
+ * Holds the graph values as a cyclic buffer. It has a fixed capacity, and it replaces the
+ * old values with new ones to avoid creating new objects.
+ */
+ private static class CyclicBuffer {
+ private final GraphValue[] mValues;
+ private final int mCapacity;
+ private int mSize = 0;
+ private int mLastIndex = 0;
+
+ // The iteration index and counter are here to make it easier to reset them.
+ /** Determines the value currently pointed by the iterator. */
+ private int mIteratorIndex;
+ /** Counts how many values have been iterated through. */
+ private int mIteratorCount;
+
+ /** Used traverse the values in reverse order. */
+ private final Iterator<GraphValue> mReverseIterator = new Iterator<GraphValue>() {
+ @Override
+ public boolean hasNext() {
+ return mIteratorCount <= mSize;
+ }
+
+ @Override
+ public GraphValue next() {
+ // Returns the value currently pointed by the iterator and moves the iterator to
+ // the previous one.
+ mIteratorCount++;
+ return mValues[(mIteratorIndex-- + mCapacity) % mCapacity];
+ }
+ };
+
+ CyclicBuffer(int capacity) {
+ mCapacity = capacity;
+ mValues = new GraphValue[capacity];
+ }
+
+ /**
+ * Add new graph value. If there is an existing object, we replace its data with the
+ * new one. With this, we re-use old objects instead of creating new ones.
+ */
+ void add(float pos, long time) {
+ mLastIndex = (mLastIndex + 1) % mCapacity;
+ if (mValues[mLastIndex] == null) {
+ mValues[mLastIndex] = new GraphValue(pos, time);
+ } else {
+ final GraphValue oldValue = mValues[mLastIndex];
+ oldValue.mPos = pos;
+ oldValue.mTime = time;
+ }
+
+ // If needed, account for new value in the buffer size.
+ if (mSize != mCapacity) {
+ mSize++;
+ }
+ }
+
+ int getSize() {
+ return mSize;
+ }
+
+ GraphValue getFirst() {
+ final int distanceBetweenLastAndFirst = (mCapacity - mSize) + 1;
+ final int firstIndex = (mLastIndex + distanceBetweenLastAndFirst) % mCapacity;
+ return mValues[firstIndex];
+ }
+
+ GraphValue getLast() {
+ return mValues[mLastIndex];
+ }
+
+ void removeFirst() {
+ mSize--;
+ }
+
+ /** Returns an iterator pointing at the last value. */
+ Iterator<GraphValue> reverseIterator() {
+ mIteratorIndex = mLastIndex;
+ mIteratorCount = 1;
+ return mReverseIterator;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2fc4829..8e7baf2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5959,6 +5959,8 @@
mVisibilityStateComputer.dump(pw);
p.println(" mInFullscreenMode=" + mInFullscreenMode);
p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
+ p.println(" ENABLE_HIDE_IME_CAPTION_BAR="
+ + InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR);
p.println(" mSettingsObserver=" + mSettingsObserver);
p.println(" mStylusIds=" + (mStylusIds != null
? Arrays.toString(mStylusIds.toArray()) : ""));
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 567d8ac..f21a9fe 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -201,10 +201,10 @@
this::onProviderEnabledChanged;
private final SettingsHelper.GlobalSettingChangedListener
mBackgroundThrottlePackageWhitelistChangedListener =
- this::onBackgroundThrottlePackageWhitelistChanged;
+ this::onBackgroundThrottlePackageAllowlistChanged;
private final SettingsHelper.UserSettingChangedListener
mLocationPackageBlacklistChangedListener =
- this::onLocationPackageBlacklistChanged;
+ this::onLocationPackageDenylistChanged;
private final LocationPermissionsHelper.LocationPermissionsListener
mLocationPermissionsListener =
new LocationPermissionsHelper.LocationPermissionsListener() {
@@ -407,11 +407,11 @@
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private void onBackgroundThrottlePackageWhitelistChanged() {
+ private void onBackgroundThrottlePackageAllowlistChanged() {
updateRegistrations(registration -> true);
}
- private void onLocationPackageBlacklistChanged(int userId) {
+ private void onLocationPackageDenylistChanged(int userId) {
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
diff --git a/services/core/java/com/android/server/location/injector/SettingsHelper.java b/services/core/java/com/android/server/location/injector/SettingsHelper.java
index 490bfe1..32cbff8 100644
--- a/services/core/java/com/android/server/location/injector/SettingsHelper.java
+++ b/services/core/java/com/android/server/location/injector/SettingsHelper.java
@@ -93,37 +93,37 @@
GlobalSettingChangedListener listener);
/**
- * Check if the given package is blacklisted for location access.
+ * Check if the given package is denylisted for location access.
*/
public abstract boolean isLocationPackageBlacklisted(int userId, String packageName);
/**
- * Add a listener for changes to the location package blacklist. Callbacks occur on an
+ * Add a listener for changes to the location package denylist. Callbacks occur on an
* unspecified thread.
*/
public abstract void addOnLocationPackageBlacklistChangedListener(
UserSettingChangedListener listener);
/**
- * Remove a listener for changes to the location package blacklist.
+ * Remove a listener for changes to the location package denylist.
*/
public abstract void removeOnLocationPackageBlacklistChangedListener(
UserSettingChangedListener listener);
/**
- * Retrieve the background throttle package whitelist.
+ * Retrieve the background throttle package allowlist.
*/
public abstract Set<String> getBackgroundThrottlePackageWhitelist();
/**
- * Add a listener for changes to the background throttle package whitelist. Callbacks occur on
+ * Add a listener for changes to the background throttle package allowlist. Callbacks occur on
* an unspecified thread.
*/
public abstract void addOnBackgroundThrottlePackageWhitelistChangedListener(
GlobalSettingChangedListener listener);
/**
- * Remove a listener for changes to the background throttle package whitelist.
+ * Remove a listener for changes to the background throttle package allowlist.
*/
public abstract void removeOnBackgroundThrottlePackageWhitelistChangedListener(
GlobalSettingChangedListener listener);
@@ -134,14 +134,14 @@
public abstract boolean isGnssMeasurementsFullTrackingEnabled();
/**
- * Add a listener for changes to the background throttle package whitelist. Callbacks occur on
+ * Add a listener for changes to the background throttle package allowlist. Callbacks occur on
* an unspecified thread.
*/
public abstract void addOnGnssMeasurementsFullTrackingEnabledChangedListener(
GlobalSettingChangedListener listener);
/**
- * Remove a listener for changes to the background throttle package whitelist.
+ * Remove a listener for changes to the background throttle package allowlist.
*/
public abstract void removeOnGnssMeasurementsFullTrackingEnabledChangedListener(
GlobalSettingChangedListener listener);
@@ -166,14 +166,14 @@
public abstract PackageTagsList getIgnoreSettingsAllowlist();
/**
- * Add a listener for changes to the ignore settings package whitelist. Callbacks occur on an
+ * Add a listener for changes to the ignore settings package allowlist. Callbacks occur on an
* unspecified thread.
*/
public abstract void addIgnoreSettingsAllowlistChangedListener(
GlobalSettingChangedListener listener);
/**
- * Remove a listener for changes to the ignore settings package whitelist.
+ * Remove a listener for changes to the ignore settings package allowlist.
*/
public abstract void removeIgnoreSettingsAllowlistChangedListener(
GlobalSettingChangedListener listener);
diff --git a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
index 777683e..8bb184c 100644
--- a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
@@ -65,8 +65,8 @@
*/
public class SystemSettingsHelper extends SettingsHelper {
- private static final String LOCATION_PACKAGE_BLACKLIST = "locationPackagePrefixBlacklist";
- private static final String LOCATION_PACKAGE_WHITELIST = "locationPackagePrefixWhitelist";
+ private static final String LOCATION_PACKAGE_DENYLIST = "locationPackagePrefixBlacklist";
+ private static final String LOCATION_PACKAGE_ALLOWLIST = "locationPackagePrefixWhitelist";
private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS =
@@ -93,9 +93,9 @@
mGnssMeasurementFullTracking = new BooleanGlobalSetting(context,
ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, FgThread.getHandler());
mLocationPackageBlacklist = new StringListCachedSecureSetting(context,
- LOCATION_PACKAGE_BLACKLIST, FgThread.getHandler());
+ LOCATION_PACKAGE_DENYLIST, FgThread.getHandler());
mLocationPackageWhitelist = new StringListCachedSecureSetting(context,
- LOCATION_PACKAGE_WHITELIST, FgThread.getHandler());
+ LOCATION_PACKAGE_ALLOWLIST, FgThread.getHandler());
mBackgroundThrottlePackageWhitelist = new StringSetCachedGlobalSetting(context,
LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
() -> SystemConfig.getInstance().getAllowUnthrottledLocation(),
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index e7bd68e..515c7fb 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -735,12 +735,9 @@
}
@Override //Binder call
+ @EnforcePermission(MANAGE_MEDIA_PROJECTION)
public void addCallback(final IMediaProjectionWatcherCallback callback) {
- if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to add "
- + "projection callbacks");
- }
+ addCallback_enforcePermission();
final long token = Binder.clearCallingIdentity();
try {
MediaProjectionManagerService.this.addCallback(callback);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index dc8fcb0..85731651 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -338,8 +338,8 @@
return "App idle state of uid " + uid + ": " + idle;
}
- private static String getAppIdleWlChangedLog(int uid, boolean isWhitelisted) {
- return "App idle whitelist state of uid " + uid + ": " + isWhitelisted;
+ private static String getAppIdleWlChangedLog(int uid, boolean isAllowlisted) {
+ return "App idle whitelist state of uid " + uid + ": " + isAllowlisted;
}
private static String getParoleStateChanged(boolean paroleOn) {
@@ -519,14 +519,14 @@
data.timeStamp = System.currentTimeMillis();
}
- public void appIdleWlChanged(int uid, boolean isWhitelisted) {
+ public void appIdleWlChanged(int uid, boolean isAllowlisted) {
final Data data = getNextSlot();
if (data == null) return;
data.reset();
data.type = EVENT_APP_IDLE_WL_CHANGED;
data.ifield1 = uid;
- data.bfield1 = isWhitelisted;
+ data.bfield1 = isAllowlisted;
data.timeStamp = System.currentTimeMillis();
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index fb9bc01..46e7041 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -365,7 +365,7 @@
private static final String TAG_NETWORK_POLICY = "network-policy";
private static final String TAG_UID_POLICY = "uid-policy";
private static final String TAG_APP_POLICY = "app-policy";
- private static final String TAG_WHITELIST = "whitelist";
+ private static final String TAG_ALLOWLIST = "whitelist";
private static final String TAG_RESTRICT_BACKGROUND = "restrict-background";
private static final String TAG_REVOKED_RESTRICT_BACKGROUND = "revoked-restrict-background";
private static final String TAG_XML_UTILS_INT_ARRAY = "int-array";
@@ -859,7 +859,7 @@
}
@GuardedBy("mUidRulesFirstLock")
- private void updatePowerSaveWhitelistUL() {
+ private void updatePowerSaveAllowlistUL() {
int[] whitelist = mPowerWhitelistManager.getWhitelistedAppIds(/* includingIdle */ false);
mPowerSaveWhitelistExceptIdleAppIds.clear();
for (int uid : whitelist) {
@@ -950,7 +950,7 @@
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
- updatePowerSaveWhitelistUL();
+ updatePowerSaveAllowlistUL();
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mPowerManagerInternal.registerLowPowerModeObserver(
new PowerManagerInternal.LowPowerModeListener() {
@@ -1194,7 +1194,7 @@
public void onReceive(Context context, Intent intent) {
// on background handler thread, and POWER_SAVE_WHITELIST_CHANGED is protected
synchronized (mUidRulesFirstLock) {
- updatePowerSaveWhitelistUL();
+ updatePowerSaveAllowlistUL();
updateRulesForRestrictPowerUL();
updateRulesForAppIdleUL();
}
@@ -2684,7 +2684,7 @@
} else {
Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring");
}
- } else if (TAG_WHITELIST.equals(tag)) {
+ } else if (TAG_ALLOWLIST.equals(tag)) {
insideAllowlist = true;
} else if (TAG_RESTRICT_BACKGROUND.equals(tag) && insideAllowlist) {
final int uid = readIntAttribute(in, ATTR_UID);
@@ -2694,7 +2694,7 @@
mRestrictBackgroundAllowlistRevokedUids.put(uid, true);
}
} else if (type == END_TAG) {
- if (TAG_WHITELIST.equals(tag)) {
+ if (TAG_ALLOWLIST.equals(tag)) {
insideAllowlist = false;
}
@@ -2870,7 +2870,7 @@
out.endTag(null, TAG_POLICY_LIST);
// write all allowlists
- out.startTag(null, TAG_WHITELIST);
+ out.startTag(null, TAG_ALLOWLIST);
// revoked restrict background allowlist
int size = mRestrictBackgroundAllowlistRevokedUids.size();
@@ -2881,7 +2881,7 @@
out.endTag(null, TAG_REVOKED_RESTRICT_BACKGROUND);
}
- out.endTag(null, TAG_WHITELIST);
+ out.endTag(null, TAG_ALLOWLIST);
out.endDocument();
@@ -4395,7 +4395,7 @@
void updateRulesForPowerSaveUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerSaveUL");
try {
- updateRulesForWhitelistedPowerSaveUL(mRestrictPower, FIREWALL_CHAIN_POWERSAVE,
+ updateRulesForAllowlistedPowerSaveUL(mRestrictPower, FIREWALL_CHAIN_POWERSAVE,
mUidFirewallPowerSaveRules);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -4404,14 +4404,14 @@
@GuardedBy("mUidRulesFirstLock")
void updateRuleForRestrictPowerUL(int uid) {
- updateRulesForWhitelistedPowerSaveUL(uid, mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
+ updateRulesForAllowlistedPowerSaveUL(uid, mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
}
@GuardedBy("mUidRulesFirstLock")
void updateRulesForDeviceIdleUL() {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForDeviceIdleUL");
try {
- updateRulesForWhitelistedPowerSaveUL(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE,
+ updateRulesForAllowlistedPowerSaveUL(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE,
mUidFirewallDozableRules);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -4420,26 +4420,26 @@
@GuardedBy("mUidRulesFirstLock")
void updateRuleForDeviceIdleUL(int uid) {
- updateRulesForWhitelistedPowerSaveUL(uid, mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE);
+ updateRulesForAllowlistedPowerSaveUL(uid, mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE);
}
// NOTE: since both fw_dozable and fw_powersave uses the same map
// (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method.
@GuardedBy("mUidRulesFirstLock")
- private void updateRulesForWhitelistedPowerSaveUL(boolean enabled, int chain,
+ private void updateRulesForAllowlistedPowerSaveUL(boolean enabled, int chain,
SparseIntArray rules) {
if (enabled) {
- // Sync the whitelists before enabling the chain. We don't care about the rules if
+ // Sync the allowlists before enabling the chain. We don't care about the rules if
// we are disabling the chain.
final SparseIntArray uidRules = rules;
uidRules.clear();
final List<UserInfo> users = mUserManager.getUsers();
for (int ui = users.size() - 1; ui >= 0; ui--) {
UserInfo user = users.get(ui);
- updateRulesForWhitelistedAppIds(uidRules, mPowerSaveTempWhitelistAppIds, user.id);
- updateRulesForWhitelistedAppIds(uidRules, mPowerSaveWhitelistAppIds, user.id);
+ updateRulesForAllowlistedAppIds(uidRules, mPowerSaveTempWhitelistAppIds, user.id);
+ updateRulesForAllowlistedAppIds(uidRules, mPowerSaveWhitelistAppIds, user.id);
if (chain == FIREWALL_CHAIN_POWERSAVE) {
- updateRulesForWhitelistedAppIds(uidRules,
+ updateRulesForAllowlistedAppIds(uidRules,
mPowerSaveWhitelistExceptIdleAppIds, user.id);
}
}
@@ -4454,7 +4454,7 @@
}
}
- private void updateRulesForWhitelistedAppIds(final SparseIntArray uidRules,
+ private void updateRulesForAllowlistedAppIds(final SparseIntArray uidRules,
final SparseBooleanArray whitelistedAppIds, int userId) {
for (int i = whitelistedAppIds.size() - 1; i >= 0; --i) {
if (whitelistedAppIds.valueAt(i)) {
@@ -4515,12 +4515,12 @@
* allowlisted.
*/
@GuardedBy("mUidRulesFirstLock")
- private boolean isWhitelistedFromPowerSaveUL(int uid, boolean deviceIdleMode) {
+ private boolean isAllowlistedFromPowerSaveUL(int uid, boolean deviceIdleMode) {
final int appId = UserHandle.getAppId(uid);
boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
|| mPowerSaveWhitelistAppIds.get(appId);
if (!deviceIdleMode) {
- isWhitelisted = isWhitelisted || isWhitelistedFromPowerSaveExceptIdleUL(uid);
+ isWhitelisted = isWhitelisted || isAllowlistedFromPowerSaveExceptIdleUL(uid);
}
return isWhitelisted;
}
@@ -4530,7 +4530,7 @@
* (eg: Battery Saver and app idle).
*/
@GuardedBy("mUidRulesFirstLock")
- private boolean isWhitelistedFromPowerSaveExceptIdleUL(int uid) {
+ private boolean isAllowlistedFromPowerSaveExceptIdleUL(int uid) {
final int appId = UserHandle.getAppId(uid);
return mPowerSaveWhitelistExceptIdleAppIds.get(appId);
}
@@ -4546,9 +4546,9 @@
// NOTE: since both fw_dozable and fw_powersave uses the same map
// (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method.
@GuardedBy("mUidRulesFirstLock")
- private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) {
+ private void updateRulesForAllowlistedPowerSaveUL(int uid, boolean enabled, int chain) {
if (enabled) {
- final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid,
+ final boolean isWhitelisted = isAllowlistedFromPowerSaveUL(uid,
chain == FIREWALL_CHAIN_DOZABLE);
if (isWhitelisted || isUidForegroundOnRestrictPowerUL(uid)) {
setUidFirewallRuleUL(chain, uid, FIREWALL_RULE_ALLOW);
@@ -4806,7 +4806,7 @@
}
@GuardedBy("mUidRulesFirstLock")
- private void updateRulesForTempWhitelistChangeUL(int appId) {
+ private void updateRulesForTempAllowlistChangeUL(int appId) {
final List<UserInfo> users = mUserManager.getUsers();
final int numUsers = users.size();
for (int i = 0; i < numUsers; i++) {
@@ -5197,7 +5197,7 @@
final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
final boolean isTop = isUidTop(uid);
- final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
+ final boolean isWhitelisted = isAllowlistedFromPowerSaveUL(uid, mDeviceIdleMode);
final int oldEffectiveBlockedReasons;
final int newEffectiveBlockedReasons;
@@ -5220,9 +5220,9 @@
newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0);
newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0);
newAllowedReasons |= (isTop ? ALLOWED_REASON_TOP : 0);
- newAllowedReasons |= (isWhitelistedFromPowerSaveUL(uid, true)
+ newAllowedReasons |= (isAllowlistedFromPowerSaveUL(uid, true)
? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0);
- newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid)
+ newAllowedReasons |= (isAllowlistedFromPowerSaveExceptIdleUL(uid)
? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0);
newAllowedReasons |= (uidBlockedState.allowedReasons
& ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS);
@@ -6137,7 +6137,7 @@
} else {
mPowerSaveTempWhitelistAppIds.delete(appId);
}
- updateRulesForTempWhitelistChangeUL(appId);
+ updateRulesForTempAllowlistChangeUL(appId);
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 47bb8f0..34c8a0d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -154,13 +154,13 @@
}
switch(type) {
case "app-idle-whitelist":
- return listAppIdleWhitelist();
+ return listAppIdleAllowlist();
case "wifi-networks":
return listWifiNetworks();
case "restrict-background-whitelist":
- return listRestrictBackgroundWhitelist();
+ return listRestrictBackgroundAllowlist();
case "restrict-background-blacklist":
- return listRestrictBackgroundBlacklist();
+ return listRestrictBackgroundDenylist();
}
pw.println("Error: unknown list type '" + type + "'");
return -1;
@@ -175,11 +175,11 @@
}
switch(type) {
case "restrict-background-whitelist":
- return addRestrictBackgroundWhitelist();
+ return addRestrictBackgroundAllowlist();
case "restrict-background-blacklist":
- return addRestrictBackgroundBlacklist();
+ return addRestrictBackgroundDenylist();
case "app-idle-whitelist":
- return addAppIdleWhitelist();
+ return addAppIdleAllowlist();
}
pw.println("Error: unknown add type '" + type + "'");
return -1;
@@ -194,11 +194,11 @@
}
switch(type) {
case "restrict-background-whitelist":
- return removeRestrictBackgroundWhitelist();
+ return removeRestrictBackgroundAllowlist();
case "restrict-background-blacklist":
- return removeRestrictBackgroundBlacklist();
+ return removeRestrictBackgroundDenylist();
case "app-idle-whitelist":
- return removeAppIdleWhitelist();
+ return removeAppIdleAllowlist();
}
pw.println("Error: unknown remove type '" + type + "'");
return -1;
@@ -241,17 +241,17 @@
return 0;
}
- private int listRestrictBackgroundWhitelist() throws RemoteException {
+ private int listRestrictBackgroundAllowlist() throws RemoteException {
return listUidPolicies("Restrict background whitelisted UIDs",
POLICY_ALLOW_METERED_BACKGROUND);
}
- private int listRestrictBackgroundBlacklist() throws RemoteException {
+ private int listRestrictBackgroundDenylist() throws RemoteException {
return listUidPolicies("Restrict background blacklisted UIDs",
POLICY_REJECT_METERED_BACKGROUND);
}
- private int listAppIdleWhitelist() throws RemoteException {
+ private int listAppIdleAllowlist() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final int[] uids = mInterface.getAppIdleWhitelist();
return listUidList("App Idle whitelisted UIDs", uids);
@@ -311,23 +311,23 @@
return 0;
}
- private int addRestrictBackgroundWhitelist() throws RemoteException {
+ private int addRestrictBackgroundAllowlist() throws RemoteException {
return setUidPolicy(POLICY_ALLOW_METERED_BACKGROUND);
}
- private int removeRestrictBackgroundWhitelist() throws RemoteException {
+ private int removeRestrictBackgroundAllowlist() throws RemoteException {
return resetUidPolicy("not whitelisted", POLICY_ALLOW_METERED_BACKGROUND);
}
- private int addRestrictBackgroundBlacklist() throws RemoteException {
+ private int addRestrictBackgroundDenylist() throws RemoteException {
return setUidPolicy(POLICY_REJECT_METERED_BACKGROUND);
}
- private int removeRestrictBackgroundBlacklist() throws RemoteException {
+ private int removeRestrictBackgroundDenylist() throws RemoteException {
return resetUidPolicy("not blacklisted", POLICY_REJECT_METERED_BACKGROUND);
}
- private int setAppIdleWhitelist(boolean isWhitelisted) {
+ private int setAppIdleAllowlist(boolean isWhitelisted) {
final int uid = getUidFromNextArg();
if (uid < 0) {
return uid;
@@ -336,12 +336,12 @@
return 0;
}
- private int addAppIdleWhitelist() throws RemoteException {
- return setAppIdleWhitelist(true);
+ private int addAppIdleAllowlist() throws RemoteException {
+ return setAppIdleAllowlist(true);
}
- private int removeAppIdleWhitelist() throws RemoteException {
- return setAppIdleWhitelist(false);
+ private int removeAppIdleAllowlist() throws RemoteException {
+ return setAppIdleAllowlist(false);
}
private int listWifiNetworks() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e782ea9..009cc3b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1239,7 +1239,9 @@
mNotificationRecordLogger.log(
NotificationRecordLogger.NotificationEvent.fromAction(actionIndex,
generatedByAssistant, action.isContextual()), r);
- EventLogTags.writeNotificationActionClicked(key, actionIndex,
+ EventLogTags.writeNotificationActionClicked(key,
+ action.actionIntent.getTarget().toString(),
+ action.actionIntent.getIntent().toString(), actionIndex,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
nv.rank, nv.count);
nv.recycle();
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index b2d3fca..100c638 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,8 +25,6 @@
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.IActivityManager;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -48,6 +46,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.provider.Settings;
@@ -98,8 +97,7 @@
// the period after which a notification is updated where it can make sound
private static final int MAX_SOUND_DELAY_MS = 2000;
private final StatusBarNotification sbn;
- IActivityManager mAm;
- UriGrantsManagerInternal mUgmInternal;
+ private final UriGrantsManagerInternal mUgmInternal;
final int mTargetSdkVersion;
final int mOriginalFlags;
private final Context mContext;
@@ -223,7 +221,6 @@
this.sbn = sbn;
mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class)
.getPackageTargetSdkVersion(sbn.getPackageName());
- mAm = ActivityManager.getService();
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mOriginalFlags = sbn.getNotification().flags;
mRankingTimeMs = calculateRankingTimeMs(0L);
@@ -1387,18 +1384,27 @@
* Collect all {@link Uri} that should have permission granted to whoever
* will be rendering it.
*/
- protected void calculateGrantableUris() {
- final Notification notification = getNotification();
- notification.visitUris((uri) -> {
- visitGrantableUri(uri, false, false);
- });
+ private void calculateGrantableUris() {
+ Trace.beginSection("NotificationRecord.calculateGrantableUris");
+ try {
+ // We can't grant URI permissions from system.
+ final int sourceUid = getSbn().getUid();
+ if (sourceUid == android.os.Process.SYSTEM_UID) return;
- if (notification.getChannelId() != null) {
- NotificationChannel channel = getChannel();
- if (channel != null) {
- visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
- & NotificationChannel.USER_LOCKED_SOUND) != 0, true);
+ final Notification notification = getNotification();
+ notification.visitUris((uri) -> {
+ visitGrantableUri(uri, false, false);
+ });
+
+ if (notification.getChannelId() != null) {
+ NotificationChannel channel = getChannel();
+ if (channel != null) {
+ visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
+ & NotificationChannel.USER_LOCKED_SOUND) != 0, true);
+ }
}
+ } finally {
+ Trace.endSection();
}
}
@@ -1413,13 +1419,14 @@
private void visitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) {
if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
- // We can't grant Uri permissions from system
- final int sourceUid = getSbn().getUid();
- if (sourceUid == android.os.Process.SYSTEM_UID) return;
+ if (mGrantableUris != null && mGrantableUris.contains(uri)) {
+ return; // already verified this URI
+ }
+ final int sourceUid = getSbn().getUid();
final long ident = Binder.clearCallingIdentity();
try {
- // This will throw SecurityException if caller can't grant
+ // This will throw a SecurityException if the caller can't grant.
mUgmInternal.checkGrantUriPermission(sourceUid, null,
ContentProvider.getUriWithoutUserId(uri),
Intent.FLAG_GRANT_READ_URI_PERMISSION,
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 6e75605..f987629 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -487,6 +487,8 @@
boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId);
+ boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId);
+
boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, @UserIdInt int userId);
@NonNull
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 7d878ec..1cfc7d7 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -4925,8 +4925,8 @@
}
}
- @Override
- public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId) {
+ private PackageUserStateInternal getUserStageOrDefaultForUser(@NonNull String packageName,
+ int userId) {
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
false /* checkShell */, "isPackageSuspendedForUser for user " + userId);
@@ -4934,7 +4934,17 @@
if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
throw new IllegalArgumentException("Unknown target package: " + packageName);
}
- return ps.getUserStateOrDefault(userId).isSuspended();
+ return ps.getUserStateOrDefault(userId);
+ }
+
+ @Override
+ public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId) {
+ return getUserStageOrDefaultForUser(packageName, userId).isSuspended();
+ }
+
+ @Override
+ public boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId) {
+ return getUserStageOrDefaultForUser(packageName, userId).isQuarantined();
}
@Override
diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java
index fd47846..76203ac 100644
--- a/services/core/java/com/android/server/pm/IPackageManagerBase.java
+++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java
@@ -955,6 +955,13 @@
@Override
@Deprecated
+ public final boolean isPackageQuarantinedForUser(@NonNull String packageName,
+ @UserIdInt int userId) {
+ return snapshot().isPackageQuarantinedForUser(packageName, userId);
+ }
+
+ @Override
+ @Deprecated
public final boolean isSafeMode() {
// allow instant applications
return mService.getSafeMode();
diff --git a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
index b4c40e6..c477751 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
@@ -280,6 +280,11 @@
} catch (NoSuchElementException ignore) { }
}
mRemoteInstance = null;
+
+ try {
+ mContext.unbindService(mServiceConnection);
+ } catch (Exception ignored) {
+ }
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 303f321..6270655 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -68,6 +68,7 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.Context;
@@ -312,6 +313,19 @@
private static final long SILENT_INSTALL_ALLOWED = 265131695L;
/**
+ * The system supports pre-approval and update ownership features from
+ * {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE API 34}. The change id is used to make sure
+ * the system includes the fix of pre-approval with update ownership case. When checking the
+ * change id, if it is disabled, it means the build includes the fix. The more detail is on
+ * b/293644536.
+ * See {@link PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean)} and
+ * {@link #requestUserPreapproval(PreapprovalDetails, IntentSender)} for more details.
+ */
+ @Disabled
+ @ChangeId
+ private static final long PRE_APPROVAL_WITH_UPDATE_OWNERSHIP_FIX = 293644536L;
+
+ /**
* The default value of {@link #mValidatedTargetSdk} is {@link Integer#MAX_VALUE}. If {@link
* #mValidatedTargetSdk} is compared with {@link Build.VERSION_CODES#S} before getting the
* target sdk version from a validated apk in {@link #validateApkInstallLocked()}, the compared
@@ -927,16 +941,27 @@
if (mPermissionsManuallyAccepted) {
return USER_ACTION_NOT_NEEDED;
}
- packageName = mPackageName;
+ // For pre-pappvoal case, the mPackageName would be null.
+ if (mPackageName != null) {
+ packageName = mPackageName;
+ } else if (mPreapprovalRequested.get() && mPreapprovalDetails != null) {
+ packageName = mPreapprovalDetails.getPackageName();
+ } else {
+ packageName = null;
+ }
hasDeviceAdminReceiver = mHasDeviceAdminReceiver;
}
- final boolean forcePermissionPrompt =
+ // For the below cases, force user action prompt
+ // 1. installFlags includes INSTALL_FORCE_PERMISSION_PROMPT
+ // 2. params.requireUserAction is USER_ACTION_REQUIRED
+ final boolean forceUserActionPrompt =
(params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0
|| params.requireUserAction == SessionParams.USER_ACTION_REQUIRED;
- if (forcePermissionPrompt) {
- return USER_ACTION_REQUIRED;
- }
+ final int userActionNotTypicallyNeededResponse = forceUserActionPrompt
+ ? USER_ACTION_REQUIRED
+ : USER_ACTION_NOT_NEEDED;
+
// It is safe to access mInstallerUid and mInstallSource without lock
// because they are immutable after sealing.
final Computer snapshot = mPm.snapshotComputer();
@@ -990,7 +1015,7 @@
|| isInstallerDeviceOwnerOrAffiliatedProfileOwner();
if (noUserActionNecessary) {
- return USER_ACTION_NOT_NEEDED;
+ return userActionNotTypicallyNeededResponse;
}
if (isUpdateOwnershipEnforcementEnabled
@@ -1003,7 +1028,7 @@
}
if (isPermissionGranted) {
- return USER_ACTION_NOT_NEEDED;
+ return userActionNotTypicallyNeededResponse;
}
if (snapshot.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid,
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index 6efd067..651845e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -755,9 +755,7 @@
@Override
public boolean isPackageQuarantined(@NonNull String packageName,
@UserIdInt int userId) {
- final PackageStateInternal packageState = getPackageStateInternal(packageName);
- return (packageState == null) ? false
- : packageState.getUserStateOrDefault(userId).isQuarantined();
+ return snapshot().isPackageQuarantinedForUser(packageName, userId);
}
@NonNull
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index ceae1fe..8bdbe04 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -226,6 +226,8 @@
return runPath();
case "dump":
return runDump();
+ case "dump-package":
+ return runDumpPackage();
case "list":
return runList();
case "gc":
@@ -978,6 +980,7 @@
boolean listInstaller = false;
boolean showUid = false;
boolean showVersionCode = false;
+ boolean listQuarantinedOnly = false;
boolean listApexOnly = false;
boolean showStopped = false;
int uid = -1;
@@ -1008,6 +1011,9 @@
case "-s":
listSystem = true;
break;
+ case "-q":
+ listQuarantinedOnly = true;
+ break;
case "-U":
showUid = true;
break;
@@ -1093,6 +1099,10 @@
|| (listApexOnly && !isApex)) {
continue;
}
+ if (listQuarantinedOnly && !mInterface.isPackageQuarantinedForUser(info.packageName,
+ translatedUserId)) {
+ continue;
+ }
String name = null;
if (showSdks) {
@@ -3598,6 +3608,23 @@
return 0;
}
+ private int runDumpPackage() {
+ String pkg = getNextArg();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified");
+ return 1;
+ }
+ try {
+ ((IBinder) mInterface).dump(getOutFileDescriptor(), new String[]{pkg});
+ } catch (Throwable e) {
+ PrintWriter pw = getErrPrintWriter();
+ pw.println("Failure dumping service:");
+ e.printStackTrace(pw);
+ pw.flush();
+ }
+ return 0;
+ }
+
private int runSetHarmfulAppWarning() throws RemoteException {
int userId = UserHandle.USER_CURRENT;
@@ -4282,6 +4309,9 @@
pw.println(" dump PACKAGE");
pw.println(" Print various system state associated with the given PACKAGE.");
pw.println("");
+ pw.println(" dump-package PACKAGE");
+ pw.println(" Print package manager state associated with the given PACKAGE.");
+ pw.println("");
pw.println(" has-feature FEATURE_NAME [version]");
pw.println(" Prints true and returns exit status 0 when system has a FEATURE_NAME,");
pw.println(" otherwise prints false and returns exit status 1");
@@ -4299,7 +4329,7 @@
pw.println(" Options:");
pw.println(" -v: shows the location of the library in the device's filesystem");
pw.println("");
- pw.println(" list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U] ");
+ pw.println(" list packages [-f] [-d] [-e] [-s] [-q] [-3] [-i] [-l] [-u] [-U] ");
pw.println(" [--show-versioncode] [--apex-only] [--factory-only]");
pw.println(" [--uid UID] [--user USER_ID] [FILTER]");
pw.println(" Prints all packages; optionally only those whose name contains");
@@ -4309,6 +4339,7 @@
pw.println(" -d: filter to only show disabled packages");
pw.println(" -e: filter to only show enabled packages");
pw.println(" -s: filter to only show system packages");
+ pw.println(" -q: filter to only show quarantined packages");
pw.println(" -3: filter to only show third party packages");
pw.println(" -i: see the installer for the packages");
pw.println(" -l: ignored (used for compatibility with older releases)");
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 80d6ebb..2d58fe5 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -34,11 +34,13 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicLong;
-import java.util.stream.Stream;
/**
* Metrics class for reporting stats to logging infrastructures like statsd
@@ -155,10 +157,27 @@
private long getApksSize(File apkDir) {
// TODO(b/249294752): also count apk sizes for failed installs
final AtomicLong apksSize = new AtomicLong();
- try (Stream<Path> walkStream = Files.walk(apkDir.toPath())) {
- walkStream.filter(p -> p.toFile().isFile()
- && ApkLiteParseUtils.isApkFile(p.toFile())).forEach(
- f -> apksSize.addAndGet(f.toFile().length()));
+ try {
+ Files.walkFileTree(apkDir.toPath(), new SimpleFileVisitor<>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
+ throws IOException {
+ if (dir.equals(apkDir.toPath())) {
+ return FileVisitResult.CONTINUE;
+ } else {
+ return FileVisitResult.SKIP_SUBTREE;
+ }
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+ throws IOException {
+ if (file.toFile().isFile() && ApkLiteParseUtils.isApkFile(file.toFile())) {
+ apksSize.addAndGet(file.toFile().length());
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
} catch (IOException e) {
// ignore
}
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 7ed10a4..160b2aa 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -516,6 +516,13 @@
@PackageManager.ResolveInfoFlagsBits long flags, int userId) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
+
+ // Only if the service query is coming from the system process,
+ // it should be allowed to match quarantined components
+ if (callingUid != Process.SYSTEM_UID) {
+ flags |= PackageManager.FILTER_OUT_QUARANTINED_COMPONENTS;
+ }
+
final String instantAppPkgName = computer.getInstantAppPackageName(callingUid);
flags = computer.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 385dfcb8..f2797eb 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4907,6 +4907,11 @@
USER_OPERATION_ERROR_UNKNOWN);
}
}
+ if (isMainUser && getMainUserIdUnchecked() != UserHandle.USER_NULL) {
+ throwCheckedUserOperationException(
+ "Cannot add user with FLAG_MAIN as main user already exists.",
+ UserManager.USER_OPERATION_ERROR_MAX_USERS);
+ }
if (!preCreate && !canAddMoreUsersOfType(userTypeDetails)) {
throwCheckedUserOperationException(
"Cannot add more users of type " + userType
diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
index 153238fa..4e08106 100644
--- a/services/core/java/com/android/server/pm/pkg/SuspendParams.java
+++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
@@ -24,6 +24,7 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.pm.Flags;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -141,7 +142,8 @@
PersistableBundle readAppExtras = null;
PersistableBundle readLauncherExtras = null;
- final boolean quarantined = in.getAttributeBoolean(null, ATTR_QUARANTINED, false);
+ final boolean quarantined = in.getAttributeBoolean(null, ATTR_QUARANTINED, false)
+ && Flags.quarantinedEnabled();
final int currentDepth = in.getDepth();
int type;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index e2cb87e..dc022f7 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -3055,7 +3055,7 @@
*
* TODO(b/155513789): Remove this in favor of collecting certificates during the original parse
* call if requested. Leaving this as an optional method for the caller means we have to
- * construct a dummy ParseInput.
+ * construct a placeholder ParseInput.
*/
@CheckResult
public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index faf132e..2f68021 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -208,7 +208,6 @@
import com.android.internal.policy.PhoneWindow;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.ExtconStateObserver;
@@ -622,9 +621,16 @@
private boolean mAllowTheaterModeWakeFromLidSwitch;
private boolean mAllowTheaterModeWakeFromWakeGesture;
- // Whether to support long press from power button in non-interactive mode
+ // If true, the power button long press behavior will be invoked even if the default display is
+ // non-interactive. If false, the power button long press behavior will be skipped if the
+ // default display is non-interactive.
private boolean mSupportLongPressPowerWhenNonInteractive;
+ // If true, the power button short press behavior will be always invoked as long as the default
+ // display is on, even if the display is not interactive. If false, the power button short press
+ // behavior will be skipped if the default display is non-interactive.
+ private boolean mSupportShortPressPowerWhenDefaultDisplayOn;
+
// Whether to go to sleep entering theater mode from power button
private boolean mGoToSleepOnButtonPressTheaterMode;
@@ -1041,7 +1047,7 @@
}
}
- private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {
+ private void powerPress(long eventTime, int count) {
// SideFPS still needs to know about suppressed power buttons, in case it needs to block
// an auth attempt.
if (count == 1) {
@@ -1055,9 +1061,16 @@
final boolean interactive = mDefaultDisplayPolicy.isAwake();
- Slog.d(TAG, "powerPress: eventTime=" + eventTime + " interactive=" + interactive
- + " count=" + count + " beganFromNonInteractive=" + beganFromNonInteractive
- + " mShortPressOnPowerBehavior=" + mShortPressOnPowerBehavior);
+ Slog.d(
+ TAG,
+ "powerPress: eventTime="
+ + eventTime
+ + " interactive="
+ + interactive
+ + " count="
+ + count
+ + " mShortPressOnPowerBehavior="
+ + mShortPressOnPowerBehavior);
if (count == 2) {
powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
@@ -1065,12 +1078,7 @@
powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
} else if (count > 3 && count <= getMaxMultiPressPowerCount()) {
Slog.d(TAG, "No behavior defined for power press count " + count);
- } else if (count == 1 && interactive && !beganFromNonInteractive) {
- if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) {
- Slog.i(TAG, "Suppressing power key because the user is interacting with the "
- + "fingerprint sensor");
- return;
- }
+ } else if (count == 1 && shouldHandleShortPressPowerAction(interactive, eventTime)) {
switch (mShortPressOnPowerBehavior) {
case SHORT_PRESS_POWER_NOTHING:
break;
@@ -1118,6 +1126,44 @@
}
}
+ private boolean shouldHandleShortPressPowerAction(boolean interactive, long eventTime) {
+ if (mSupportShortPressPowerWhenDefaultDisplayOn) {
+ final boolean defaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
+ final boolean beganFromDefaultDisplayOn =
+ mSingleKeyGestureDetector.beganFromDefaultDisplayOn();
+ if (!defaultDisplayOn || !beganFromDefaultDisplayOn) {
+ Slog.v(
+ TAG,
+ "Ignoring short press of power button because the default display is not"
+ + " on. defaultDisplayOn="
+ + defaultDisplayOn
+ + ", beganFromDefaultDisplayOn="
+ + beganFromDefaultDisplayOn);
+ return false;
+ }
+ return true;
+ }
+ final boolean beganFromNonInteractive = mSingleKeyGestureDetector.beganFromNonInteractive();
+ if (!interactive || beganFromNonInteractive) {
+ Slog.v(
+ TAG,
+ "Ignoring short press of power button because the device is not interactive."
+ + " interactive="
+ + interactive
+ + ", beganFromNonInteractive="
+ + beganFromNonInteractive);
+ return false;
+ }
+ if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) {
+ Slog.i(
+ TAG,
+ "Suppressing power key because the user is interacting with the "
+ + "fingerprint sensor");
+ return false;
+ }
+ return true;
+ }
+
/**
* Attempt to dream from a power button press.
*
@@ -2231,6 +2277,11 @@
mSupportLongPressPowerWhenNonInteractive = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_supportLongPressPowerWhenNonInteractive);
+ mSupportShortPressPowerWhenDefaultDisplayOn =
+ mContext.getResources()
+ .getBoolean(
+ com.android.internal.R.bool
+ .config_supportShortPressPowerWhenDefaultDisplayOn);
mLongPressOnBackBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnBackBehavior);
@@ -2518,8 +2569,7 @@
@Override
void onPress(long downTime) {
- powerPress(downTime, 1 /*count*/,
- mSingleKeyGestureDetector.beganFromNonInteractive());
+ powerPress(downTime, 1 /*count*/);
}
@Override
@@ -2550,7 +2600,7 @@
@Override
void onMultiPress(long downTime, int count) {
- powerPress(downTime, count, mSingleKeyGestureDetector.beganFromNonInteractive());
+ powerPress(downTime, count);
}
}
@@ -4323,10 +4373,11 @@
// This could prevent some wrong state in multi-displays environment,
// the default display may turned off but interactive is true.
- final boolean isDefaultDisplayOn = mDefaultDisplayPolicy.isAwake();
- final boolean interactiveAndOn = interactive && isDefaultDisplayOn;
+ final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
+ final boolean isDefaultDisplayAwake = mDefaultDisplayPolicy.isAwake();
+ final boolean interactiveAndAwake = interactive && isDefaultDisplayAwake;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
- handleKeyGesture(event, interactiveAndOn);
+ handleKeyGesture(event, interactiveAndAwake, isDefaultDisplayOn);
}
// Enable haptics if down and virtual key without multiple repetitions. If this is a hard
@@ -4479,7 +4530,7 @@
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
- interceptPowerKeyDown(event, interactiveAndOn);
+ interceptPowerKeyDown(event, interactiveAndAwake);
} else {
interceptPowerKeyUp(event, canceled);
}
@@ -4695,7 +4746,7 @@
return result;
}
- private void handleKeyGesture(KeyEvent event, boolean interactive) {
+ private void handleKeyGesture(KeyEvent event, boolean interactive, boolean defaultDisplayOn) {
if (mKeyCombinationManager.interceptKey(event, interactive)) {
// handled by combo keys manager.
mSingleKeyGestureDetector.reset();
@@ -4711,7 +4762,7 @@
}
}
- mSingleKeyGestureDetector.interceptKey(event, interactive);
+ mSingleKeyGestureDetector.interceptKey(event, interactive, defaultDisplayOn);
}
// The camera gesture will be detected by GestureLauncherService.
@@ -6167,6 +6218,9 @@
pw.print("mTriplePressOnPowerBehavior=");
pw.println(multiPressOnPowerBehaviorToString(mTriplePressOnPowerBehavior));
pw.print(prefix);
+ pw.print("mSupportShortPressPowerWhenDefaultDisplayOn=");
+ pw.println(mSupportShortPressPowerWhenDefaultDisplayOn);
+ pw.print(prefix);
pw.print("mPowerVolUpBehavior=");
pw.println(powerVolumeUpBehaviorToString(mPowerVolUpBehavior));
pw.print(prefix);
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index b999bbb3..5fc0637 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -43,6 +43,7 @@
private int mKeyPressCounter;
private boolean mBeganFromNonInteractive = false;
+ private boolean mBeganFromDefaultDisplayOn = false;
private final ArrayList<SingleKeyRule> mRules = new ArrayList();
private SingleKeyRule mActiveRule = null;
@@ -194,11 +195,12 @@
mRules.remove(rule);
}
- void interceptKey(KeyEvent event, boolean interactive) {
+ void interceptKey(KeyEvent event, boolean interactive, boolean defaultDisplayOn) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
- // Store the non interactive state when first down.
+ // Store the non interactive state and display on state when first down.
if (mDownKeyCode == KeyEvent.KEYCODE_UNKNOWN || mDownKeyCode != event.getKeyCode()) {
mBeganFromNonInteractive = !interactive;
+ mBeganFromDefaultDisplayOn = defaultDisplayOn;
}
interceptKeyDown(event);
} else {
@@ -388,6 +390,10 @@
return mBeganFromNonInteractive;
}
+ boolean beganFromDefaultDisplayOn() {
+ return mBeganFromDefaultDisplayOn;
+ }
+
void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "SingleKey rules:");
for (SingleKeyRule rule : mRules) {
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 33bed3d..577468b 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -56,6 +56,7 @@
public final class HintManagerService extends SystemService {
private static final String TAG = "HintManagerService";
private static final boolean DEBUG = false;
+ private static final int MAX_HINT_SESSION_COUNT_PER_UID = 20;
@VisibleForTesting final long mHintSessionPreferredRate;
// Multi-level map storing all active AppHintSessions.
@@ -367,6 +368,23 @@
+ " not be empty.");
final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID) {
+ int sessionCount = 0;
+ synchronized (mLock) {
+ ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap =
+ mActiveSessions.get(callingUid);
+ if (tokenMap != null) {
+ for (ArraySet<AppHintSession> arr : tokenMap.values()) {
+ sessionCount += arr.size();
+ }
+ }
+ }
+ if (sessionCount >= MAX_HINT_SESSION_COUNT_PER_UID) {
+ throw new IllegalStateException(
+ "Max session count limit reached: " + sessionCount);
+ }
+ }
+
final int callingTgid = Process.getThreadGroupLeader(Binder.getCallingPid());
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
new file mode 100644
index 0000000..6cc9d0a
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
@@ -0,0 +1,147 @@
+/*
+ * 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.power.stats;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.DurationMillisLong;
+import android.os.UserHandle;
+import android.text.format.DateFormat;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.os.PowerStats;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This class represents aggregated power stats for a variety of power components (CPU, WiFi,
+ * etc) covering a specific period of power usage history.
+ */
+class AggregatedPowerStats {
+ private final PowerComponentAggregatedPowerStats[] mPowerComponentStats;
+
+ @CurrentTimeMillisLong
+ private long mStartTime;
+
+ @DurationMillisLong
+ private long mDurationMs;
+
+ AggregatedPowerStats(PowerComponentAggregatedPowerStats... powerComponentAggregatedPowerStats) {
+ this.mPowerComponentStats = powerComponentAggregatedPowerStats;
+ }
+
+ void setStartTime(@CurrentTimeMillisLong long startTime) {
+ mStartTime = startTime;
+ }
+
+ @CurrentTimeMillisLong
+ public long getStartTime() {
+ return mStartTime;
+ }
+
+ void setDuration(long durationMs) {
+ mDurationMs = durationMs;
+ }
+
+ @DurationMillisLong
+ public long getDuration() {
+ return mDurationMs;
+ }
+
+ PowerComponentAggregatedPowerStats getPowerComponentStats(int powerComponentId) {
+ for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ if (stats.powerComponentId == powerComponentId) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ void setDeviceState(@PowerStatsAggregator.TrackedState int stateId, int state, long time) {
+ for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ stats.setState(stateId, state, time);
+ }
+ }
+
+ void setUidState(int uid, @PowerStatsAggregator.TrackedState int stateId, int state,
+ long time) {
+ for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ stats.setUidState(uid, stateId, state, time);
+ }
+ }
+
+ boolean isCompatible(PowerStats powerStats) {
+ int powerComponentId = powerStats.descriptor.powerComponentId;
+ for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ if (stats.powerComponentId == powerComponentId && !stats.isCompatible(powerStats)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void addPowerStats(PowerStats powerStats, long time) {
+ int powerComponentId = powerStats.descriptor.powerComponentId;
+ for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ if (stats.powerComponentId == powerComponentId) {
+ stats.addPowerStats(powerStats, time);
+ }
+ }
+ }
+
+ void reset() {
+ mStartTime = 0;
+ mDurationMs = 0;
+ for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ stats.reset();
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ ipw.print("Start time: ");
+ ipw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", mStartTime));
+ ipw.print(" duration: ");
+ ipw.print(mDurationMs);
+ ipw.println();
+
+ ipw.println("Device");
+ ipw.increaseIndent();
+ for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ stats.dumpDevice(ipw);
+ }
+ ipw.decreaseIndent();
+
+ Set<Integer> uids = new HashSet<>();
+ for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ stats.collectUids(uids);
+ }
+
+ Integer[] allUids = uids.toArray(new Integer[uids.size()]);
+ Arrays.sort(allUids);
+ for (int uid : allUids) {
+ ipw.println(UserHandle.formatUid(uid));
+ ipw.increaseIndent();
+ for (PowerComponentAggregatedPowerStats stats : mPowerComponentStats) {
+ stats.dumpUid(ipw, uid);
+ }
+ ipw.decreaseIndent();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index f4b2f52..f9d57e4 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -342,20 +342,24 @@
@Override
public Future<?> scheduleCleanupDueToRemovedUser(int userId) {
synchronized (BatteryExternalStatsWorker.this) {
- // Initial quick clean-up after a user removal
- mExecutorService.schedule(() -> {
- synchronized (mStats) {
- mStats.clearRemovedUserUidsLocked(userId);
- }
- }, UID_QUICK_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS, TimeUnit.MILLISECONDS);
+ try {
+ // Initial quick clean-up after a user removal
+ mExecutorService.schedule(() -> {
+ synchronized (mStats) {
+ mStats.clearRemovedUserUidsLocked(userId);
+ }
+ }, UID_QUICK_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS, TimeUnit.MILLISECONDS);
- // Final clean-up after a user removal, to take care of UIDs that were running longer
- // than expected
- return mExecutorService.schedule(() -> {
- synchronized (mStats) {
- mStats.clearRemovedUserUidsLocked(userId);
- }
- }, UID_FINAL_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS, TimeUnit.MILLISECONDS);
+ // Final clean-up after a user removal, to take care of UIDs that were running
+ // longer than expected
+ return mExecutorService.schedule(() -> {
+ synchronized (mStats) {
+ mStats.clearRemovedUserUidsLocked(userId);
+ }
+ }, UID_FINAL_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (RejectedExecutionException e) {
+ return CompletableFuture.failedFuture(e);
+ }
}
}
@@ -401,7 +405,11 @@
scheduleSyncLocked("write", UPDATE_ALL);
// Since we use a single threaded executor, we can assume the next scheduled task's
// Future finishes after the sync.
- return mExecutorService.submit(mWriteTask);
+ try {
+ return mExecutorService.submit(mWriteTask);
+ } catch (RejectedExecutionException e) {
+ return CompletableFuture.failedFuture(e);
+ }
}
/**
@@ -429,7 +437,11 @@
if (mCurrentFuture == null) {
mUpdateFlags = flags;
mCurrentReason = reason;
- mCurrentFuture = mExecutorService.submit(mSyncTask);
+ try {
+ mCurrentFuture = mExecutorService.submit(mSyncTask);
+ } catch (RejectedExecutionException e) {
+ return CompletableFuture.failedFuture(e);
+ }
}
mUpdateFlags |= flags;
return mCurrentFuture;
@@ -687,12 +699,6 @@
BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
reason, 0);
- if (energyConsumerDeltas != null && !energyConsumerDeltas.isEmpty()
- && mStats.isUsageHistoryEnabled()) {
- mStats.recordEnergyConsumerDetailsLocked(elapsedRealtime, uptime,
- mEnergyConsumerSnapshot.getEnergyConsumerDetails(energyConsumerDeltas));
- }
-
if ((updateFlags & UPDATE_CPU) != 0) {
if (useLatestStates) {
onBattery = mStats.isOnBatteryLocked();
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index cf4e845..613f189 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -125,6 +125,7 @@
import com.android.internal.os.LongArrayMultiStateCounter;
import com.android.internal.os.LongMultiStateCounter;
import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
import com.android.internal.os.RailStats;
import com.android.internal.os.RpmStats;
import com.android.internal.power.EnergyConsumerStats;
@@ -135,6 +136,7 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.net.module.util.NetworkCapabilitiesUtils;
+import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import libcore.util.EmptyArray;
@@ -280,7 +282,8 @@
= new KernelMemoryBandwidthStats();
private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
private int[] mCpuPowerBracketMap;
- private final CpuUsageDetails mCpuUsageDetails = new CpuUsageDetails();
+ private final CpuPowerStatsCollector mCpuPowerStatsCollector;
+ private final PowerStatsAggregator mPowerStatsAggregator;
public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
return mKernelMemoryStats;
@@ -439,6 +442,7 @@
static final int RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG = 1 << 1;
private final int mFlags;
+ private final long mPowerStatsThrottlePeriodCpu;
private BatteryStatsConfig(Builder builder) {
int flags = 0;
@@ -449,6 +453,7 @@
flags |= RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
}
mFlags = flags;
+ mPowerStatsThrottlePeriodCpu = builder.mPowerStatsThrottlePeriodCpu;
}
/**
@@ -469,15 +474,22 @@
== RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
}
+ long getPowerStatsThrottlePeriodCpu() {
+ return mPowerStatsThrottlePeriodCpu;
+ }
+
/**
* Builder for BatteryStatsConfig
*/
public static class Builder {
private boolean mResetOnUnplugHighBatteryLevel;
private boolean mResetOnUnplugAfterSignificantCharge;
+ private long mPowerStatsThrottlePeriodCpu;
+
public Builder() {
mResetOnUnplugHighBatteryLevel = true;
mResetOnUnplugAfterSignificantCharge = true;
+ mPowerStatsThrottlePeriodCpu = 60000;
}
/**
@@ -504,8 +516,16 @@
mResetOnUnplugAfterSignificantCharge = reset;
return this;
}
- }
+ /**
+ * Sets the minimum amount of time (in millis) to wait between passes
+ * of CPU power stats collection.
+ */
+ public Builder setPowerStatsThrottlePeriodCpu(long periodMs) {
+ mPowerStatsThrottlePeriodCpu = periodMs;
+ return this;
+ }
+ }
}
private final PlatformIdleStateCallback mPlatformIdleStateCallback;
@@ -595,15 +615,8 @@
LongArrayMultiStateCounter onBatteryScreenOffCounter =
u.getProcStateScreenOffTimeCounter(elapsedRealtimeMs).getCounter();
- if (isUsageHistoryEnabled()) {
- LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
- getCpuTimeInFreqContainer();
- mKernelSingleUidTimeReader.addDelta(uid, onBatteryCounter, elapsedRealtimeMs,
- deltaContainer);
- recordCpuUsage(uid, deltaContainer, elapsedRealtimeMs, uptimeMs);
- } else {
- mKernelSingleUidTimeReader.addDelta(uid, onBatteryCounter, elapsedRealtimeMs);
- }
+
+ mKernelSingleUidTimeReader.addDelta(uid, onBatteryCounter, elapsedRealtimeMs);
mKernelSingleUidTimeReader.addDelta(uid, onBatteryScreenOffCounter, elapsedRealtimeMs);
if (u.mChildUids != null) {
@@ -617,25 +630,12 @@
mKernelSingleUidTimeReader.addDelta(u.mChildUids.keyAt(j),
cpuTimeInFreqCounter, elapsedRealtimeMs, deltaContainer);
onBatteryCounter.addCounts(deltaContainer);
- if (isUsageHistoryEnabled()) {
- recordCpuUsage(uid, deltaContainer, elapsedRealtimeMs, uptimeMs);
- }
onBatteryScreenOffCounter.addCounts(deltaContainer);
}
}
}
}
- private void recordCpuUsage(int uid, LongArrayMultiStateCounter.LongArrayContainer cpuUsage,
- long elapsedRealtimeMs, long uptimeMs) {
- if (!cpuUsage.combineValues(mCpuUsageDetails.cpuUsageMs, mCpuPowerBracketMap)) {
- return;
- }
-
- mCpuUsageDetails.uid = uid;
- mHistory.recordCpuUsage(elapsedRealtimeMs, uptimeMs, mCpuUsageDetails);
- }
-
/**
* Removes kernel CPU stats for removed UIDs, in the order they were added to the
* mPendingRemovedUids queue.
@@ -656,6 +656,10 @@
*/
@SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter
public void updateCpuTimesForAllUids() {
+ if (mCpuPowerStatsCollector != null) {
+ mCpuPowerStatsCollector.schedule();
+ }
+
synchronized (BatteryStatsImpl.this) {
if (!trackPerProcStateCpuTimes()) {
return;
@@ -687,16 +691,8 @@
u.getProcStateScreenOffTimeCounter(elapsedRealtimeMs).getCounter();
if (uid == parentUid || Process.isSdkSandboxUid(uid)) {
- if (isUsageHistoryEnabled()) {
- LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
- getCpuTimeInFreqContainer();
- mKernelSingleUidTimeReader.addDelta(parentUid, onBatteryCounter,
- elapsedRealtimeMs, deltaContainer);
- recordCpuUsage(parentUid, deltaContainer, elapsedRealtimeMs, uptimeMs);
- } else {
- mKernelSingleUidTimeReader.addDelta(parentUid, onBatteryCounter,
- elapsedRealtimeMs);
- }
+ mKernelSingleUidTimeReader.addDelta(parentUid, onBatteryCounter,
+ elapsedRealtimeMs);
mKernelSingleUidTimeReader.addDelta(parentUid, onBatteryScreenOffCounter,
elapsedRealtimeMs);
} else {
@@ -709,9 +705,6 @@
mKernelSingleUidTimeReader.addDelta(uid, counter, elapsedRealtimeMs,
deltaContainer);
onBatteryCounter.addCounts(deltaContainer);
- if (isUsageHistoryEnabled()) {
- recordCpuUsage(uid, deltaContainer, elapsedRealtimeMs, uptimeMs);
- }
onBatteryScreenOffCounter.addCounts(deltaContainer);
}
}
@@ -842,6 +835,8 @@
private final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
private final HistoryStepDetailsCalculatorImpl mStepDetailsCalculator =
new HistoryStepDetailsCalculatorImpl();
+ private final PowerStats.DescriptorRegistry mPowerStatsDescriptorRegistry =
+ new PowerStats.DescriptorRegistry();
private boolean mHaveBatteryLevel = false;
private boolean mBatteryPluggedIn;
@@ -1556,7 +1551,7 @@
@VisibleForTesting
@GuardedBy("this")
- protected BatteryStatsConfig mBatteryStatsConfig = new BatteryStatsConfig.Builder().build();
+ protected BatteryStatsConfig mBatteryStatsConfig;
@GuardedBy("this")
private AlarmManager mAlarmManager = null;
@@ -1733,6 +1728,7 @@
public BatteryStatsImpl(Clock clock, File historyDirectory) {
init(clock);
+ mBatteryStatsConfig = new BatteryStatsConfig.Builder().build();
mHandler = null;
mConstants = new Constants(mHandler);
mStartClockTimeMs = clock.currentTimeMillis();
@@ -1751,6 +1747,8 @@
mPlatformIdleStateCallback = null;
mEnergyConsumerRetriever = null;
mUserInfoProvider = null;
+ mCpuPowerStatsCollector = null;
+ mPowerStatsAggregator = null;
}
private void init(Clock clock) {
@@ -4383,6 +4381,12 @@
public void noteCurrentTimeChangedLocked(long currentTimeMs,
long elapsedRealtimeMs, long uptimeMs) {
mHistory.recordCurrentTimeChange(elapsedRealtimeMs, uptimeMs, currentTimeMs);
+ adjustStartClockTime(currentTimeMs);
+ }
+
+ private void adjustStartClockTime(long currentTimeMs) {
+ mStartClockTimeMs =
+ currentTimeMs - (mClock.elapsedRealtime() - (mRealtimeStartUs / 1000));
}
@GuardedBy("this")
@@ -7654,18 +7658,6 @@
return names;
}
- /**
- * Adds energy consumer delta to battery history.
- */
- @GuardedBy("this")
- public void recordEnergyConsumerDetailsLocked(long elapsedRealtimeMs,
- long uptimeMs, EnergyConsumerDetails energyConsumerDetails) {
- if (isUsageHistoryEnabled()) {
- mHistory.recordEnergyConsumerDetails(elapsedRealtimeMs, uptimeMs,
- energyConsumerDetails);
- }
- }
-
@GuardedBy("this")
@Override public long getStartClockTime() {
final long currentTimeMs = mClock.currentTimeMillis();
@@ -7676,9 +7668,8 @@
// the previous time was completely bogus. So we are going to figure out a
// new time based on how much time has elapsed since we started counting.
mHistory.recordCurrentTimeChange(mClock.elapsedRealtime(), mClock.uptimeMillis(),
- currentTimeMs
- );
- return currentTimeMs - (mClock.elapsedRealtime() - (mRealtimeStartUs / 1000));
+ currentTimeMs);
+ adjustStartClockTime(currentTimeMs);
}
return mStartClockTimeMs;
}
@@ -10569,6 +10560,10 @@
final int batteryConsumerProcessState =
mapUidProcessStateToBatteryConsumerProcessState(uidRunningState);
+ if (mBsi.mSystemReady && Flags.streamlinedBatteryStats()) {
+ mBsi.mHistory.recordProcessStateChange(elapsedRealtimeMs, uptimeMs, mUid,
+ batteryConsumerProcessState);
+ }
getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedRealtimeMs);
getMobileRadioActiveTimeCounter()
@@ -10911,21 +10906,23 @@
return mTmpCpuTimeInFreq;
}
- public BatteryStatsImpl(@Nullable File systemDir, @NonNull Handler handler,
- @Nullable PlatformIdleStateCallback cb, @Nullable EnergyStatsRetriever energyStatsCb,
- @NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
- @NonNull CpuScalingPolicies cpuScalingPolicies) {
- this(Clock.SYSTEM_CLOCK, systemDir, handler, cb, energyStatsCb, userInfoProvider,
- powerProfile, cpuScalingPolicies);
- }
-
- private BatteryStatsImpl(@NonNull Clock clock, @Nullable File systemDir,
+ public BatteryStatsImpl(@NonNull BatteryStatsConfig config, @Nullable File systemDir,
@NonNull Handler handler, @Nullable PlatformIdleStateCallback cb,
@Nullable EnergyStatsRetriever energyStatsCb,
@NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
@NonNull CpuScalingPolicies cpuScalingPolicies) {
+ this(config, Clock.SYSTEM_CLOCK, systemDir, handler, cb, energyStatsCb, userInfoProvider,
+ powerProfile, cpuScalingPolicies);
+ }
+
+ private BatteryStatsImpl(@NonNull BatteryStatsConfig config, @NonNull Clock clock,
+ @Nullable File systemDir, @NonNull Handler handler,
+ @Nullable PlatformIdleStateCallback cb, @Nullable EnergyStatsRetriever energyStatsCb,
+ @NonNull UserInfoProvider userInfoProvider, @NonNull PowerProfile powerProfile,
+ @NonNull CpuScalingPolicies cpuScalingPolicies) {
init(clock);
+ mBatteryStatsConfig = config;
mHandler = new MyHandler(handler.getLooper());
mConstants = new Constants(mHandler);
@@ -10947,6 +10944,23 @@
mHistory = new BatteryStatsHistory(systemDir, mConstants.MAX_HISTORY_FILES,
mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
}
+
+ mCpuPowerStatsCollector = new CpuPowerStatsCollector(mCpuScalingPolicies, mPowerProfile,
+ mHandler, mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu());
+ mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
+
+ PowerStatsAggregator.Builder builder = new PowerStatsAggregator.Builder(mHistory);
+ builder.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
+ .trackDeviceStates(
+ PowerStatsAggregator.STATE_POWER,
+ PowerStatsAggregator.STATE_SCREEN)
+ .trackUidStates(
+ PowerStatsAggregator.STATE_POWER,
+ PowerStatsAggregator.STATE_SCREEN,
+ PowerStatsAggregator.STATE_PROCESS_STATE);
+
+ mPowerStatsAggregator = builder.build();
+
mStartCount++;
initTimersAndCounters();
mOnBattery = mOnBatteryInternal = false;
@@ -10965,6 +10979,14 @@
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
}
+ private void recordPowerStats(PowerStats stats) {
+ if (stats.durationMs > 0) {
+ synchronized (this) {
+ mHistory.recordPowerStats(mClock.elapsedRealtime(), mClock.uptimeMillis(), stats);
+ }
+ }
+ }
+
@VisibleForTesting
protected void initTimersAndCounters() {
mScreenOnTimer = new StopwatchTimer(mClock, null, -1, null, mOnBatteryTimeBase);
@@ -11074,14 +11096,6 @@
}
}
- int cpuPowerBracketCount = mPowerProfile.getCpuPowerBracketCount();
- mCpuUsageDetails.cpuBracketDescriptions = new String[cpuPowerBracketCount];
- mCpuUsageDetails.cpuUsageMs = new long[cpuPowerBracketCount];
- for (int i = 0; i < cpuPowerBracketCount; i++) {
- mCpuUsageDetails.cpuBracketDescriptions[i] =
- mPowerProfile.getCpuPowerBracketDescription(mCpuScalingPolicies, i);
- }
-
if (mEstimatedBatteryCapacityMah == -1) {
// Initialize the estimated battery capacity to a known preset one.
mEstimatedBatteryCapacityMah = (int) mPowerProfile.getBatteryCapacity();
@@ -11095,15 +11109,6 @@
}
/**
- * Injects BatteryStatsConfig
- */
- public void setBatteryStatsConfig(BatteryStatsConfig config) {
- synchronized (this) {
- mBatteryStatsConfig = config;
- }
- }
-
- /**
* Starts tracking CPU time-in-state for threads of the system server process,
* keeping a separate account of threads receiving incoming binder calls.
*/
@@ -11466,8 +11471,9 @@
* Creates an iterator for battery stats history.
*/
@Override
- public BatteryStatsHistoryIterator iterateBatteryStatsHistory() {
- return mHistory.copy().iterate();
+ public BatteryStatsHistoryIterator iterateBatteryStatsHistory(long startTimeMs,
+ long endTimeMs) {
+ return mHistory.copy().iterate(startTimeMs, endTimeMs);
}
@Override
@@ -14197,6 +14203,9 @@
if (mCpuUidFreqTimeReader != null) {
mCpuUidFreqTimeReader.onSystemReady();
}
+ if (mCpuPowerStatsCollector != null) {
+ mCpuPowerStatsCollector.onSystemReady();
+ }
mSystemReady = true;
}
@@ -15213,11 +15222,6 @@
}
@GuardedBy("this")
- boolean isUsageHistoryEnabled() {
- return mConstants.RECORD_USAGE_HISTORY;
- }
-
- @GuardedBy("this")
public void systemServicesReady(Context context) {
mConstants.startObserving(context.getContentResolver());
registerUsbStateReceiver(context);
@@ -15328,8 +15332,6 @@
public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
public static final String KEY_BATTERY_CHARGED_DELAY_MS =
"battery_charged_delay_ms";
- public static final String KEY_RECORD_USAGE_HISTORY =
- "record_usage_history";
public static final String KEY_PER_UID_MODEM_POWER_MODEL =
"per_uid_modem_power_model";
public static final String KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION =
@@ -15380,7 +15382,6 @@
private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/
private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */
- private static final boolean DEFAULT_RECORD_USAGE_HISTORY = false;
@PerUidModemPowerModel
private static final int DEFAULT_PER_UID_MODEM_MODEL =
PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX;
@@ -15402,7 +15403,6 @@
public int MAX_HISTORY_FILES;
public int MAX_HISTORY_BUFFER; /*Bytes*/
public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS;
- public boolean RECORD_USAGE_HISTORY = DEFAULT_RECORD_USAGE_HISTORY;
public int PER_UID_MODEM_MODEL = DEFAULT_PER_UID_MODEM_MODEL;
public boolean PHONE_ON_EXTERNAL_STATS_COLLECTION =
DEFAULT_PHONE_ON_EXTERNAL_STATS_COLLECTION;
@@ -15483,8 +15483,6 @@
DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB
: DEFAULT_MAX_HISTORY_BUFFER_KB)
* 1024;
- RECORD_USAGE_HISTORY = mParser.getBoolean(
- KEY_RECORD_USAGE_HISTORY, DEFAULT_RECORD_USAGE_HISTORY);
final String perUidModemModel = mParser.getString(KEY_PER_UID_MODEM_POWER_MODEL,
"");
PER_UID_MODEM_MODEL = getPerUidModemModel(perUidModemModel);
@@ -15562,8 +15560,6 @@
pw.println(MAX_HISTORY_BUFFER/1024);
pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("=");
pw.println(BATTERY_CHARGED_DELAY_MS);
- pw.print(KEY_RECORD_USAGE_HISTORY); pw.print("=");
- pw.println(RECORD_USAGE_HISTORY);
pw.print(KEY_PER_UID_MODEM_POWER_MODEL); pw.print("=");
pw.println(getPerUidModemModelName(PER_UID_MODEM_MODEL));
pw.print(KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION); pw.print("=");
@@ -15705,6 +15701,21 @@
iPw.decreaseIndent();
}
+ /**
+ * Grabs one sample of PowerStats and prints it.
+ */
+ public void dumpStatsSample(PrintWriter pw) {
+ mCpuPowerStatsCollector.collectAndDump(pw);
+ }
+
+ /**
+ * Aggregates power stats between the specified times and prints them.
+ */
+ public void dumpAggregatedStats(PrintWriter pw, long startTimeMs, long endTimeMs) {
+ mPowerStatsAggregator.aggregateBatteryStats(startTimeMs, endTimeMs,
+ stats-> stats.dump(pw));
+ }
+
private final Runnable mWriteAsyncRunnable = () -> {
synchronized (BatteryStatsImpl.this) {
writeSyncLocked();
diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java
new file mode 100644
index 0000000..5b3fe06
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java
@@ -0,0 +1,29 @@
+/*
+ * 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.power.stats;
+
+import android.os.BatteryConsumer;
+
+import com.android.internal.os.MultiStateStats;
+
+class CpuAggregatedPowerStats extends PowerComponentAggregatedPowerStats {
+
+ CpuAggregatedPowerStats(MultiStateStats.States[] deviceStates,
+ MultiStateStats.States[] uidStates) {
+ super(BatteryConsumer.POWER_COMPONENT_CPU, deviceStates, uidStates);
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
new file mode 100644
index 0000000..376ca89
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -0,0 +1,140 @@
+/*
+ * 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.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.Keep;
+import com.android.internal.annotations.VisibleForNative;
+import com.android.internal.os.Clock;
+import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.optimization.Flags;
+
+/**
+ * Collects snapshots of power-related system statistics.
+ * <p>
+ * The class is intended to be used in a serialized fashion using the handler supplied in the
+ * constructor. Thus the object is not thread-safe except where noted.
+ */
+public class CpuPowerStatsCollector extends PowerStatsCollector {
+ private static final long NANOS_PER_MILLIS = 1000000;
+
+ private final KernelCpuStatsReader mKernelCpuStatsReader;
+ private final int[] mScalingStepToPowerBracketMap;
+ private final long[] mTempUidStats;
+ private final SparseArray<UidStats> mUidStats = new SparseArray<>();
+ private final int mUidStatsSize;
+ // Reusable instance
+ private final PowerStats mCpuPowerStats;
+ private long mLastUpdateTimestampNanos;
+
+ public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
+ Handler handler, long throttlePeriodMs) {
+ this(cpuScalingPolicies, powerProfile, handler, new KernelCpuStatsReader(),
+ throttlePeriodMs, Clock.SYSTEM_CLOCK);
+ }
+
+ public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
+ Handler handler, KernelCpuStatsReader kernelCpuStatsReader,
+ long throttlePeriodMs, Clock clock) {
+ super(handler, throttlePeriodMs, clock);
+ mKernelCpuStatsReader = kernelCpuStatsReader;
+
+ int scalingStepCount = cpuScalingPolicies.getScalingStepCount();
+ mScalingStepToPowerBracketMap = new int[scalingStepCount];
+ int index = 0;
+ for (int policy : cpuScalingPolicies.getPolicies()) {
+ int[] frequencies = cpuScalingPolicies.getFrequencies(policy);
+ for (int step = 0; step < frequencies.length; step++) {
+ int bracket = powerProfile.getCpuPowerBracketForScalingStep(policy, step);
+ mScalingStepToPowerBracketMap[index++] = bracket;
+ }
+ }
+ mUidStatsSize = powerProfile.getCpuPowerBracketCount();
+ mTempUidStats = new long[mUidStatsSize];
+
+ mCpuPowerStats = new PowerStats(
+ new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 0, mUidStatsSize,
+ new PersistableBundle()));
+ }
+
+ /**
+ * Initializes the collector during the boot sequence.
+ */
+ public void onSystemReady() {
+ setEnabled(Flags.streamlinedBatteryStats());
+ }
+
+ @Override
+ protected PowerStats collectStats() {
+ mCpuPowerStats.uidStats.clear();
+ long newTimestampNanos = mKernelCpuStatsReader.nativeReadCpuStats(
+ this::processUidStats, mScalingStepToPowerBracketMap, mLastUpdateTimestampNanos,
+ mTempUidStats);
+ mCpuPowerStats.durationMs =
+ (newTimestampNanos - mLastUpdateTimestampNanos) / NANOS_PER_MILLIS;
+ mLastUpdateTimestampNanos = newTimestampNanos;
+ return mCpuPowerStats;
+ }
+
+ @VisibleForNative
+ interface KernelCpuStatsCallback {
+ @Keep // Called from native
+ void processUidStats(int uid, long[] stats);
+ }
+
+ private void processUidStats(int uid, long[] stats) {
+ UidStats uidStats = mUidStats.get(uid);
+ if (uidStats == null) {
+ uidStats = new UidStats();
+ uidStats.stats = new long[mUidStatsSize];
+ uidStats.delta = new long[mUidStatsSize];
+ mUidStats.put(uid, uidStats);
+ }
+
+ boolean nonzero = false;
+ for (int i = mUidStatsSize - 1; i >= 0; i--) {
+ long delta = uidStats.delta[i] = stats[i] - uidStats.stats[i];
+ if (delta != 0) {
+ nonzero = true;
+ }
+ uidStats.stats[i] = stats[i];
+ }
+ if (nonzero) {
+ mCpuPowerStats.uidStats.put(uid, uidStats.delta);
+ }
+ }
+
+ /**
+ * Native class that retrieves CPU stats from the kernel.
+ */
+ public static class KernelCpuStatsReader {
+ protected native long nativeReadCpuStats(KernelCpuStatsCallback callback,
+ int[] scalingStepToPowerBracketMap, long lastUpdateTimestampNanos,
+ long[] tempForUidStats);
+ }
+
+ private static class UidStats {
+ public long[] stats;
+ public long[] delta;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerSnapshot.java b/services/core/java/com/android/server/power/stats/EnergyConsumerSnapshot.java
index 939a08b..7f50ae0 100644
--- a/services/core/java/com/android/server/power/stats/EnergyConsumerSnapshot.java
+++ b/services/core/java/com/android/server/power/stats/EnergyConsumerSnapshot.java
@@ -23,7 +23,6 @@
import android.hardware.power.stats.EnergyConsumerAttribution;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
-import android.os.BatteryStats.EnergyConsumerDetails;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -85,8 +84,6 @@
*/
private final SparseArray<SparseLongArray> mAttributionSnapshots;
- private EnergyConsumerDetails mEnergyConsumerDetails;
-
/**
* Constructor that initializes to the given id->EnergyConsumer map, indicating which consumers
* exist and what their details are.
@@ -423,122 +420,4 @@
// since the last snapshot. Round off to the nearest whole long.
return (deltaEnergyUJ * MILLIVOLTS_PER_VOLT + (avgVoltageMV / 2)) / avgVoltageMV;
}
-
- /**
- * Converts the EnergyConsumerDeltaData object to EnergyConsumerDetails, which can
- * be saved in battery history.
- */
- EnergyConsumerDetails getEnergyConsumerDetails(
- EnergyConsumerDeltaData delta) {
- if (mEnergyConsumerDetails == null) {
- mEnergyConsumerDetails = createEnergyConsumerDetails();
- }
-
- final long[] chargeUC = mEnergyConsumerDetails.chargeUC;
- for (int i = 0; i < mEnergyConsumerDetails.consumers.length; i++) {
- EnergyConsumerDetails.EnergyConsumer energyConsumer =
- mEnergyConsumerDetails.consumers[i];
- switch (energyConsumer.type) {
- case EnergyConsumerType.BLUETOOTH:
- chargeUC[i] = delta.bluetoothChargeUC;
- break;
- case EnergyConsumerType.CPU_CLUSTER:
- if (delta.cpuClusterChargeUC != null) {
- chargeUC[i] = delta.cpuClusterChargeUC[energyConsumer.ordinal];
- } else {
- chargeUC[i] = UNAVAILABLE;
- }
- break;
- case EnergyConsumerType.DISPLAY:
- if (delta.displayChargeUC != null) {
- chargeUC[i] = delta.displayChargeUC[energyConsumer.ordinal];
- } else {
- chargeUC[i] = UNAVAILABLE;
- }
- break;
- case EnergyConsumerType.GNSS:
- chargeUC[i] = delta.gnssChargeUC;
- break;
- case EnergyConsumerType.MOBILE_RADIO:
- chargeUC[i] = delta.mobileRadioChargeUC;
- break;
- case EnergyConsumerType.WIFI:
- chargeUC[i] = delta.wifiChargeUC;
- break;
- case EnergyConsumerType.CAMERA:
- chargeUC[i] = delta.cameraChargeUC;
- break;
- case EnergyConsumerType.OTHER:
- if (delta.otherTotalChargeUC != null) {
- chargeUC[i] = delta.otherTotalChargeUC[energyConsumer.ordinal];
- } else {
- chargeUC[i] = UNAVAILABLE;
- }
- break;
- default:
- chargeUC[i] = UNAVAILABLE;
- break;
- }
- }
- return mEnergyConsumerDetails;
- }
-
- private EnergyConsumerDetails createEnergyConsumerDetails() {
- EnergyConsumerDetails details = new EnergyConsumerDetails();
- details.consumers =
- new EnergyConsumerDetails.EnergyConsumer[mEnergyConsumers.size()];
- for (int i = 0; i < mEnergyConsumers.size(); i++) {
- EnergyConsumer energyConsumer = mEnergyConsumers.valueAt(i);
- EnergyConsumerDetails.EnergyConsumer consumer =
- new EnergyConsumerDetails.EnergyConsumer();
- consumer.type = energyConsumer.type;
- consumer.ordinal = energyConsumer.ordinal;
- switch (consumer.type) {
- case EnergyConsumerType.BLUETOOTH:
- consumer.name = "BLUETOOTH";
- break;
- case EnergyConsumerType.CPU_CLUSTER:
- consumer.name = "CPU";
- break;
- case EnergyConsumerType.DISPLAY:
- consumer.name = "DISPLAY";
- break;
- case EnergyConsumerType.GNSS:
- consumer.name = "GNSS";
- break;
- case EnergyConsumerType.MOBILE_RADIO:
- consumer.name = "MOBILE_RADIO";
- break;
- case EnergyConsumerType.WIFI:
- consumer.name = "WIFI";
- break;
- case EnergyConsumerType.OTHER:
- consumer.name = sanitizeCustomBucketName(energyConsumer.name);
- break;
- default:
- consumer.name = "UNKNOWN";
- break;
- }
- if (consumer.type != EnergyConsumerType.OTHER) {
- boolean hasOrdinal = consumer.ordinal != 0;
- if (!hasOrdinal) {
- // See if any other EnergyConsumer of the same type has an ordinal
- for (int j = 0; j < mEnergyConsumers.size(); j++) {
- EnergyConsumer aConsumer = mEnergyConsumers.valueAt(j);
- if (aConsumer.type == consumer.type && aConsumer.ordinal != 0) {
- hasOrdinal = true;
- break;
- }
- }
- }
- if (hasOrdinal) {
- consumer.name = consumer.name + "/" + energyConsumer.ordinal;
- }
- }
- details.consumers[i] = consumer;
- }
-
- details.chargeUC = new long[details.consumers.length];
- return details;
- }
}
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
new file mode 100644
index 0000000..686268f
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -0,0 +1,232 @@
+/*
+ * 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.power.stats;
+
+import android.util.IndentingPrintWriter;
+import android.util.SparseArray;
+
+import com.android.internal.os.MultiStateStats;
+import com.android.internal.os.PowerStats;
+
+import java.util.Collection;
+
+/**
+ * Aggregated power stats for a specific power component (e.g. CPU, WiFi, etc). This class
+ * treats stats as arrays of nonspecific longs. Subclasses contain specific logic to interpret those
+ * longs and use them for calculations such as power attribution. They may use meta-data supplied
+ * as part of the {@link PowerStats.Descriptor}.
+ */
+class PowerComponentAggregatedPowerStats {
+ public final int powerComponentId;
+ private final MultiStateStats.States[] mDeviceStateConfig;
+ private final MultiStateStats.States[] mUidStateConfig;
+ private final int[] mDeviceStates;
+ private final long[] mDeviceStateTimestamps;
+
+ private MultiStateStats.Factory mStatsFactory;
+ private MultiStateStats.Factory mUidStatsFactory;
+ private PowerStats.Descriptor mPowerStatsDescriptor;
+ private MultiStateStats mDeviceStats;
+ private final SparseArray<UidStats> mUidStats = new SparseArray<>();
+
+ private static class UidStats {
+ public int[] states;
+ public long[] stateTimestampMs;
+ public MultiStateStats stats;
+ }
+
+ PowerComponentAggregatedPowerStats(int powerComponentId,
+ MultiStateStats.States[] deviceStates,
+ MultiStateStats.States[] uidStates) {
+ this.powerComponentId = powerComponentId;
+ mDeviceStateConfig = deviceStates;
+ mUidStateConfig = uidStates;
+ mDeviceStates = new int[mDeviceStateConfig.length];
+ mDeviceStateTimestamps = new long[mDeviceStateConfig.length];
+ }
+
+ void setState(@PowerStatsAggregator.TrackedState int stateId, int state, long time) {
+ mDeviceStates[stateId] = state;
+ mDeviceStateTimestamps[stateId] = time;
+
+ if (mDeviceStateConfig[stateId].isTracked()) {
+ if (mDeviceStats != null || createDeviceStats()) {
+ mDeviceStats.setState(stateId, state, time);
+ }
+ }
+
+ if (mUidStateConfig[stateId].isTracked()) {
+ for (int i = mUidStats.size() - 1; i >= 0; i--) {
+ PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
+ if (uidStats.stats != null || createUidStats(uidStats)) {
+ uidStats.stats.setState(stateId, state, time);
+ }
+ }
+ }
+ }
+
+ void setUidState(int uid, @PowerStatsAggregator.TrackedState int stateId, int state,
+ long time) {
+ if (!mUidStateConfig[stateId].isTracked()) {
+ return;
+ }
+
+ UidStats uidStats = getUidStats(uid);
+ uidStats.states[stateId] = state;
+ uidStats.stateTimestampMs[stateId] = time;
+
+ if (uidStats.stats != null || createUidStats(uidStats)) {
+ uidStats.stats.setState(stateId, state, time);
+ }
+ }
+
+ boolean isCompatible(PowerStats powerStats) {
+ return mPowerStatsDescriptor == null || mPowerStatsDescriptor.equals(powerStats.descriptor);
+ }
+
+ void addPowerStats(PowerStats powerStats, long timestampMs) {
+ mPowerStatsDescriptor = powerStats.descriptor;
+
+ if (mDeviceStats == null) {
+ if (mStatsFactory == null) {
+ mStatsFactory = new MultiStateStats.Factory(
+ mPowerStatsDescriptor.statsArrayLength, mDeviceStateConfig);
+ mUidStatsFactory = new MultiStateStats.Factory(
+ mPowerStatsDescriptor.uidStatsArrayLength, mUidStateConfig);
+ }
+
+ createDeviceStats();
+ }
+
+ mDeviceStats.increment(powerStats.stats, timestampMs);
+
+ for (int i = powerStats.uidStats.size() - 1; i >= 0; i--) {
+ int uid = powerStats.uidStats.keyAt(i);
+ PowerComponentAggregatedPowerStats.UidStats uidStats = getUidStats(uid);
+ if (uidStats.stats == null) {
+ createUidStats(uidStats);
+ }
+ uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs);
+ }
+ }
+
+ void reset() {
+ mPowerStatsDescriptor = null;
+ mStatsFactory = null;
+ mUidStatsFactory = null;
+ mDeviceStats = null;
+ for (int i = mUidStats.size() - 1; i >= 0; i--) {
+ mUidStats.valueAt(i).stats = null;
+ }
+ }
+
+ private UidStats getUidStats(int uid) {
+ // TODO(b/292247660): map isolated and sandbox UIDs
+ UidStats uidStats = mUidStats.get(uid);
+ if (uidStats == null) {
+ uidStats = new UidStats();
+ uidStats.states = new int[mUidStateConfig.length];
+ uidStats.stateTimestampMs = new long[mUidStateConfig.length];
+ mUidStats.put(uid, uidStats);
+ }
+ return uidStats;
+ }
+
+ void collectUids(Collection<Integer> uids) {
+ for (int i = mUidStats.size() - 1; i >= 0; i--) {
+ if (mUidStats.valueAt(i).stats != null) {
+ uids.add(mUidStats.keyAt(i));
+ }
+ }
+ }
+
+ boolean getDeviceStats(long[] outValues, int[] deviceStates) {
+ if (deviceStates.length != mDeviceStateConfig.length) {
+ throw new IllegalArgumentException(
+ "Invalid number of tracked states: " + deviceStates.length
+ + " expected: " + mDeviceStateConfig.length);
+ }
+ if (mDeviceStats != null) {
+ mDeviceStats.getStats(outValues, deviceStates);
+ return true;
+ }
+ return false;
+ }
+
+ boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
+ if (uidStates.length != mUidStateConfig.length) {
+ throw new IllegalArgumentException(
+ "Invalid number of tracked states: " + uidStates.length
+ + " expected: " + mUidStateConfig.length);
+ }
+ UidStats uidStats = mUidStats.get(uid);
+ if (uidStats != null && uidStats.stats != null) {
+ uidStats.stats.getStats(outValues, uidStates);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean createDeviceStats() {
+ if (mStatsFactory == null) {
+ return false;
+ }
+
+ mDeviceStats = mStatsFactory.create();
+ for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
+ mDeviceStats.setState(stateId, mDeviceStates[stateId],
+ mDeviceStateTimestamps[stateId]);
+ }
+ return true;
+ }
+
+ private boolean createUidStats(UidStats uidStats) {
+ if (mUidStatsFactory == null) {
+ return false;
+ }
+
+ uidStats.stats = mUidStatsFactory.create();
+ for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
+ uidStats.stats.setState(stateId, mDeviceStates[stateId],
+ mDeviceStateTimestamps[stateId]);
+ }
+ for (int stateId = mDeviceStateConfig.length; stateId < mUidStateConfig.length; stateId++) {
+ uidStats.stats.setState(stateId, uidStats.states[stateId],
+ uidStats.stateTimestampMs[stateId]);
+ }
+ return true;
+ }
+
+ void dumpDevice(IndentingPrintWriter ipw) {
+ if (mDeviceStats != null) {
+ ipw.println(mPowerStatsDescriptor.name);
+ ipw.increaseIndent();
+ mDeviceStats.dump(ipw);
+ ipw.decreaseIndent();
+ }
+ }
+
+ void dumpUid(IndentingPrintWriter ipw, int uid) {
+ UidStats uidStats = mUidStats.get(uid);
+ if (uidStats != null && uidStats.stats != null) {
+ ipw.println(mPowerStatsDescriptor.name);
+ ipw.increaseIndent();
+ uidStats.stats.dump(ipw);
+ ipw.decreaseIndent();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
new file mode 100644
index 0000000..6a1c1da
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
@@ -0,0 +1,226 @@
+/*
+ * 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.power.stats;
+
+import android.annotation.IntDef;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.BatteryStatsHistoryIterator;
+import com.android.internal.os.MultiStateStats;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+class PowerStatsAggregator {
+ public static final int STATE_POWER = 0;
+ public static final int STATE_SCREEN = 1;
+ public static final int STATE_PROCESS_STATE = 2;
+
+ @IntDef({
+ STATE_POWER,
+ STATE_SCREEN,
+ STATE_PROCESS_STATE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TrackedState {
+ }
+
+ static final int POWER_STATE_BATTERY = 0;
+ static final int POWER_STATE_OTHER = 1; // Plugged in, or on wireless charger, etc.
+ static final String[] STATE_LABELS_POWER = {"pwr-battery", "pwr-other"};
+
+ static final int SCREEN_STATE_ON = 0;
+ static final int SCREEN_STATE_OTHER = 1; // Off, doze etc
+ static final String[] STATE_LABELS_SCREEN = {"scr-on", "scr-other"};
+
+ static final String[] STATE_LABELS_PROCESS_STATE;
+
+ static {
+ String[] procStateLabels = new String[BatteryConsumer.PROCESS_STATE_COUNT];
+ for (int i = 0; i < BatteryConsumer.PROCESS_STATE_COUNT; i++) {
+ procStateLabels[i] = BatteryConsumer.processStateToString(i);
+ }
+ STATE_LABELS_PROCESS_STATE = procStateLabels;
+ }
+
+ private final BatteryStatsHistory mHistory;
+ private final AggregatedPowerStats mStats;
+
+ private PowerStatsAggregator(BatteryStatsHistory history,
+ AggregatedPowerStats aggregatedPowerStats) {
+ mHistory = history;
+ mStats = aggregatedPowerStats;
+ }
+
+ /**
+ * Iterates of the battery history and aggregates power stats between the specified times.
+ * The start and end are specified in the battery-stats monotonic time, which is the
+ * adjusted elapsed time found in HistoryItem.time.
+ * <p>
+ * The aggregated stats are sent to the consumer. One aggregation pass may produce
+ * multiple sets of aggregated stats if there was an incompatible change that occurred in the
+ * middle of the recorded battery history.
+ * <p>
+ * Note: the AggregatedPowerStats object is reused, so the consumer should fully consume
+ * the stats in the <code>accept</code> method and never cache it.
+ */
+ void aggregateBatteryStats(long startTimeMs, long endTimeMs,
+ Consumer<AggregatedPowerStats> consumer) {
+ mStats.reset();
+
+ int currentBatteryState = POWER_STATE_BATTERY;
+ int currentScreenState = SCREEN_STATE_OTHER;
+ long baseTime = -1;
+ long lastTime = 0;
+ try (BatteryStatsHistoryIterator iterator =
+ mHistory.copy().iterate(startTimeMs, endTimeMs)) {
+ while (iterator.hasNext()) {
+ BatteryStats.HistoryItem item = iterator.next();
+
+ if (baseTime < 0) {
+ mStats.setStartTime(item.currentTime);
+ baseTime = item.time;
+ }
+
+ lastTime = item.time;
+
+ int batteryState =
+ (item.states & BatteryStats.HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0
+ ? POWER_STATE_OTHER : POWER_STATE_BATTERY;
+ if (batteryState != currentBatteryState) {
+ mStats.setDeviceState(STATE_POWER, batteryState, item.time);
+ currentBatteryState = batteryState;
+ }
+
+ int screenState =
+ (item.states & BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG) != 0
+ ? SCREEN_STATE_ON : SCREEN_STATE_OTHER;
+ if (screenState != currentScreenState) {
+ mStats.setDeviceState(STATE_SCREEN, screenState, item.time);
+ currentScreenState = screenState;
+ }
+
+ if (item.processStateChange != null) {
+ mStats.setUidState(item.processStateChange.uid, STATE_PROCESS_STATE,
+ item.processStateChange.processState, item.time);
+ }
+
+ if (item.powerStats != null) {
+ if (!mStats.isCompatible(item.powerStats)) {
+ mStats.setDuration(lastTime - baseTime);
+ consumer.accept(mStats);
+ mStats.reset();
+ mStats.setStartTime(item.currentTime);
+ baseTime = lastTime = item.time;
+ }
+ mStats.addPowerStats(item.powerStats, item.time);
+ }
+ }
+ }
+ mStats.setDuration(lastTime - baseTime);
+ consumer.accept(mStats);
+ }
+
+ static class Builder {
+ static class PowerComponentAggregateStatsBuilder {
+ private final int mPowerComponentId;
+ private @TrackedState int[] mTrackedDeviceStates;
+ private @TrackedState int[] mTrackedUidStates;
+
+ PowerComponentAggregateStatsBuilder(int powerComponentId) {
+ this.mPowerComponentId = powerComponentId;
+ }
+
+ public PowerComponentAggregateStatsBuilder trackDeviceStates(
+ @TrackedState int... states) {
+ mTrackedDeviceStates = states;
+ return this;
+ }
+
+ public PowerComponentAggregateStatsBuilder trackUidStates(@TrackedState int... states) {
+ mTrackedUidStates = states;
+ return this;
+ }
+
+ private PowerComponentAggregatedPowerStats build() {
+ MultiStateStats.States[] deviceStates = new MultiStateStats.States[]{
+ new MultiStateStats.States(isTracked(mTrackedDeviceStates, STATE_POWER),
+ PowerStatsAggregator.STATE_LABELS_POWER),
+ new MultiStateStats.States(isTracked(mTrackedDeviceStates, STATE_SCREEN),
+ PowerStatsAggregator.STATE_LABELS_SCREEN),
+ };
+
+ MultiStateStats.States[] uidStates = new MultiStateStats.States[]{
+ new MultiStateStats.States(isTracked(mTrackedUidStates, STATE_POWER),
+ PowerStatsAggregator.STATE_LABELS_POWER),
+ new MultiStateStats.States(isTracked(mTrackedUidStates, STATE_SCREEN),
+ PowerStatsAggregator.STATE_LABELS_SCREEN),
+ new MultiStateStats.States(
+ isTracked(mTrackedUidStates, STATE_PROCESS_STATE),
+ PowerStatsAggregator.STATE_LABELS_PROCESS_STATE),
+ };
+
+ switch (mPowerComponentId) {
+ case BatteryConsumer.POWER_COMPONENT_CPU:
+ return new CpuAggregatedPowerStats(deviceStates, uidStates);
+ default:
+ return new PowerComponentAggregatedPowerStats(mPowerComponentId,
+ deviceStates, uidStates);
+ }
+ }
+
+ private boolean isTracked(int[] trackedStates, int state) {
+ if (trackedStates == null) {
+ return false;
+ }
+
+ for (int trackedState : trackedStates) {
+ if (trackedState == state) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private final BatteryStatsHistory mHistory;
+ private final List<PowerComponentAggregateStatsBuilder> mPowerComponents =
+ new ArrayList<>();
+
+ Builder(BatteryStatsHistory history) {
+ mHistory = history;
+ }
+
+ PowerComponentAggregateStatsBuilder trackPowerComponent(int powerComponentId) {
+ PowerComponentAggregateStatsBuilder builder = new PowerComponentAggregateStatsBuilder(
+ powerComponentId);
+ mPowerComponents.add(builder);
+ return builder;
+ }
+
+ PowerStatsAggregator build() {
+ return new PowerStatsAggregator(mHistory, new AggregatedPowerStats(
+ mPowerComponents.stream()
+ .map(PowerComponentAggregateStatsBuilder::build)
+ .toArray(PowerComponentAggregatedPowerStats[]::new)));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
new file mode 100644
index 0000000..b49c89f
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -0,0 +1,183 @@
+/*
+ * 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.power.stats;
+
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.util.FastImmutableArraySet;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+/**
+ * Collects snapshots of power-related system statistics.
+ * <p>
+ * Instances of this class are intended to be used in a serialized fashion using
+ * the handler supplied in the constructor. Thus these objects are not thread-safe
+ * except where noted.
+ */
+public abstract class PowerStatsCollector {
+ private final Handler mHandler;
+ private final Clock mClock;
+ private final long mThrottlePeriodMs;
+ private final Runnable mCollectAndDeliverStats = this::collectAndDeliverStats;
+ private boolean mEnabled;
+ private long mLastScheduledUpdateMs = -1;
+
+ @GuardedBy("this")
+ @SuppressWarnings("unchecked")
+ private volatile FastImmutableArraySet<Consumer<PowerStats>> mConsumerList =
+ new FastImmutableArraySet<Consumer<PowerStats>>(new Consumer[0]);
+
+ public PowerStatsCollector(Handler handler, long throttlePeriodMs, Clock clock) {
+ mHandler = handler;
+ mThrottlePeriodMs = throttlePeriodMs;
+ mClock = clock;
+ }
+
+ /**
+ * Adds a consumer that will receive a callback every time a snapshot of stats is collected.
+ * The method is thread safe.
+ */
+ @SuppressWarnings("unchecked")
+ public void addConsumer(Consumer<PowerStats> consumer) {
+ synchronized (this) {
+ mConsumerList = new FastImmutableArraySet<Consumer<PowerStats>>(
+ Stream.concat(mConsumerList.stream(), Stream.of(consumer))
+ .toArray(Consumer[]::new));
+ }
+ }
+
+ /**
+ * Removes a consumer.
+ * The method is thread safe.
+ */
+ @SuppressWarnings("unchecked")
+ public void removeConsumer(Consumer<PowerStats> consumer) {
+ synchronized (this) {
+ mConsumerList = new FastImmutableArraySet<Consumer<PowerStats>>(
+ mConsumerList.stream().filter(c -> c != consumer)
+ .toArray(Consumer[]::new));
+ }
+ }
+
+ /**
+ * Should be called at most once, before the first invocation of {@link #schedule} or
+ * {@link #forceSchedule}
+ */
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ }
+
+ /**
+ * Returns true if the collector is enabled.
+ */
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @SuppressWarnings("GuardedBy") // Field is volatile
+ private void collectAndDeliverStats() {
+ PowerStats stats = collectStats();
+ for (Consumer<PowerStats> consumer : mConsumerList) {
+ consumer.accept(stats);
+ }
+ }
+
+ /**
+ * Schedules a stats snapshot collection, throttled in accordance with the
+ * {@link #mThrottlePeriodMs} parameter.
+ */
+ public boolean schedule() {
+ if (!mEnabled) {
+ return false;
+ }
+
+ long uptimeMillis = mClock.uptimeMillis();
+ if (uptimeMillis - mLastScheduledUpdateMs < mThrottlePeriodMs
+ && mLastScheduledUpdateMs >= 0) {
+ return false;
+ }
+ mLastScheduledUpdateMs = uptimeMillis;
+ mHandler.post(mCollectAndDeliverStats);
+ return true;
+ }
+
+ /**
+ * Schedules an immediate snapshot collection, foregoing throttling.
+ */
+ public boolean forceSchedule() {
+ if (!mEnabled) {
+ return false;
+ }
+
+ mHandler.removeCallbacks(mCollectAndDeliverStats);
+ mHandler.postAtFrontOfQueue(mCollectAndDeliverStats);
+ return true;
+ }
+
+ protected abstract PowerStats collectStats();
+
+ /**
+ * Collects a fresh stats snapshot and prints it to the supplied printer.
+ */
+ public void collectAndDump(PrintWriter pw) {
+ if (Thread.currentThread() == mHandler.getLooper().getThread()) {
+ throw new RuntimeException(
+ "Calling this method from the handler thread would cause a deadlock");
+ }
+
+ IndentingPrintWriter out = new IndentingPrintWriter(pw);
+ out.print(getClass().getSimpleName());
+ if (!isEnabled()) {
+ out.println(": disabled");
+ return;
+ }
+ out.println();
+
+ ArrayList<PowerStats> collected = new ArrayList<>();
+ Consumer<PowerStats> consumer = collected::add;
+ addConsumer(consumer);
+
+ try {
+ if (forceSchedule()) {
+ awaitCompletion();
+ }
+ } finally {
+ removeConsumer(consumer);
+ }
+
+ out.increaseIndent();
+ for (PowerStats stats : collected) {
+ stats.dump(out);
+ }
+ out.decreaseIndent();
+ }
+
+ private void awaitCompletion() {
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ done.block();
+ }
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 0ca5603..2007079 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -121,7 +121,7 @@
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
} else if (getAvailableRollback(failedPackage) != null) {
// Rollback is available, we may get a callback into #execute
- impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_60;
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
} else if (anyRollbackAvailable) {
// If any rollbacks are available, we will commit them
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index cb09aef..5b36cd6 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -3055,10 +3055,10 @@
TvInputInfo tvInputInfo = tvInputState.info;
int inputState = tvInputState.state;
int inputType = tvInputInfo.getType();
- // For non-CEC input, the value of vendorId is 0.
- int vendorId = 0;
- // For non-HDMI input, the value of hdmiPort is 0.
- int hdmiPort = 0;
+ // For non-CEC input, the value of vendorId is 0xFFFFFF (16777215 in decimal).
+ int vendorId = 16777215;
+ // For non-HDMI input, the value of hdmiPort is -1.
+ int hdmiPort = -1;
String tifSessionId = sessionState.sessionId;
if (tvInputInfo.getType() == TvInputInfo.TYPE_HDMI) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index ddc0519..aaf48fb 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2000,12 +2000,7 @@
WallpaperData wallpaper, IRemoteCallback reply, ServiceInfo serviceInfo) {
if (serviceInfo == null) {
- if (wallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)) {
- clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
- clearWallpaperLocked(FLAG_LOCK, wallpaper.userId, reply);
- } else {
- clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, reply);
- }
+ clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, reply);
return;
}
Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
@@ -2037,7 +2032,7 @@
WallpaperData data = null;
synchronized (mLock) {
if (mIsLockscreenLiveWallpaperEnabled) {
- clearWallpaperLocked(callingPackage, which, userId);
+ clearWallpaperLocked(callingPackage, which, userId, null);
} else {
clearWallpaperLocked(which, userId, null);
}
@@ -2057,7 +2052,8 @@
}
}
- private void clearWallpaperLocked(String callingPackage, int which, int userId) {
+ private void clearWallpaperLocked(String callingPackage, int which, int userId,
+ IRemoteCallback reply) {
// Might need to bring it in the first time to establish our rewrite
if (!mWallpaperMap.contains(userId)) {
@@ -2111,8 +2107,14 @@
withCleanCallingIdentity(() -> clearWallpaperComponentLocked(wallpaper));
}
- // TODO(b/266818039) remove this version of the method
private void clearWallpaperLocked(int which, int userId, IRemoteCallback reply) {
+
+ if (mIsLockscreenLiveWallpaperEnabled) {
+ String callingPackage = mPackageManagerInternal.getNameForUid(getCallingUid());
+ clearWallpaperLocked(callingPackage, which, userId, reply);
+ return;
+ }
+
if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
}
@@ -3284,15 +3286,21 @@
boolean setWallpaperComponent(ComponentName name, String callingPackage,
@SetWallpaperFlags int which, int userId) {
if (mIsLockscreenLiveWallpaperEnabled) {
- return setWallpaperComponentInternal(name, callingPackage, which, userId);
+ return setWallpaperComponentInternal(name, callingPackage, which, userId, null);
} else {
setWallpaperComponentInternalLegacy(name, callingPackage, which, userId);
return true;
}
}
+ private boolean setWallpaperComponent(ComponentName name, @SetWallpaperFlags int which,
+ int userId) {
+ String callingPackage = mPackageManagerInternal.getNameForUid(getCallingUid());
+ return setWallpaperComponentInternal(name, callingPackage, which, userId, null);
+ }
+
private boolean setWallpaperComponentInternal(ComponentName name, String callingPackage,
- @SetWallpaperFlags int which, int userIdIn) {
+ @SetWallpaperFlags int which, int userIdIn, IRemoteCallback reply) {
if (DEBUG) {
Slog.v(TAG, "Setting new live wallpaper: which=" + which + ", component: " + name);
}
@@ -3341,6 +3349,7 @@
Slog.d(TAG, "publish system wallpaper changed!");
}
liveSync.complete();
+ if (reply != null) reply.sendResult(null);
}
};
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index 70f2007..cdd1a269 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -161,9 +161,7 @@
final boolean visible = (window.inputConfig & InputConfig.NOT_VISIBLE) == 0;
final boolean isNotClone = (window.inputConfig & InputConfig.CLONE) == 0;
final boolean hasTouchableRegion = !window.touchableRegion.isEmpty();
- final boolean hasNonEmptyFrame =
- (window.frameBottom != window.frameTop) && (window.frameLeft
- != window.frameRight);
+ final boolean hasNonEmptyFrame = !window.frame.isEmpty();
if (visible && isNotClone && hasTouchableRegion && hasNonEmptyFrame) {
tempVisibleWindows.add(window);
}
@@ -694,9 +692,7 @@
instance.mIgnoreDuetoRecentsAnimation = windowState != null && controller != null
&& controller.shouldIgnoreForAccessibility(windowState);
- final Rect windowFrame = new Rect(inputWindowHandle.frameLeft,
- inputWindowHandle.frameTop, inputWindowHandle.frameRight,
- inputWindowHandle.frameBottom);
+ final Rect windowFrame = new Rect(inputWindowHandle.frame);
getTouchableRegionInWindow(instance.mShouldMagnify, inputWindowHandle.touchableRegion,
instance.mTouchableRegionInWindow, windowFrame, magnificationInverseMatrix,
displayMatrix);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3125518..64c7c6f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -224,6 +224,9 @@
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
+import static com.android.server.wm.StartingData.AFTER_TRANSACTION_COPY_TO_CLIENT;
+import static com.android.server.wm.StartingData.AFTER_TRANSACTION_IDLE;
+import static com.android.server.wm.StartingData.AFTER_TRANSACTION_REMOVE_DIRECTLY;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
@@ -2690,6 +2693,11 @@
if (isTransferringSplashScreen()) {
return true;
}
+ // Only do transfer after transaction has done when starting window exist.
+ if (mStartingData != null && mStartingData.mWaitForSyncTransactionCommit) {
+ mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_COPY_TO_CLIENT;
+ return true;
+ }
requestCopySplashScreen();
return isTransferringSplashScreen();
}
@@ -2850,11 +2858,14 @@
if (mStartingData == null) {
return;
}
- mStartingData.mWaitForSyncTransactionCommit = false;
- if (mStartingData.mRemoveAfterTransaction) {
- mStartingData.mRemoveAfterTransaction = false;
- removeStartingWindowAnimation(mStartingData.mPrepareRemoveAnimation);
+ final StartingData lastData = mStartingData;
+ lastData.mWaitForSyncTransactionCommit = false;
+ if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_REMOVE_DIRECTLY) {
+ removeStartingWindowAnimation(lastData.mPrepareRemoveAnimation);
+ } else if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_COPY_TO_CLIENT) {
+ removeStartingWindow();
}
+ lastData.mRemoveAfterTransaction = AFTER_TRANSACTION_IDLE;
}
void removeStartingWindowAnimation(boolean prepareAnimation) {
@@ -2881,7 +2892,7 @@
if (mStartingData != null) {
if (mStartingData.mWaitForSyncTransactionCommit
|| mTransitionController.inCollectingTransition(startingWindow)) {
- mStartingData.mRemoveAfterTransaction = true;
+ mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY;
mStartingData.mPrepareRemoveAnimation = prepareAnimation;
return;
}
@@ -4370,7 +4381,6 @@
// Reset the last saved PiP snap fraction on removal.
mDisplayContent.mPinnedTaskController.onActivityHidden(mActivityComponent);
mDisplayContent.onRunningActivityChanged();
- mWmService.mEmbeddedWindowController.onActivityRemoved(this);
mRemovingFromDisplay = false;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 6c848d1..fb62412 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2606,9 +2606,6 @@
final int callingUid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- // When a task is locked, dismiss the root pinned task if it exists
- mRootWindowContainer.removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
-
getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 2eceecc..0250475 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -172,10 +172,9 @@
if (recents != null && recents.isNavigationBarAttachedToApp()) {
return;
}
- } else if (navigationBarCanMove || mTransitionOp == OP_CHANGE_MAY_SEAMLESS) {
+ } else if (navigationBarCanMove || mTransitionOp == OP_CHANGE_MAY_SEAMLESS
+ || mDisplayContent.mTransitionController.mNavigationBarAttachedToApp) {
action = Operation.ACTION_SEAMLESS;
- } else if (mDisplayContent.mTransitionController.mNavigationBarAttachedToApp) {
- return;
}
mTargetWindowTokens.put(w.mToken, new Operation(action));
return;
@@ -294,6 +293,11 @@
finishOp(mTargetWindowTokens.keyAt(i));
}
mTargetWindowTokens.clear();
+ onAllCompleted();
+ }
+
+ private void onAllCompleted() {
+ if (DEBUG) Slog.d(TAG, "onAllCompleted");
if (mTimeoutRunnable != null) {
mService.mH.removeCallbacks(mTimeoutRunnable);
}
@@ -333,7 +337,7 @@
if (DEBUG) Slog.d(TAG, "Complete directly " + token.getTopChild());
finishOp(token);
if (mTargetWindowTokens.isEmpty()) {
- if (mTimeoutRunnable != null) mService.mH.removeCallbacks(mTimeoutRunnable);
+ onAllCompleted();
return true;
}
}
@@ -411,14 +415,18 @@
if (mDisplayContent.mInputMethodWindow == null) return;
final WindowToken imeWindowToken = mDisplayContent.mInputMethodWindow.mToken;
if (isTargetToken(imeWindowToken)) return;
+ hideImmediately(imeWindowToken, Operation.ACTION_TOGGLE_IME);
+ if (DEBUG) Slog.d(TAG, "hideImeImmediately " + imeWindowToken.getTopChild());
+ }
+
+ private void hideImmediately(WindowToken token, @Operation.Action int action) {
final boolean original = mHideImmediately;
mHideImmediately = true;
- final Operation op = new Operation(Operation.ACTION_TOGGLE_IME);
- mTargetWindowTokens.put(imeWindowToken, op);
- fadeWindowToken(false /* show */, imeWindowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
- op.mLeash = imeWindowToken.getAnimationLeash();
+ final Operation op = new Operation(action);
+ mTargetWindowTokens.put(token, op);
+ fadeWindowToken(false /* show */, token, ANIMATION_TYPE_TOKEN_TRANSFORM);
+ op.mLeash = token.getAnimationLeash();
mHideImmediately = original;
- if (DEBUG) Slog.d(TAG, "hideImeImmediately " + imeWindowToken.getTopChild());
}
/** Returns {@code true} if the window will rotate independently. */
@@ -428,11 +436,20 @@
|| isTargetToken(w.mToken);
}
- /** Returns {@code true} if the controller will run fade animations on the window. */
+ /**
+ * Returns {@code true} if the rotation transition appearance of the window is currently
+ * managed by this controller.
+ */
boolean isTargetToken(WindowToken token) {
return mTargetWindowTokens.containsKey(token);
}
+ /** Returns {@code true} if the controller will run fade animations on the window. */
+ boolean hasFadeOperation(WindowToken token) {
+ final Operation op = mTargetWindowTokens.get(token);
+ return op != null && op.mAction == Operation.ACTION_FADE;
+ }
+
/**
* Whether the insets animation leash should use previous position when running fade animation
* or seamless transformation in a rotated display.
@@ -564,7 +581,18 @@
return false;
}
final Operation op = mTargetWindowTokens.get(w.mToken);
- if (op == null) return false;
+ if (op == null) {
+ // If a window becomes visible after the rotation transition is requested but before
+ // the transition is ready, hide it by an animation leash so it won't be flickering
+ // by drawing the rotated content before applying projection transaction of display.
+ // And it will fade in after the display transition is finished.
+ if (mTransitionOp == OP_APP_SWITCH && !mIsStartTransactionCommitted
+ && canBeAsync(w.mToken)) {
+ hideImmediately(w.mToken, Operation.ACTION_FADE);
+ if (DEBUG) Slog.d(TAG, "Hide on finishDrawing " + w.mToken.getTopChild());
+ }
+ return false;
+ }
if (DEBUG) Slog.d(TAG, "handleFinishDrawing " + w);
if (postDrawTransaction == null || !mIsSyncDrawRequested
|| canDrawBeforeStartTransaction(op)) {
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 98027bb..c9bae12 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -135,19 +135,6 @@
return mWindowsByWindowToken.get(windowToken);
}
- void onActivityRemoved(ActivityRecord activityRecord) {
- for (int i = mWindows.size() - 1; i >= 0; i--) {
- final EmbeddedWindow window = mWindows.valueAt(i);
- if (window.mHostActivityRecord == activityRecord) {
- final WindowProcessController processController =
- mAtmService.getProcessController(window.mOwnerPid, window.mOwnerUid);
- if (processController != null) {
- processController.removeHostActivity(activityRecord);
- }
- }
- }
- }
-
static class EmbeddedWindow implements InputTarget {
final IWindow mClient;
@Nullable final WindowState mHostWindowState;
@@ -230,6 +217,13 @@
mInputChannel.dispose();
mInputChannel = null;
}
+ if (mHostActivityRecord != null) {
+ final WindowProcessController wpc =
+ mWmService.mAtmService.getProcessController(mOwnerPid, mOwnerUid);
+ if (wpc != null) {
+ wpc.removeHostActivity(mHostActivityRecord);
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 3d4e0eb..64b7a60 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -227,18 +227,6 @@
mChanged = true;
}
- void setFrame(int left, int top, int right, int bottom) {
- if (mHandle.frameLeft == left && mHandle.frameTop == top && mHandle.frameRight == right
- && mHandle.frameBottom == bottom) {
- return;
- }
- mHandle.frameLeft = left;
- mHandle.frameTop = top;
- mHandle.frameRight = right;
- mHandle.frameBottom = bottom;
- mChanged = true;
- }
-
void setSurfaceInset(int inset) {
if (mHandle.surfaceInset == inset) {
return;
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 0c98fb5..0f9998c 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Context.DEVICE_POLICY_SERVICE;
import static android.content.Context.STATUS_BAR_SERVICE;
@@ -669,6 +670,9 @@
}
}
+ // When a task is locked, dismiss the root pinned task if it exists
+ mSupervisor.mRootWindowContainer.removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
+
// System can only initiate screen pinning, not full lock task mode
ProtoLog.w(WM_DEBUG_LOCKTASK, "%s", isSystemCaller ? "Locking pinned" : "Locking fully");
setLockTaskMode(task, isSystemCaller ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED,
diff --git a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
index 2e5474e..79b26d2 100644
--- a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
@@ -86,7 +86,7 @@
ANIMATION_TYPE_TOKEN_TRANSFORM);
if (controller == null) {
fadeAnim.run();
- } else if (!controller.isTargetToken(mNavigationBar.mToken)) {
+ } else if (!controller.hasFadeOperation(mNavigationBar.mToken)) {
// If fade rotation animation is running and the nav bar is not controlled by it:
// - For fade-in animation, defer the animation until fade rotation animation finishes.
// - For fade-out animation, just play the animation.
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 2378469..def32a1 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -40,6 +40,7 @@
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -546,7 +547,7 @@
contentInsets = mTmpRect;
}
mRunner.onAnimationStart(mController, appTargets, wallpaperTargets, contentInsets,
- null);
+ null, new Bundle());
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"startAnimation(): Notify animation start: %s",
mPendingAnimations.stream()
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 34806bd..a23547e 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import android.annotation.IntDef;
+
import com.android.server.wm.StartingSurfaceController.StartingSurface;
/**
@@ -23,6 +25,20 @@
*/
public abstract class StartingData {
+ /** Nothing need to do after transaction */
+ static final int AFTER_TRANSACTION_IDLE = 0;
+ /** Remove the starting window directly after transaction done. */
+ static final int AFTER_TRANSACTION_REMOVE_DIRECTLY = 1;
+ /** Do copy splash screen to client after transaction done. */
+ static final int AFTER_TRANSACTION_COPY_TO_CLIENT = 2;
+
+ @IntDef(prefix = { "AFTER_TRANSACTION" }, value = {
+ AFTER_TRANSACTION_IDLE,
+ AFTER_TRANSACTION_REMOVE_DIRECTLY,
+ AFTER_TRANSACTION_COPY_TO_CLIENT,
+ })
+ @interface AfterTransaction {}
+
protected final WindowManagerService mService;
protected final int mTypeParams;
@@ -60,7 +76,7 @@
* This starting window should be removed after applying the start transaction of transition,
* which ensures the app window has shown.
*/
- boolean mRemoveAfterTransaction;
+ @AfterTransaction int mRemoveAfterTransaction;
/** Whether to prepare the removal animation. */
boolean mPrepareRemoveAnimation;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1566bb2c..e9af42b 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1930,11 +1930,6 @@
break;
}
- final AsyncRotationController asyncRotationController = dc.getAsyncRotationController();
- if (asyncRotationController != null) {
- asyncRotationController.accept(navWindow);
- }
-
if (animate) {
final NavBarFadeAnimationController controller =
new NavBarFadeAnimationController(dc);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 6432ff0..6ede345 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -1124,14 +1124,15 @@
+ "track #%d", transition.getSyncId(), track);
}
}
- if (sync) {
+ transition.mAnimationTrack = track;
+ info.setTrack(track);
+ mTrackCount = Math.max(mTrackCount, track + 1);
+ if (sync && mTrackCount > 1) {
+ // If there are >1 tracks, mark as sync so that all tracks finish.
info.setFlags(info.getFlags() | TransitionInfo.FLAG_SYNC);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Marking #%d animation as SYNC.",
transition.getSyncId());
}
- transition.mAnimationTrack = track;
- info.setTrack(track);
- mTrackCount = Math.max(mTrackCount, track + 1);
}
void updateAnimatingState(SurfaceControl.Transaction t) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5a45fe1..a172d99 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -30,7 +30,6 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.SurfaceControl.Transaction;
import static android.view.SurfaceControl.getGlobalTransaction;
-import static android.view.ViewRootImpl.LOCAL_LAYOUT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -1446,9 +1445,7 @@
}
final boolean dragResizingChanged = !mDragResizingChangeReported && isDragResizeChanged();
-
- final boolean attachedFrameChanged = LOCAL_LAYOUT
- && mLayoutAttached && getParentWindow().frameChanged();
+ final boolean attachedFrameChanged = mLayoutAttached && getParentWindow().frameChanged();
if (DEBUG) {
Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 405b133..2f150a1 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -50,6 +50,7 @@
"com_android_server_locksettings_SyntheticPasswordManager.cpp",
"com_android_server_power_PowerManagerService.cpp",
"com_android_server_powerstats_PowerStatsService.cpp",
+ "com_android_server_power_stats_CpuPowerStatsCollector.cpp",
"com_android_server_hint_HintManagerService.cpp",
"com_android_server_SerialService.cpp",
"com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
@@ -144,6 +145,7 @@
"libsensorservicehidl",
"libsensorserviceaidl",
"libgui",
+ "libtimeinstate",
"libtimestats_atoms_proto",
"libusbhost",
"libtinyalsa",
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index d9acf41..7e8ce60 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -23,6 +23,7 @@
per-file com_android_server_pm_* = file:/services/core/java/com/android/server/pm/OWNERS
per-file com_android_server_power_* = file:/services/core/java/com/android/server/power/OWNERS
per-file com_android_server_powerstats_* = file:/services/core/java/com/android/server/powerstats/OWNERS
+per-file com_android_server_power_stats_* = file:/BATTERY_STATS_OWNERS
per-file com_android_server_security_* = file:/core/java/android/security/OWNERS
per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS
per-file com_android_server_vibrator_* = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index cfc63f0..7b08413 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -54,6 +54,9 @@
using android::meminfo::ProcMemInfo;
using namespace android::meminfo;
+static const size_t kPageSize = getpagesize();
+static const size_t kPageMask = ~(kPageSize - 1);
+
#define COMPACT_ACTION_FILE_FLAG 1
#define COMPACT_ACTION_ANON_FLAG 2
@@ -64,7 +67,7 @@
#define ASYNC_RECEIVED_WHILE_FROZEN (2)
#define TXNS_PENDING_WHILE_FROZEN (4)
-#define MAX_RW_COUNT (INT_MAX & PAGE_MASK)
+#define MAX_RW_COUNT (INT_MAX & kPageMask)
// Defines the maximum amount of VMAs we can send per process_madvise syscall.
// Currently this is set to UIO_MAXIOV which is the maximum segments allowed by
@@ -233,7 +236,6 @@
// process_madvise on failure
int madviseVmasFromBatch(unique_fd& pidfd, VmaBatch& batch, int madviseType,
uint64_t* outBytesProcessed) {
- static const size_t kPageSize = getpagesize();
if (batch.totalVmas == 0 || batch.totalBytes == 0) {
// No VMAs in Batch, skip.
*outBytesProcessed = 0;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 7c8c558..5ab8d36 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -400,7 +400,7 @@
PointerCaptureRequest pointerCaptureRequest{};
// Sprite controller singleton, created on first use.
- sp<SpriteController> spriteController{};
+ std::shared_ptr<SpriteController> spriteController{};
// Pointer controller singleton, created and destroyed as needed.
std::weak_ptr<PointerController> pointerController{};
@@ -709,7 +709,7 @@
if (controller == nullptr) {
ensureSpriteControllerLocked();
- controller = PointerController::create(this, mLooper, mLocked.spriteController);
+ controller = PointerController::create(this, mLooper, *mLocked.spriteController);
mLocked.pointerController = controller;
updateInactivityTimeoutLocked();
}
@@ -738,16 +738,21 @@
}
void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
- if (mLocked.spriteController == nullptr) {
- JNIEnv* env = jniEnv();
- jint layer = env->CallIntMethod(mServiceObj, gServiceClassInfo.getPointerLayer);
- if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
- layer = -1;
- }
- mLocked.spriteController = new SpriteController(mLooper, layer, [this](int displayId) {
- return getParentSurfaceForPointers(displayId);
- });
+ if (mLocked.spriteController) {
+ return;
}
+ JNIEnv* env = jniEnv();
+ jint layer = env->CallIntMethod(mServiceObj, gServiceClassInfo.getPointerLayer);
+ if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
+ layer = -1;
+ }
+ mLocked.spriteController =
+ std::make_shared<SpriteController>(mLooper, layer, [this](int displayId) {
+ return getParentSurfaceForPointers(displayId);
+ });
+ // The SpriteController needs to be shared pointer because the handler callback needs to hold
+ // a weak reference so that we can avoid racy conditions when the controller is being destroyed.
+ mLocked.spriteController->setHandlerController(mLocked.spriteController);
}
void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
diff --git a/services/core/jni/com_android_server_power_stats_CpuPowerStatsCollector.cpp b/services/core/jni/com_android_server_power_stats_CpuPowerStatsCollector.cpp
new file mode 100644
index 0000000..a6084ea
--- /dev/null
+++ b/services/core/jni/com_android_server_power_stats_CpuPowerStatsCollector.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CpuPowerStatsCollector"
+
+#include <cputimeinstate.h>
+#include <log/log.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+
+#include "core_jni_helpers.h"
+
+#define EXCEPTION (-1)
+
+namespace android {
+
+#define JAVA_CLASS_CPU_POWER_STATS_COLLECTOR "com/android/server/power/stats/CpuPowerStatsCollector"
+#define JAVA_CLASS_KERNEL_CPU_STATS_READER \
+ JAVA_CLASS_CPU_POWER_STATS_COLLECTOR "$KernelCpuStatsReader"
+#define JAVA_CLASS_KERNEL_CPU_STATS_CALLBACK \
+ JAVA_CLASS_CPU_POWER_STATS_COLLECTOR "$KernelCpuStatsCallback"
+
+static constexpr uint64_t NSEC_PER_MSEC = 1000000;
+
+static int extractUidStats(JNIEnv *env, std::vector<std::vector<uint64_t>> ×,
+ ScopedIntArrayRO &scopedScalingStepToPowerBracketMap,
+ jlongArray tempForUidStats);
+
+static bool initialized = false;
+static jclass class_KernelCpuStatsCallback;
+static jmethodID method_KernelCpuStatsCallback_processUidStats;
+
+static int init(JNIEnv *env) {
+ jclass temp = env->FindClass(JAVA_CLASS_KERNEL_CPU_STATS_CALLBACK);
+ class_KernelCpuStatsCallback = (jclass)env->NewGlobalRef(temp);
+ if (!class_KernelCpuStatsCallback) {
+ jniThrowExceptionFmt(env, "java/lang/ClassNotFoundException",
+ "Class not found: " JAVA_CLASS_KERNEL_CPU_STATS_CALLBACK);
+ return EXCEPTION;
+ }
+ method_KernelCpuStatsCallback_processUidStats =
+ env->GetMethodID(class_KernelCpuStatsCallback, "processUidStats", "(I[J)V");
+ if (!method_KernelCpuStatsCallback_processUidStats) {
+ jniThrowExceptionFmt(env, "java/lang/NoSuchMethodException",
+ "Method not found: " JAVA_CLASS_KERNEL_CPU_STATS_CALLBACK
+ ".processUidStats");
+ return EXCEPTION;
+ }
+ initialized = true;
+ return OK;
+}
+
+static jlong nativeReadCpuStats(JNIEnv *env, [[maybe_unused]] jobject zis, jobject callback,
+ jintArray scalingStepToPowerBracketMap,
+ jlong lastUpdateTimestampNanos, jlongArray tempForUidStats) {
+ if (!initialized) {
+ if (init(env) == EXCEPTION) {
+ return 0L;
+ }
+ }
+
+ uint64_t newLastUpdate = lastUpdateTimestampNanos;
+ auto data = android::bpf::getUidsUpdatedCpuFreqTimes(&newLastUpdate);
+ if (!data.has_value()) return lastUpdateTimestampNanos;
+
+ ScopedIntArrayRO scopedScalingStepToPowerBracketMap(env, scalingStepToPowerBracketMap);
+
+ for (auto &[uid, times] : *data) {
+ int status =
+ extractUidStats(env, times, scopedScalingStepToPowerBracketMap, tempForUidStats);
+ if (status == EXCEPTION) {
+ return 0L;
+ }
+ env->CallVoidMethod(callback, method_KernelCpuStatsCallback_processUidStats, (jint)uid,
+ tempForUidStats);
+ }
+ return newLastUpdate;
+}
+
+static int extractUidStats(JNIEnv *env, std::vector<std::vector<uint64_t>> ×,
+ ScopedIntArrayRO &scopedScalingStepToPowerBracketMap,
+ jlongArray tempForUidStats) {
+ ScopedLongArrayRW scopedTempForStats(env, tempForUidStats);
+ uint64_t *arrayForStats = reinterpret_cast<uint64_t *>(scopedTempForStats.get());
+ const uint8_t statsSize = scopedTempForStats.size();
+ memset(arrayForStats, 0, statsSize * sizeof(uint64_t));
+ const uint8_t scalingStepCount = scopedScalingStepToPowerBracketMap.size();
+
+ uint32_t scalingStep = 0;
+ for (const auto &subVec : times) {
+ for (uint32_t i = 0; i < subVec.size(); ++i) {
+ if (scalingStep >= scalingStepCount) {
+ jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
+ "scalingStepToPowerBracketMap is too short, "
+ "size=%u, scalingStep=%u",
+ scalingStepCount, scalingStep);
+ return EXCEPTION;
+ }
+ uint32_t bucket = scopedScalingStepToPowerBracketMap[scalingStep];
+ if (bucket >= statsSize) {
+ jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
+ "UidStats array is too short, length=%u, bucket[%u]=%u",
+ statsSize, scalingStep, bucket);
+ return EXCEPTION;
+ }
+ arrayForStats[bucket] += subVec[i] / NSEC_PER_MSEC;
+ scalingStep++;
+ }
+ }
+ return OK;
+}
+
+static const JNINativeMethod method_table[] = {
+ {"nativeReadCpuStats", "(L" JAVA_CLASS_KERNEL_CPU_STATS_CALLBACK ";[IJ[J)J",
+ (void *)nativeReadCpuStats},
+};
+
+int register_android_server_power_stats_CpuPowerStatsCollector(JNIEnv *env) {
+ return jniRegisterNativeMethods(env, JAVA_CLASS_KERNEL_CPU_STATS_READER, method_table,
+ NELEM(method_table));
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 290ad8d..97d7be6 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -30,6 +30,7 @@
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_PowerStatsService(JNIEnv* env);
+int register_android_server_power_stats_CpuPowerStatsCollector(JNIEnv* env);
int register_android_server_HintManagerService(JNIEnv* env);
int register_android_server_storage_AppFuse(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
@@ -85,6 +86,7 @@
register_android_server_broadcastradio_Tuner(vm, env);
register_android_server_PowerManagerService(env);
register_android_server_PowerStatsService(env);
+ register_android_server_power_stats_CpuPowerStatsCollector(env);
register_android_server_HintManagerService(env);
register_android_server_SerialService(env);
register_android_server_InputManager(env);
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
index 14b4dc3..2db2438 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
@@ -27,6 +27,7 @@
import android.util.Log;
import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import com.android.server.testing.shadows.ShadowEventLog;
import com.android.server.testing.shadows.ShadowSlog;
@@ -46,10 +47,13 @@
@Mock private IBackupManagerMonitor mMonitor;
private KeyValueBackupReporter mReporter;
+ private BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
@Before
public void setUp() {
- mReporter = new KeyValueBackupReporter(mBackupManagerService, mObserver, mMonitor);
+ mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(mMonitor);
+ mReporter = new KeyValueBackupReporter(
+ mBackupManagerService, mObserver, mBackupManagerMonitorEventSender);
}
@Test
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index bfbc0f5..7349c14 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -122,6 +122,7 @@
import com.android.server.backup.testing.TransportTestUtils;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.backup.utils.BackupEligibilityRules;
+import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import com.android.server.testing.shadows.FrameworkShadowLooper;
import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import com.android.server.testing.shadows.ShadowBackupDataInput;
@@ -260,7 +261,8 @@
mBackupHandler = mBackupManagerService.getBackupHandler();
mShadowBackupLooper = shadowOf(mBackupHandler.getLooper());
ShadowEventLog.setUp();
- mReporter = spy(new KeyValueBackupReporter(mBackupManagerService, mObserver, mMonitor));
+ mReporter = spy(new KeyValueBackupReporter(mBackupManagerService, mObserver,
+ new BackupManagerMonitorEventSender(mMonitor)));
when(mPackageManagerInternal.getApplicationEnabledState(any(), anyInt()))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
index 74dc853..7552800 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
@@ -56,7 +56,7 @@
import android.os.RemoteException;
import android.os.StatFs;
import android.os.SystemClock;
-import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.Postsubmit;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
@@ -93,7 +93,7 @@
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
-@Presubmit
+@Postsubmit
public class PackageManagerTests extends AndroidTestCase {
private static final boolean localLOGV = true;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
new file mode 100644
index 0000000..4fd8f26
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.display;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for the {@link DisplayDevice} class.
+ *
+ * Build/Install/Run:
+ * atest DisplayServicesTests:DisplayDeviceTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class DisplayDeviceTest {
+ private final DisplayDeviceInfo mDisplayDeviceInfo = new DisplayDeviceInfo();
+ private static final int WIDTH = 500;
+ private static final int HEIGHT = 900;
+ private static final Point PORTRAIT_SIZE = new Point(WIDTH, HEIGHT);
+ private static final Point LANDSCAPE_SIZE = new Point(HEIGHT, WIDTH);
+
+ @Mock
+ private SurfaceControl.Transaction mMockTransaction;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mDisplayDeviceInfo.width = WIDTH;
+ mDisplayDeviceInfo.height = HEIGHT;
+ mDisplayDeviceInfo.rotation = ROTATION_0;
+ }
+
+ @Test
+ public void testGetDisplaySurfaceDefaultSizeLocked_notRotated() {
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
+ }
+
+ @Test
+ public void testGetDisplaySurfaceDefaultSizeLocked_rotation0() {
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ displayDevice.setProjectionLocked(mMockTransaction, ROTATION_0, new Rect(), new Rect());
+ assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
+ }
+
+ @Test
+ public void testGetDisplaySurfaceDefaultSizeLocked_rotation90() {
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ displayDevice.setProjectionLocked(mMockTransaction, ROTATION_90, new Rect(), new Rect());
+ assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
+ }
+
+ @Test
+ public void testGetDisplaySurfaceDefaultSizeLocked_rotation180() {
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ displayDevice.setProjectionLocked(mMockTransaction, ROTATION_180, new Rect(), new Rect());
+ assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(PORTRAIT_SIZE);
+ }
+
+ @Test
+ public void testGetDisplaySurfaceDefaultSizeLocked_rotation270() {
+ DisplayDevice displayDevice = new FakeDisplayDevice(mDisplayDeviceInfo);
+ displayDevice.setProjectionLocked(mMockTransaction, ROTATION_270, new Rect(), new Rect());
+ assertThat(displayDevice.getDisplaySurfaceDefaultSizeLocked()).isEqualTo(LANDSCAPE_SIZE);
+ }
+
+ private static class FakeDisplayDevice extends DisplayDevice {
+ private final DisplayDeviceInfo mDisplayDeviceInfo;
+
+ FakeDisplayDevice(DisplayDeviceInfo displayDeviceInfo) {
+ super(null, null, "", InstrumentationRegistry.getInstrumentation().getContext());
+ mDisplayDeviceInfo = displayDeviceInfo;
+ }
+
+ @Override
+ public boolean hasStableUniqueId() {
+ return false;
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ return mDisplayDeviceInfo;
+ }
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index f975b6f..183a84d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -64,7 +64,9 @@
private static final int AMBIENT_COLOR_TYPE = 20705;
private static final String AMBIENT_COLOR_TYPE_STR = "colorSensoryDensoryDoc";
private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5432.1f;
+ private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG = 5555.5f;
private static final float HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE = 3456.7f;
+ private static final float HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG = 3333.3f;
private Handler mHandler = new Handler(Looper.getMainLooper());
private Sensor mLightSensor;
@@ -78,6 +80,10 @@
@Mock private TypedArray mBiases;
@Mock private TypedArray mHighLightBrightnesses;
@Mock private TypedArray mHighLightBiases;
+ @Mock private TypedArray mBrightnessesStrong;
+ @Mock private TypedArray mBiasesStrong;
+ @Mock private TypedArray mHighLightBrightnessesStrong;
+ @Mock private TypedArray mHighLightBiasesStrong;
@Mock private TypedArray mAmbientColorTemperatures;
@Mock private TypedArray mDisplayColorTemperatures;
@Mock private TypedArray mStrongAmbientColorTemperatures;
@@ -108,6 +114,10 @@
LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE);
mockResourcesFloat(R.dimen.config_displayWhiteBalanceHighLightAmbientColorTemperature,
HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE);
+ mockResourcesFloat(R.dimen.config_displayWhiteBalanceLowLightAmbientColorTemperatureStrong,
+ LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG);
+ mockResourcesFloat(R.dimen.config_displayWhiteBalanceHighLightAmbientColorTemperatureStrong,
+ HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceAmbientColorTemperatures))
.thenReturn(mAmbientColorTemperatures);
@@ -133,6 +143,18 @@
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceHighLightAmbientBiases))
.thenReturn(mHighLightBiases);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceLowLightAmbientBrightnessesStrong))
+ .thenReturn(mBrightnessesStrong);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceLowLightAmbientBiasesStrong))
+ .thenReturn(mBiasesStrong);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceHighLightAmbientBrightnessesStrong))
+ .thenReturn(mHighLightBrightnessesStrong);
+ when(mResourcesSpy.obtainTypedArray(
+ R.array.config_displayWhiteBalanceHighLightAmbientBiasesStrong))
+ .thenReturn(mHighLightBiasesStrong);
mockThrottler();
LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
@@ -388,8 +410,8 @@
public void testStrongMode() {
final float lowerBrightness = 10.0f;
final float upperBrightness = 50.0f;
- setBrightnesses(lowerBrightness, upperBrightness);
- setBiases(0.0f, 1.0f);
+ setBrightnessesStrong(lowerBrightness, upperBrightness);
+ setBiasesStrong(0.0f, 1.0f);
final int ambientColorTempLow = 6000;
final int ambientColorTempHigh = 8000;
final int displayColorTempLow = 6400;
@@ -413,7 +435,7 @@
setEstimatedBrightnessAndUpdate(controller,
mix(lowerBrightness, upperBrightness, brightnessFraction));
assertEquals(controller.mPendingAmbientColorTemperature,
- mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE,
+ mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE_STRONG,
mix(displayColorTempLow, displayColorTempHigh, ambientTempFraction),
brightnessFraction),
ALLOWED_ERROR_DELTA);
@@ -458,7 +480,7 @@
assertEquals(-1.0f, controller.mPendingAmbientColorTemperature, 0);
}
- void mockThrottler() {
+ private void mockThrottler() {
when(mResourcesSpy.getInteger(
R.integer.config_displayWhiteBalanceDecreaseDebounce)).thenReturn(0);
when(mResourcesSpy.getInteger(
@@ -513,10 +535,18 @@
setFloatArrayResource(mBrightnesses, vals);
}
+ private void setBrightnessesStrong(float... vals) {
+ setFloatArrayResource(mBrightnessesStrong, vals);
+ }
+
private void setBiases(float... vals) {
setFloatArrayResource(mBiases, vals);
}
+ private void setBiasesStrong(float... vals) {
+ setFloatArrayResource(mBiasesStrong, vals);
+ }
+
private void setHighLightBrightnesses(float... vals) {
setFloatArrayResource(mHighLightBrightnesses, vals);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
index de27d77..e672928 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -102,7 +102,7 @@
).when(() -> Settings.Global.getString(any(), anyString()));
mTestMapper = new SettingsToPropertiesMapper(
- mMockContentResolver, TEST_MAPPING, new String[] {});
+ mMockContentResolver, TEST_MAPPING, new String[] {}, new String[] {});
}
@After
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 7eb78eb..65286d9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -62,7 +62,7 @@
import com.android.server.backup.transport.BackupTransportClient;
import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import com.google.common.collect.ImmutableSet;
@@ -98,6 +98,7 @@
@Mock LifecycleOperationStorage mOperationStorage;
@Mock JobScheduler mJobScheduler;
@Mock BackupHandler mBackupHandler;
+ @Mock BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
private TestableContext mContext;
private MockitoSession mSession;
@@ -107,7 +108,7 @@
public void setUp() throws Exception {
mSession = mockitoSession()
.initMocks(this)
- .mockStatic(BackupManagerMonitorUtils.class)
+ .mockStatic(BackupManagerMonitorEventSender.class)
.mockStatic(FeatureFlagUtils.class)
// TODO(b/263239775): Remove unnecessary stubbing.
.strictness(Strictness.LENIENT)
@@ -124,7 +125,7 @@
mService.setEnabled(true);
mService.setSetupComplete(true);
mService.enqueueFullBackup("com.test.backup.app", /* lastBackedUp= */ 0);
- }
+ }
@After
public void tearDown() {
@@ -297,9 +298,9 @@
new DataTypeResult(/* dataType */ "type_2"));
mService.reportDelayedRestoreResult(TEST_PACKAGE, results);
- verify(() -> BackupManagerMonitorUtils.sendAgentLoggingResults(
- eq(mBackupManagerMonitor), eq(packageInfo), eq(results), eq(
- BackupAnnotations.OperationType.RESTORE)));
+
+ verify(mBackupManagerMonitorEventSender).sendAgentLoggingResults(
+ eq(packageInfo), eq(results), eq(BackupAnnotations.OperationType.RESTORE));
}
private static PackageInfo getPackageInfo(String packageName) {
@@ -309,7 +310,7 @@
return packageInfo;
}
- private static class TestBackupService extends UserBackupManagerService {
+ private class TestBackupService extends UserBackupManagerService {
boolean isEnabledStatePersisted = false;
boolean shouldUseNewBackupEligibilityRules = false;
@@ -356,6 +357,11 @@
return mWorkerThread;
}
+ @Override
+ BackupManagerMonitorEventSender getBMMEventSender(IBackupManagerMonitor monitor) {
+ return mBackupManagerMonitorEventSender;
+ }
+
private void waitForAsyncOperation() {
if (mWorkerThread == null) {
return;
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
similarity index 80%
rename from services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
rename to services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
index 093ad3c..9d9b03f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
@@ -34,7 +34,6 @@
import static org.mockito.Mockito.verify;
import android.app.IBackupAgent;
-import android.app.backup.BackupAnnotations;
import android.app.backup.BackupAnnotations.OperationType;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupRestoreEventLogger;
@@ -62,39 +61,43 @@
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class BackupManagerMonitorUtilsTest {
+public class BackupManagerMonitorEventSenderTest {
@Mock private IBackupManagerMonitor mMonitorMock;
+ private BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(mMonitorMock);
}
@Test
public void monitorEvent_monitorIsNull_returnsNull() throws Exception {
- IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(null, 0, null, 0,
- null);
+ mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(/* monitor */ null);
+ mBackupManagerMonitorEventSender.monitorEvent(0, null, 0, null);
+ IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
- assertThat(result).isNull();
+ assertThat(monitor).isNull();
}
@Test
public void monitorEvent_monitorOnEventThrows_returnsNull() throws Exception {
doThrow(new RemoteException()).when(mMonitorMock).onEvent(any(Bundle.class));
- IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(mMonitorMock, 0, null,
- 0, null);
+ mBackupManagerMonitorEventSender.monitorEvent(0, null, 0, null);
+ IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
verify(mMonitorMock).onEvent(any(Bundle.class));
- assertThat(result).isNull();
+ assertThat(monitor).isNull();
}
@Test
public void monitorEvent_packageAndExtrasAreNull_fillsBundleCorrectly() throws Exception {
- IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(mMonitorMock, 1, null,
- 2, null);
+ mBackupManagerMonitorEventSender.monitorEvent(1, null, 2, null);
+ IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
- assertThat(result).isEqualTo(mMonitorMock);
+ assertThat(monitor).isEqualTo(mMonitorMock);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mMonitorMock).onEvent(bundleCaptor.capture());
Bundle eventBundle = bundleCaptor.getValue();
@@ -112,10 +115,10 @@
extras.putInt("key1", 4);
extras.putString("key2", "value2");
- IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(mMonitorMock, 1,
- packageInfo, 2, extras);
+ mBackupManagerMonitorEventSender.monitorEvent(1, packageInfo, 2, extras);
+ IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
- assertThat(result).isEqualTo(mMonitorMock);
+ assertThat(monitor).isEqualTo(mMonitorMock);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mMonitorMock).onEvent(bundleCaptor.capture());
Bundle eventBundle = bundleCaptor.getValue();
@@ -130,7 +133,8 @@
}
@Test
- public void monitorEvent_packageAndExtrasAreNotNull_fillsBundleCorrectlyLong() throws Exception {
+ public void monitorEvent_packageAndExtrasAreNotNull_fillsBundleCorrectlyLong()
+ throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test.package";
packageInfo.versionCode = 3;
@@ -139,10 +143,10 @@
extras.putInt("key1", 4);
extras.putString("key2", "value2");
- IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(mMonitorMock, 1,
- packageInfo, 2, extras);
+ mBackupManagerMonitorEventSender.monitorEvent(1, packageInfo, 2, extras);
+ IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
- assertThat(result).isEqualTo(mMonitorMock);
+ assertThat(monitor).isEqualTo(mMonitorMock);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mMonitorMock).onEvent(bundleCaptor.capture());
Bundle eventBundle = bundleCaptor.getValue();
@@ -164,9 +168,9 @@
// Mock an agent that returns a logging result.
IBackupAgent agent = setUpLoggingAgentForOperation(OperationType.BACKUP);
- IBackupManagerMonitor monitor =
- BackupManagerMonitorUtils.monitorAgentLoggingResults(
- mMonitorMock, packageInfo, agent);
+
+ mBackupManagerMonitorEventSender.monitorAgentLoggingResults(packageInfo, agent);
+ IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
assertCorrectBundleSentToMonitor(monitor, OperationType.BACKUP);
}
@@ -178,9 +182,8 @@
// Mock an agent that returns a logging result.
IBackupAgent agent = setUpLoggingAgentForOperation(OperationType.RESTORE);
- IBackupManagerMonitor monitor =
- BackupManagerMonitorUtils.monitorAgentLoggingResults(
- mMonitorMock, packageInfo, agent);
+ mBackupManagerMonitorEventSender.monitorAgentLoggingResults(packageInfo, agent);
+ IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
assertCorrectBundleSentToMonitor(monitor, OperationType.RESTORE);
}
@@ -217,9 +220,9 @@
List<BackupRestoreEventLogger.DataTypeResult> loggingResults = new ArrayList<>();
loggingResults.add(new BackupRestoreEventLogger.DataTypeResult("testLoggingResult"));
- IBackupManagerMonitor monitor = BackupManagerMonitorUtils.sendAgentLoggingResults(
- mMonitorMock, packageInfo, loggingResults, OperationType.BACKUP);
-
+ mBackupManagerMonitorEventSender.sendAgentLoggingResults(
+ packageInfo, loggingResults, OperationType.BACKUP);
+ IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
assertCorrectBundleSentToMonitor(monitor, OperationType.BACKUP);
}
@@ -230,8 +233,9 @@
List<BackupRestoreEventLogger.DataTypeResult> loggingResults = new ArrayList<>();
loggingResults.add(new BackupRestoreEventLogger.DataTypeResult("testLoggingResult"));
- IBackupManagerMonitor monitor = BackupManagerMonitorUtils.sendAgentLoggingResults(
- mMonitorMock, packageInfo, loggingResults, OperationType.RESTORE);
+ mBackupManagerMonitorEventSender.sendAgentLoggingResults(
+ packageInfo, loggingResults, OperationType.RESTORE);
+ IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();
assertCorrectBundleSentToMonitor(monitor, OperationType.RESTORE);
}
@@ -262,7 +266,7 @@
public void putMonitoringExtraString_bundleExists_fillsBundleCorrectly() throws Exception {
Bundle bundle = new Bundle();
- Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(bundle, "key", "value");
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", "value");
assertThat(result).isEqualTo(bundle);
assertThat(result.size()).isEqualTo(1);
@@ -272,7 +276,7 @@
@Test
public void putMonitoringExtraString_bundleDoesNotExist_fillsBundleCorrectly()
throws Exception {
- Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(null, "key", "value");
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", "value");
assertThat(result).isNotNull();
assertThat(result.size()).isEqualTo(1);
@@ -284,7 +288,7 @@
public void putMonitoringExtraLong_bundleExists_fillsBundleCorrectly() throws Exception {
Bundle bundle = new Bundle();
- Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(bundle, "key", 123);
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", 123);
assertThat(result).isEqualTo(bundle);
assertThat(result.size()).isEqualTo(1);
@@ -293,7 +297,7 @@
@Test
public void putMonitoringExtraLong_bundleDoesNotExist_fillsBundleCorrectly() throws Exception {
- Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(null, "key", 123);
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", 123);
assertThat(result).isNotNull();
assertThat(result.size()).isEqualTo(1);
@@ -304,7 +308,7 @@
public void putMonitoringExtraBoolean_bundleExists_fillsBundleCorrectly() throws Exception {
Bundle bundle = new Bundle();
- Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(bundle, "key", true);
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", true);
assertThat(result).isEqualTo(bundle);
assertThat(result.size()).isEqualTo(1);
@@ -314,10 +318,10 @@
@Test
public void putMonitoringExtraBoolean_bundleDoesNotExist_fillsBundleCorrectly()
throws Exception {
- Bundle result = BackupManagerMonitorUtils.putMonitoringExtra(null, "key", true);
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", true);
assertThat(result).isNotNull();
assertThat(result.size()).isEqualTo(1);
assertThat(result.getBoolean("key")).isTrue();
}
-}
\ No newline at end of file
+}
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 78e5a42..bdbf4ec 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -63,6 +63,8 @@
import org.junit.Test;
import org.mockito.Mock;
+import java.io.File;
+
/**
* Run as {@code atest FrameworksMockingServicesTests:com.android.server.pm.UserManagerServiceTest}
*/
@@ -107,6 +109,8 @@
.getTargetContext();
private final SparseArray<UserData> mUsers = new SparseArray<>();
+ private File mTestDir;
+
private Context mSpiedContext;
private @Mock PackageManagerService mMockPms;
@@ -144,17 +148,23 @@
doNothing().when(mSpiedContext).sendBroadcastAsUser(any(), any(), any());
// Must construct UserManagerService in the UiThread
+ mTestDir = new File(mRealContext.getDataDir(), "umstest");
+ mTestDir.mkdirs();
mUms = new UserManagerService(mSpiedContext, mMockPms, mMockUserDataPreparer,
- mPackagesLock, mRealContext.getDataDir(), mUsers);
+ mPackagesLock, mTestDir, mUsers);
mUmi = LocalServices.getService(UserManagerInternal.class);
assertWithMessage("LocalServices.getService(UserManagerInternal.class)").that(mUmi)
.isNotNull();
}
@After
- public void resetUserManagerInternal() {
+ public void tearDown() {
// LocalServices follows the "Highlander rule" - There can be only one!
LocalServices.removeServiceForTest(UserManagerInternal.class);
+
+ // Clean up test dir to remove persisted user files.
+ assertThat(deleteRecursive(mTestDir)).isTrue();
+ mUsers.clear();
}
@Test
@@ -496,6 +506,14 @@
@Test
public void testMainUser_hasNoCallsOrSMSRestrictionsByDefault() {
+ // Remove the main user so we can add another one
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserData userData = mUsers.valueAt(i);
+ if (userData.info.isMain()) {
+ mUsers.delete(i);
+ break;
+ }
+ }
UserInfo mainUser = mUms.createUserWithThrow("main user", USER_TYPE_FULL_SECONDARY,
UserInfo.FLAG_FULL | UserInfo.FLAG_MAIN);
@@ -621,6 +639,18 @@
userData.mLastEnteredForegroundTimeMillis = timeMillis;
}
+ public boolean deleteRecursive(File file) {
+ if (file.isDirectory()) {
+ for (File item : file.listFiles()) {
+ boolean success = deleteRecursive(item);
+ if (!success) {
+ return false;
+ }
+ }
+ }
+ return file.delete();
+ }
+
private static final class TestUserData extends UserData {
@SuppressWarnings("deprecation")
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
index 35d4ffd..a140730 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -145,7 +145,7 @@
observer.onHealthCheckFailed(null,
PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1));
// non-native crash for the package
- assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_60,
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
observer.onHealthCheckFailed(testFailedPackage,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
// non-native crash for a different package
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index 05acd9b..8ab4507 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -23,6 +23,8 @@
"androidx.test.uiautomator_uiautomator",
"mockito-target-minus-junit4",
"servicestests-utils",
+ "platform-test-annotations",
+ "flag-junit",
],
libs: [
diff --git a/services/tests/powerstatstests/AndroidManifest.xml b/services/tests/powerstatstests/AndroidManifest.xml
index d3a88d2..d6898a1 100644
--- a/services/tests/powerstatstests/AndroidManifest.xml
+++ b/services/tests/powerstatstests/AndroidManifest.xml
@@ -21,6 +21,7 @@
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
<uses-permission android:name="android.permission.MANAGE_USERS"/>
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<queries>
<package android:name="com.android.coretests.apps.bstatstestapp" />
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
index 4fde73b..77124d0 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
@@ -32,6 +32,8 @@
import org.junit.runner.RunWith;
import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.Future;
@@ -49,6 +51,7 @@
@Before
public void setup() {
final File historyDir = createTemporaryDirectory(getClass().getSimpleName());
+ mMockClock.currentTime = 3000;
mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir);
mBatteryStats.setDummyExternalStatsSync(mExternalStatsSync);
mBatteryStats.setRecordAllHistoryLocked(true);
@@ -70,20 +73,10 @@
}
@Test
- public void testIterator() {
- mMockClock.realtime = 1000;
- mMockClock.uptime = 1000;
+ public void unconstrainedIteration() {
+ prepareHistory();
- mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
- 100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000,
- 1_000_000, 1_000_000);
- mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
- 100, /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0, 2_000_000,
- 2_000_000, 2_000_000);
- mBatteryStats.noteAlarmStartLocked("foo", null, APP_UID, 3_000_000, 2_000_000);
- mBatteryStats.noteAlarmFinishLocked("foo", null, APP_UID, 3_001_000, 2_001_000);
-
- final BatteryStatsHistoryIterator iterator = mBatteryStats.iterateBatteryStatsHistory();
+ final BatteryStatsHistoryIterator iterator = mBatteryStats.iterateBatteryStatsHistory(0, 0);
BatteryStats.HistoryItem item;
@@ -116,23 +109,75 @@
assertThat(iterator.next()).isNull();
}
- // Test history that spans multiple buffers and uses more than 32k different strings.
@Test
- public void tagsLongHistory() {
+ public void constrainedIteration() {
+ prepareHistory();
+
+ // Initial time is 3000
+ assertIncludedEvents(mBatteryStats.iterateBatteryStatsHistory(0, 0),
+ 3_000L, 3_000L, 1003_000L, 2003_000L, 2004_000L);
+ assertIncludedEvents(mBatteryStats.iterateBatteryStatsHistory(1000_000, 0),
+ 1003_000L, 2003_000L, 2004_000L);
+ assertIncludedEvents(mBatteryStats.iterateBatteryStatsHistory(0, 2000_000L),
+ 3_000L, 3_000L, 1003_000L);
+ assertIncludedEvents(mBatteryStats.iterateBatteryStatsHistory(1003_000L, 2004_000L),
+ 1003_000L, 2003_000L);
+ }
+
+ private void prepareHistory() {
+ mMockClock.realtime = 1000;
+ mMockClock.uptime = 1000;
+ mMockClock.currentTime = 3000;
+
mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000,
1_000_000, 1_000_000);
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0, 2_000_000,
+ 2_000_000, 2_000_000);
+ mBatteryStats.noteAlarmStartLocked("foo", null, APP_UID, 3_000_000, 2_000_000);
+ mBatteryStats.noteAlarmFinishLocked("foo", null, APP_UID, 3_001_000, 2_001_000);
+ }
+
+ private void assertIncludedEvents(BatteryStatsHistoryIterator iterator,
+ Long... expectedTimestamps) {
+ ArrayList<Long> actualTimestamps = new ArrayList<>();
+ while (iterator.hasNext()) {
+ BatteryStats.HistoryItem item = iterator.next();
+ actualTimestamps.add(item.currentTime);
+ }
+ assertThat(actualTimestamps).isEqualTo(Arrays.asList(expectedTimestamps));
+ }
+
+ // Test history that spans multiple buffers and uses more than 32k different strings.
+ @Test
+ public void tagsLongHistory() {
+ mMockClock.currentTime = 1_000_000;
+ mMockClock.realtime = 1_000_000;
+ mMockClock.uptime = 1_000_000;
+
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, mMockClock.realtime,
+ mMockClock.uptime, mMockClock.currentTime);
// More than 32k strings
final int eventCount = 0x7FFF + 100;
for (int i = 0; i < eventCount; i++) {
// Names repeat in order to verify de-duping of identical history tags.
String name = "a" + (i % 10);
- mBatteryStats.noteAlarmStartLocked(name, null, APP_UID, 3_000_000, 2_000_000);
- mBatteryStats.noteAlarmFinishLocked(name, null, APP_UID, 3_500_000, 2_500_000);
+ mMockClock.currentTime += 1_000_000;
+ mMockClock.realtime += 1_000_000;
+ mMockClock.uptime += 1_000_000;
+ mBatteryStats.noteAlarmStartLocked(name, null, APP_UID,
+ mMockClock.realtime, mMockClock.uptime);
+ mMockClock.currentTime += 500_000;
+ mMockClock.realtime += 500_000;
+ mMockClock.uptime += 500_000;
+ mBatteryStats.noteAlarmFinishLocked(name, null, APP_UID,
+ mMockClock.realtime, mMockClock.uptime);
}
- final BatteryStatsHistoryIterator iterator = mBatteryStats.iterateBatteryStatsHistory();
+ final BatteryStatsHistoryIterator iterator = mBatteryStats.iterateBatteryStatsHistory(0, 0);
BatteryStats.HistoryItem item;
assertThat(item = iterator.next()).isNotNull();
@@ -146,12 +191,6 @@
assertThat(item.eventTag).isNull();
assertThat(item.time).isEqualTo(1_000_000);
- assertThat(item = iterator.next()).isNotNull();
- assertThat(item.cmd).isEqualTo((int) BatteryStats.HistoryItem.CMD_UPDATE);
- assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_NONE);
- assertThat(item.eventTag).isNull();
- assertThat(item.time).isEqualTo(2_000_000);
-
for (int i = 0; i < eventCount; i++) {
String name = "a" + (i % 10);
do {
@@ -205,7 +244,7 @@
mExternalStatsSync.updateCpuStats(300, 7_100_000, 4_100_000);
- final BatteryStatsHistoryIterator iterator = mBatteryStats.iterateBatteryStatsHistory();
+ final BatteryStatsHistoryIterator iterator = mBatteryStats.iterateBatteryStatsHistory(0, 0);
BatteryStats.HistoryItem item;
assertThat(item = iterator.next()).isNotNull();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index f2cbef6..f22296a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -24,13 +24,16 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.os.BatteryConsumer;
import android.os.BatteryManager;
import android.os.BatteryStats;
-import android.os.BatteryStats.CpuUsageDetails;
-import android.os.BatteryStats.EnergyConsumerDetails;
import android.os.BatteryStats.HistoryItem;
import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.UserHandle;
import android.telephony.NetworkRegistrationInfo;
+import android.util.AtomicFile;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -38,6 +41,7 @@
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
+import com.android.internal.os.PowerStats;
import org.junit.Before;
import org.junit.Test;
@@ -71,6 +75,7 @@
private BatteryStatsHistory.TraceDelegate mTracer;
@Mock
private BatteryStatsHistory.HistoryStepDetailsCalculator mStepDetailsCalculator;
+ private List<String> mReadFiles = new ArrayList<>();
@Before
public void setUp() {
@@ -85,8 +90,17 @@
}
}
mHistoryDir.delete();
+
+ mClock.realtime = 123;
+
mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
- mStepDetailsCalculator, mClock, mTracer);
+ mStepDetailsCalculator, mClock, mTracer) {
+ @Override
+ public boolean readFileToParcel(Parcel out, AtomicFile file) {
+ mReadFiles.add(file.getBaseFile().getName());
+ return super.readFileToParcel(out, file);
+ }
+ };
when(mStepDetailsCalculator.getHistoryStepDetails())
.thenReturn(new BatteryStats.HistoryStepDetails());
@@ -179,70 +193,165 @@
@Test
public void testConstruct() {
createActiveFile(mHistory);
- verifyFileNumbers(mHistory, Arrays.asList(0));
- verifyActiveFile(mHistory, "0.bin");
+ verifyFileNames(mHistory, Arrays.asList("123.bh"));
+ verifyActiveFile(mHistory, "123.bh");
}
@Test
public void testStartNextFile() {
- List<Integer> fileList = new ArrayList<>();
- fileList.add(0);
+ mClock.realtime = 123;
+
+ List<String> fileList = new ArrayList<>();
+ fileList.add("123.bh");
createActiveFile(mHistory);
// create file 1 to 31.
for (int i = 1; i < 32; i++) {
- fileList.add(i);
+ mClock.realtime = 1000 * i;
+ fileList.add(mClock.realtime + ".bh");
+
mHistory.startNextFile();
createActiveFile(mHistory);
- verifyFileNumbers(mHistory, fileList);
- verifyActiveFile(mHistory, i + ".bin");
+ verifyFileNames(mHistory, fileList);
+ verifyActiveFile(mHistory, mClock.realtime + ".bh");
}
// create file 32
+ mClock.realtime = 1000 * 32;
mHistory.startNextFile();
createActiveFile(mHistory);
- fileList.add(32);
+ fileList.add("32000.bh");
fileList.remove(0);
// verify file 0 is deleted.
- verifyFileDeleted("0.bin");
- verifyFileNumbers(mHistory, fileList);
- verifyActiveFile(mHistory, "32.bin");
+ verifyFileDeleted("123.bh");
+ verifyFileNames(mHistory, fileList);
+ verifyActiveFile(mHistory, "32000.bh");
// create file 33
+ mClock.realtime = 1000 * 33;
mHistory.startNextFile();
createActiveFile(mHistory);
// verify file 1 is deleted
- fileList.add(33);
+ fileList.add("33000.bh");
fileList.remove(0);
- verifyFileDeleted("1.bin");
- verifyFileNumbers(mHistory, fileList);
- verifyActiveFile(mHistory, "33.bin");
-
- assertEquals(0, mHistory.getHistoryUsedSize());
+ verifyFileDeleted("1000.bh");
+ verifyFileNames(mHistory, fileList);
+ verifyActiveFile(mHistory, "33000.bh");
// create a new BatteryStatsHistory object, it will pick up existing history files.
BatteryStatsHistory history2 = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024,
null, mClock, mTracer);
// verify constructor can pick up all files from file system.
- verifyFileNumbers(history2, fileList);
- verifyActiveFile(history2, "33.bin");
+ verifyFileNames(history2, fileList);
+ verifyActiveFile(history2, "33000.bh");
+
+ mClock.realtime = 1234567;
history2.reset();
createActiveFile(history2);
+
// verify all existing files are deleted.
- for (int i = 2; i < 33; ++i) {
- verifyFileDeleted(i + ".bin");
+ for (String file : fileList) {
+ verifyFileDeleted(file);
}
// verify file 0 is created
- verifyFileNumbers(history2, Arrays.asList(0));
- verifyActiveFile(history2, "0.bin");
+ verifyFileNames(history2, Arrays.asList("1234567.bh"));
+ verifyActiveFile(history2, "1234567.bh");
// create file 1.
+ mClock.realtime = 2345678;
+
history2.startNextFile();
createActiveFile(history2);
- verifyFileNumbers(history2, Arrays.asList(0, 1));
- verifyActiveFile(history2, "1.bin");
+ verifyFileNames(history2, Arrays.asList("1234567.bh", "2345678.bh"));
+ verifyActiveFile(history2, "2345678.bh");
+ }
+
+ @Test
+ public void unconstrainedIteration() {
+ prepareMultiFileHistory();
+
+ mReadFiles.clear();
+
+ // Prepare history for iteration
+ mHistory.iterate(0, 0);
+
+ Parcel parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
+ assertThat(parcel).isNotNull();
+ assertThat(mReadFiles).containsExactly("123.bh");
+
+ // Skip to the end to force reading the next parcel
+ parcel.setDataPosition(parcel.dataSize());
+ mReadFiles.clear();
+ parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
+ assertThat(parcel).isNotNull();
+ assertThat(mReadFiles).containsExactly("1000.bh");
+
+ parcel.setDataPosition(parcel.dataSize());
+ mReadFiles.clear();
+ parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
+ assertThat(parcel).isNotNull();
+ assertThat(mReadFiles).containsExactly("2000.bh");
+
+ parcel.setDataPosition(parcel.dataSize());
+ mReadFiles.clear();
+ parcel = mHistory.getNextParcel(0, Long.MAX_VALUE);
+ assertThat(parcel).isNull();
+ assertThat(mReadFiles).isEmpty();
+ }
+
+ @Test
+ public void constrainedIteration() {
+ prepareMultiFileHistory();
+
+ mReadFiles.clear();
+
+ // Prepare history for iteration
+ mHistory.iterate(1000, 3000);
+
+ Parcel parcel = mHistory.getNextParcel(1000, 3000);
+ assertThat(parcel).isNotNull();
+ assertThat(mReadFiles).containsExactly("1000.bh");
+
+ // Skip to the end to force reading the next parcel
+ parcel.setDataPosition(parcel.dataSize());
+ mReadFiles.clear();
+ parcel = mHistory.getNextParcel(1000, 3000);
+ assertThat(parcel).isNotNull();
+ assertThat(mReadFiles).containsExactly("2000.bh");
+
+ parcel.setDataPosition(parcel.dataSize());
+ mReadFiles.clear();
+ parcel = mHistory.getNextParcel(1000, 3000);
+ assertThat(parcel).isNull();
+ assertThat(mReadFiles).isEmpty();
+ }
+
+ private void prepareMultiFileHistory() {
+ mHistory.forceRecordAllHistory();
+
+ mClock.realtime = 1000;
+ mClock.uptime = 1000;
+ mHistory.recordEvent(mClock.realtime, mClock.uptime,
+ BatteryStats.HistoryItem.EVENT_JOB_START, "job", 42);
+
+ mHistory.startNextFile(); // 1000.bh
+
+ mClock.realtime = 2000;
+ mClock.uptime = 2000;
+ mHistory.recordEvent(mClock.realtime, mClock.uptime,
+ BatteryStats.HistoryItem.EVENT_JOB_FINISH, "job", 42);
+
+ mHistory.startNextFile(); // 2000.bh
+
+ mClock.realtime = 3000;
+ mClock.uptime = 3000;
+ mHistory.recordEvent(mClock.realtime, mClock.uptime,
+ HistoryItem.EVENT_ALARM, "alarm", 42);
+
+ // Flush accumulated history to disk
+ mHistory.startNextFile();
}
private void verifyActiveFile(BatteryStatsHistory history, String file) {
@@ -251,12 +360,11 @@
assertTrue(expectedFile.exists());
}
- private void verifyFileNumbers(BatteryStatsHistory history, List<Integer> fileList) {
- assertEquals(fileList.size(), history.getFilesNumbers().size());
+ private void verifyFileNames(BatteryStatsHistory history, List<String> fileList) {
+ assertEquals(fileList.size(), history.getFilesNames().size());
for (int i = 0; i < fileList.size(); i++) {
- assertEquals(fileList.get(i), history.getFilesNumbers().get(i));
- final File expectedFile =
- new File(mHistoryDir, fileList.get(i) + ".bin");
+ assertEquals(fileList.get(i), history.getFilesNames().get(i));
+ final File expectedFile = new File(mHistoryDir, fileList.get(i));
assertTrue(expectedFile.exists());
}
}
@@ -267,6 +375,9 @@
private void createActiveFile(BatteryStatsHistory history) {
final File file = history.getActiveFile().getBaseFile();
+ if (file.exists()) {
+ return;
+ }
try {
file.createNewFile();
} catch (IOException e) {
@@ -275,49 +386,18 @@
}
@Test
- public void testRecordMeasuredEnergyDetails() {
- mHistory.forceRecordAllHistory();
- mHistory.startRecordingHistory(0, 0, /* reset */ true);
- mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80,
- 1234);
+ public void recordPowerStats() {
+ PowerStats.Descriptor descriptor = new PowerStats.Descriptor(42, "foo", 1, 2,
+ new PersistableBundle());
+ PowerStats powerStats = new PowerStats(descriptor);
+ powerStats.durationMs = 100;
+ powerStats.stats[0] = 200;
+ powerStats.uidStats.put(300, new long[]{400, 500});
+ powerStats.uidStats.put(600, new long[]{700, 800});
- EnergyConsumerDetails details = new EnergyConsumerDetails();
- EnergyConsumerDetails.EnergyConsumer consumer1 =
- new EnergyConsumerDetails.EnergyConsumer();
- consumer1.type = 42;
- consumer1.ordinal = 0;
- consumer1.name = "A";
+ mHistory.recordPowerStats(200, 200, powerStats);
- EnergyConsumerDetails.EnergyConsumer consumer2 =
- new EnergyConsumerDetails.EnergyConsumer();
- consumer2.type = 777;
- consumer2.ordinal = 0;
- consumer2.name = "B/0";
-
- EnergyConsumerDetails.EnergyConsumer consumer3 =
- new EnergyConsumerDetails.EnergyConsumer();
- consumer3.type = 777;
- consumer3.ordinal = 1;
- consumer3.name = "B/1";
-
- EnergyConsumerDetails.EnergyConsumer consumer4 =
- new EnergyConsumerDetails.EnergyConsumer();
- consumer4.type = 314;
- consumer4.ordinal = 1;
- consumer4.name = "C";
-
- details.consumers =
- new EnergyConsumerDetails.EnergyConsumer[]{consumer1, consumer2, consumer3,
- consumer4};
- details.chargeUC = new long[details.consumers.length];
- for (int i = 0; i < details.chargeUC.length; i++) {
- details.chargeUC[i] = 100L * i;
- }
- details.chargeUC[3] = BatteryStats.POWER_DATA_UNAVAILABLE;
-
- mHistory.recordEnergyConsumerDetails(200, 200, details);
-
- BatteryStatsHistoryIterator iterator = mHistory.iterate();
+ BatteryStatsHistoryIterator iterator = mHistory.iterate(0, 0);
BatteryStats.HistoryItem item;
assertThat(item = iterator.next()).isNotNull(); // First item contains current time only
@@ -325,62 +405,10 @@
String dump = toString(item, /* checkin */ false);
assertThat(dump).contains("+200ms");
- assertThat(dump).contains("ext=energy:A=0 B/0=100 B/1=200");
- assertThat(dump).doesNotContain("C=");
-
- String checkin = toString(item, /* checkin */ true);
- assertThat(checkin).contains("XE");
- assertThat(checkin).contains("A=0,B/0=100,B/1=200");
- assertThat(checkin).doesNotContain("C=");
- }
-
- @Test
- public void cpuUsageDetails() {
- mHistory.forceRecordAllHistory();
- mHistory.startRecordingHistory(0, 0, /* reset */ true);
- mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80,
- 1234);
-
- CpuUsageDetails details = new CpuUsageDetails();
- details.cpuBracketDescriptions = new String[] {"low", "Med", "HIGH"};
- details.uid = 10123;
- details.cpuUsageMs = new long[] { 100, 200, 300};
- mHistory.recordCpuUsage(200, 200, details);
-
- details.uid = 10321;
- details.cpuUsageMs = new long[] { 400, 500, 600};
- mHistory.recordCpuUsage(300, 300, details);
-
- BatteryStatsHistoryIterator iterator = mHistory.iterate();
- BatteryStats.HistoryItem item = new BatteryStats.HistoryItem();
- assertThat(item = iterator.next()).isNotNull(); // First item contains current time only
-
- assertThat(item = iterator.next()).isNotNull();
-
- String dump = toString(item, /* checkin */ false);
- assertThat(dump).contains("+200ms");
- assertThat(dump).contains("ext=cpu:u0a123: 100, 200, 300");
- assertThat(dump).contains("ext=cpu-bracket:0:low");
- assertThat(dump).contains("ext=cpu-bracket:1:Med");
- assertThat(dump).contains("ext=cpu-bracket:2:HIGH");
-
- String checkin = toString(item, /* checkin */ true);
- assertThat(checkin).contains("XB,3,0,low");
- assertThat(checkin).contains("XB,3,1,Med");
- assertThat(checkin).contains("XB,3,2,HIGH");
- assertThat(checkin).contains("XC,10123,100,200,300");
-
- assertThat(item = iterator.next()).isNotNull();
-
- dump = toString(item, /* checkin */ false);
- assertThat(dump).contains("+300ms");
- assertThat(dump).contains("ext=cpu:u0a321: 400, 500, 600");
- // Power bracket descriptions are written only once
- assertThat(dump).doesNotContain("ext=cpu-bracket");
-
- checkin = toString(item, /* checkin */ true);
- assertThat(checkin).doesNotContain("XB");
- assertThat(checkin).contains("XC,10321,400,500,600");
+ assertThat(dump).contains("duration=100");
+ assertThat(dump).contains("foo=[200]");
+ assertThat(dump).contains("300: [400, 500]");
+ assertThat(dump).contains("600: [700, 800]");
}
@Test
@@ -399,7 +427,7 @@
mHistory.recordNrStateChangeEvent(500, 500,
NetworkRegistrationInfo.NR_STATE_NONE);
- BatteryStatsHistoryIterator iterator = mHistory.iterate();
+ BatteryStatsHistoryIterator iterator = mHistory.iterate(0, 0);
BatteryStats.HistoryItem item = new BatteryStats.HistoryItem();
assertThat(item = iterator.next()).isNotNull(); // First item contains current time only
@@ -440,7 +468,7 @@
mHistory.recordNrStateChangeEvent(500, 500,
NetworkRegistrationInfo.NR_STATE_NONE);
- BatteryStatsHistoryIterator iterator = mHistory.iterate();
+ BatteryStatsHistoryIterator iterator = mHistory.iterate(0, 0);
BatteryStats.HistoryItem item = new BatteryStats.HistoryItem();
assertThat(item = iterator.next()).isNotNull(); // First item contains current time only
@@ -498,7 +526,7 @@
mClock.uptime = 1_000_000;
// More than 32k strings
final int tagCount = 0x7FFF + 20;
- for (int tag = 0; tag < tagCount;) {
+ for (int tag = 0; tag < tagCount; ) {
mClock.realtime += 10;
mClock.uptime += 10;
mHistory.recordEvent(mClock.realtime, mClock.uptime, HistoryItem.EVENT_ALARM_START,
@@ -522,8 +550,8 @@
int wakelockTagsUnpooled = 0;
int wakeReasonTagsPooled = 0;
int wakeReasonTagsUnpooled = 0;
- for (BatteryStatsHistoryIterator iterator = mHistory.iterate(); iterator.hasNext(); ) {
- HistoryItem item = iterator.next();
+ for (BatteryStatsHistoryIterator iterator = mHistory.iterate(0, 0); iterator.hasNext(); ) {
+ HistoryItem item = iterator.next();
if (item.cmd != HistoryItem.CMD_UPDATE) {
continue;
}
@@ -569,10 +597,40 @@
assertThat(wakeReasonTagsUnpooled).isGreaterThan(0);
}
+ @Test
+ public void recordProcStateChange() {
+ mHistory.recordProcessStateChange(200, 200, 42, BatteryConsumer.PROCESS_STATE_BACKGROUND);
+ mHistory.recordProcessStateChange(300, 300, 42, BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ // Large UID, > 0xFFFFFF
+ mHistory.recordProcessStateChange(400, 400,
+ UserHandle.getUid(777, Process.LAST_ISOLATED_UID),
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ BatteryStatsHistoryIterator iterator = mHistory.iterate(0, 0);
+ BatteryStats.HistoryItem item;
+ assertThat(item = iterator.next()).isNotNull(); // First item contains current time only
+
+ assertThat(item = iterator.next()).isNotNull();
+
+ String dump = toString(item, /* checkin */ false);
+ assertThat(dump).contains("+200ms");
+ assertThat(dump).contains("procstate: 42: bg");
+
+ assertThat(item = iterator.next()).isNotNull();
+ dump = toString(item, /* checkin */ false);
+ assertThat(dump).contains("+300ms");
+ assertThat(dump).contains("procstate: 42: fg");
+
+ assertThat(item = iterator.next()).isNotNull();
+ dump = toString(item, /* checkin */ false);
+ assertThat(dump).contains("+400ms");
+ assertThat(dump).contains("procstate: u777i999: fgs");
+ }
+
private String toString(BatteryStats.HistoryItem item, boolean checkin) {
StringWriter writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
- mHistoryPrinter.printNextItem(pw, item, 0, checkin, /* verbose */ false);
+ mHistoryPrinter.printNextItem(pw, item, 0, checkin, /* verbose */ true);
pw.flush();
return writer.toString();
}
@@ -596,6 +654,7 @@
0xffffffffffffffffL};
// Parcel subarrays of different lengths and assert the size of the resulting parcel
+ testVarintParceler(Arrays.copyOfRange(values, 0, 0), 0);
testVarintParceler(Arrays.copyOfRange(values, 0, 1), 4); // v. 8
testVarintParceler(Arrays.copyOfRange(values, 0, 2), 4); // v. 16
testVarintParceler(Arrays.copyOfRange(values, 0, 3), 4); // v. 24
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index 88b9522..7ef1a3f 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -263,7 +263,7 @@
clocks.realtime = clocks.uptime = 220;
bi.noteLongPartialWakelockFinish(name, historyName, ISOLATED_UID);
- final BatteryStatsHistoryIterator iterator = bi.iterateBatteryStatsHistory();
+ final BatteryStatsHistoryIterator iterator = bi.iterateBatteryStatsHistory(0, 0);
BatteryStats.HistoryItem item;
@@ -319,7 +319,7 @@
clocks.realtime = clocks.uptime = 220;
bi.noteLongPartialWakelockFinish(name, historyName, ISOLATED_UID);
- final BatteryStatsHistoryIterator iterator = bi.iterateBatteryStatsHistory();
+ final BatteryStatsHistoryIterator iterator = bi.iterateBatteryStatsHistory(0, 0);
BatteryStats.HistoryItem item;
@@ -933,7 +933,7 @@
clocks.realtime = clocks.uptime = 5000;
bi.noteAlarmFinishLocked("foo", null, UID);
- BatteryStatsHistoryIterator iterator = bi.iterateBatteryStatsHistory();
+ BatteryStatsHistoryIterator iterator = bi.iterateBatteryStatsHistory(0, 0);
HistoryItem item;
assertThat(item = iterator.next()).isNotNull();
@@ -972,7 +972,7 @@
clocks.realtime = clocks.uptime = 5000;
bi.noteAlarmFinishLocked("foo", ws, UID);
- BatteryStatsHistoryIterator iterator = bi.iterateBatteryStatsHistory();
+ BatteryStatsHistoryIterator iterator = bi.iterateBatteryStatsHistory(0, 0);
HistoryItem item;
assertThat(item = iterator.next()).isNotNull();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
new file mode 100644
index 0000000..f2ee6db
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.SparseArray;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CpuPowerStatsCollectorTest {
+ private final MockClock mMockClock = new MockClock();
+ private final HandlerThread mHandlerThread = new HandlerThread("test");
+ private Handler mHandler;
+ private CpuPowerStatsCollector mCollector;
+ private PowerStats mCollectedStats;
+ @Mock
+ private PowerProfile mPowerProfile;
+ @Mock
+ private CpuPowerStatsCollector.KernelCpuStatsReader mMockKernelCpuStatsReader;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mHandlerThread.start();
+ mHandler = mHandlerThread.getThreadHandler();
+ when(mPowerProfile.getCpuPowerBracketCount()).thenReturn(2);
+ when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 0)).thenReturn(0);
+ when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 1)).thenReturn(1);
+ mCollector = new CpuPowerStatsCollector(new CpuScalingPolicies(
+ new SparseArray<>() {{
+ put(0, new int[]{0});
+ }},
+ new SparseArray<>() {{
+ put(0, new int[]{1, 12});
+ }}),
+ mPowerProfile, mHandler, mMockKernelCpuStatsReader, 60_000, mMockClock);
+ mCollector.addConsumer(stats -> mCollectedStats = stats);
+ mCollector.setEnabled(true);
+ }
+
+ @Test
+ public void collectStats() {
+ mockKernelCpuStats(new SparseArray<>() {{
+ put(42, new long[]{100, 200});
+ put(99, new long[]{300, 600});
+ }}, 0, 1234);
+
+ mMockClock.uptime = 1000;
+ mCollector.forceSchedule();
+ waitForIdle();
+
+ assertThat(mCollectedStats.durationMs).isEqualTo(1234);
+ assertThat(mCollectedStats.uidStats.get(42)).isEqualTo(new long[]{100, 200});
+ assertThat(mCollectedStats.uidStats.get(99)).isEqualTo(new long[]{300, 600});
+
+ mockKernelCpuStats(new SparseArray<>() {{
+ put(42, new long[]{123, 234});
+ put(99, new long[]{345, 678});
+ }}, 1234, 3421);
+
+ mMockClock.uptime = 2000;
+ mCollector.forceSchedule();
+ waitForIdle();
+
+ assertThat(mCollectedStats.durationMs).isEqualTo(3421 - 1234);
+ assertThat(mCollectedStats.uidStats.get(42)).isEqualTo(new long[]{23, 34});
+ assertThat(mCollectedStats.uidStats.get(99)).isEqualTo(new long[]{45, 78});
+ }
+
+ private void mockKernelCpuStats(SparseArray<long[]> uidToCpuStats,
+ long expectedLastUpdateTimestampMs, long newLastUpdateTimestampMs) {
+ when(mMockKernelCpuStatsReader.nativeReadCpuStats(
+ any(CpuPowerStatsCollector.KernelCpuStatsCallback.class),
+ any(int[].class), anyLong(), any(long[].class)))
+ .thenAnswer(invocation -> {
+ CpuPowerStatsCollector.KernelCpuStatsCallback callback =
+ invocation.getArgument(0);
+ int[] powerBucketIndexes = invocation.getArgument(1);
+ long lastTimestamp = invocation.getArgument(2);
+ long[] tempStats = invocation.getArgument(3);
+
+ assertThat(powerBucketIndexes).isEqualTo(new int[]{0, 1});
+ assertThat(lastTimestamp / 1000000L).isEqualTo(expectedLastUpdateTimestampMs);
+ assertThat(tempStats).hasLength(2);
+
+ for (int i = 0; i < uidToCpuStats.size(); i++) {
+ int uid = uidToCpuStats.keyAt(i);
+ long[] cpuStats = uidToCpuStats.valueAt(i);
+ System.arraycopy(cpuStats, 0, tempStats, 0, tempStats.length);
+ callback.processUidStats(uid, tempStats);
+ }
+ return newLastUpdateTimestampMs * 1000000L; // Nanoseconds
+ });
+ }
+
+ private void waitForIdle() {
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ done.block();
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
new file mode 100644
index 0000000..38a5d19
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.fail;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.DeviceConfig;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.frameworks.coretests.aidl.ICmdCallback;
+import com.android.frameworks.coretests.aidl.ICmdReceiver;
+import com.android.server.power.optimization.Flags;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class CpuPowerStatsCollectorValidationTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private static final int WORK_DURATION_MS = 2000;
+ private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp";
+ private static final String TEST_ACTIVITY = TEST_PKG + ".TestActivity";
+ private static final String EXTRA_KEY_CMD_RECEIVER = "cmd_receiver";
+ private static final int START_ACTIVITY_TIMEOUT_MS = 2000;
+
+ private Context mContext;
+ private UiDevice mUiDevice;
+ private DeviceConfig.Properties mBackupFlags;
+ private int mTestPkgUid;
+
+ @Before
+ public void setup() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ mTestPkgUid = mContext.getPackageManager().getPackageUid(TEST_PKG, 0);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_STREAMLINED_BATTERY_STATS)
+ public void totalTimeInPowerBrackets() throws Exception {
+ dumpCpuStats(); // For the side effect of capturing the baseline.
+
+ doSomeWork();
+
+ long duration = 0;
+ long[] stats = null;
+
+ String[] cpuStatsDump = dumpCpuStats();
+ Pattern durationPattern = Pattern.compile("duration=([0-9]*)");
+ Pattern uidPattern = Pattern.compile("UID " + mTestPkgUid + ": \\[([0-9,\\s]*)]");
+ for (String line : cpuStatsDump) {
+ Matcher durationMatcher = durationPattern.matcher(line);
+ if (durationMatcher.find()) {
+ duration = Long.parseLong(durationMatcher.group(1));
+ }
+ Matcher uidMatcher = uidPattern.matcher(line);
+ if (uidMatcher.find()) {
+ String[] strings = uidMatcher.group(1).split(", ");
+ stats = new long[strings.length];
+ for (int i = 0; i < strings.length; i++) {
+ stats[i] = Long.parseLong(strings[i]);
+ }
+ }
+ }
+ if (stats == null) {
+ fail("No CPU stats for " + mTestPkgUid + " (" + TEST_PKG + ")");
+ }
+
+ assertThat(duration).isAtLeast(WORK_DURATION_MS);
+
+ long total = Arrays.stream(stats).sum();
+ assertThat(total).isAtLeast((long) (WORK_DURATION_MS * 0.8));
+ }
+
+ private String[] dumpCpuStats() throws Exception {
+ String dump = executeCmdSilent("dumpsys batterystats --sample");
+ String[] lines = dump.split("\n");
+ for (int i = 0; i < lines.length; i++) {
+ if (lines[i].startsWith("CpuPowerStatsCollector")) {
+ return Arrays.copyOfRange(lines, i + 1, lines.length);
+ }
+ }
+ return new String[0];
+ }
+
+ private void doSomeWork() throws Exception {
+ final ICmdReceiver receiver;
+ receiver = ICmdReceiver.Stub.asInterface(startActivity());
+ try {
+ receiver.doSomeWork(WORK_DURATION_MS);
+ } finally {
+ receiver.finishHost();
+ }
+ }
+
+ private IBinder startActivity() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final Intent launchIntent = new Intent().setComponent(
+ new ComponentName(TEST_PKG, TEST_ACTIVITY));
+ final Bundle extras = new Bundle();
+ final IBinder[] binders = new IBinder[1];
+ extras.putBinder(EXTRA_KEY_CMD_RECEIVER, new ICmdCallback.Stub() {
+ @Override
+ public void onLaunched(IBinder receiver) {
+ binders[0] = receiver;
+ latch.countDown();
+ }
+ });
+ launchIntent.putExtras(extras).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(launchIntent);
+ if (latch.await(START_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ if (binders[0] == null) {
+ fail("Receiver binder should not be null");
+ }
+ return binders[0];
+ } else {
+ fail("Timed out waiting for the test activity to start; testUid=" + mTestPkgUid);
+ }
+ return null;
+ }
+
+ private String executeCmdSilent(String cmd) throws Exception {
+ return mUiDevice.executeShellCommand(cmd).trim();
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java
index 28f4799..6cd0865 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java
@@ -26,7 +26,6 @@
import android.hardware.power.stats.EnergyConsumerAttribution;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
-import android.os.BatteryStats;
import android.util.SparseArray;
import android.util.SparseLongArray;
@@ -238,17 +237,6 @@
}
@Test
- public void getMeasuredEnergyDetails() {
- final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(ALL_ID_CONSUMER_MAP);
- snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
- EnergyConsumerDeltaData delta = snapshot.updateAndGetDelta(RESULTS_1, VOLTAGE_1);
- BatteryStats.EnergyConsumerDetails details = snapshot.getEnergyConsumerDetails(delta);
- assertThat(details.consumers).hasLength(4);
- assertThat(details.chargeUC).isEqualTo(new long[]{2667, 3200000, 0, 0});
- assertThat(details.toString()).isEqualTo("DISPLAY=2667 HPU=3200000 GPU=0 IPU &_=0");
- }
-
- @Test
public void testUpdateAndGetDelta_updatesCameraCharge() {
EnergyConsumer cameraConsumer =
createEnergyConsumer(7, 0, EnergyConsumerType.CAMERA, "CAMERA");
@@ -266,12 +254,8 @@
createEnergyConsumerResult(cameraConsumer.id, 90_000, null, null),
};
EnergyConsumerDeltaData delta = snapshot.updateAndGetDelta(result1, VOLTAGE_1);
-
- // Verify that the delta between the two results is reported.
- BatteryStats.EnergyConsumerDetails details = snapshot.getEnergyConsumerDetails(delta);
- assertThat(details.consumers).hasLength(1);
long expectedDeltaUC = calculateChargeConsumedUC(60_000, VOLTAGE_1, 90_000, VOLTAGE_1);
- assertThat(details.chargeUC[0]).isEqualTo(expectedDeltaUC);
+ assertThat(delta.cameraChargeUC).isEqualTo(expectedDeltaUC);
}
private static EnergyConsumer createEnergyConsumer(int id, int ord, byte type, String name) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 6d3f1f2..4150972a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -119,6 +119,13 @@
return MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS;
}
+ public MockBatteryStatsImpl setBatteryStatsConfig(BatteryStatsConfig config) {
+ synchronized (this) {
+ mBatteryStatsConfig = config;
+ }
+ return this;
+ }
+
public MockBatteryStatsImpl setNetworkStats(NetworkStats networkStats) {
mNetworkStats = networkStats;
return this;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
new file mode 100644
index 0000000..4ecee9f
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
@@ -0,0 +1,267 @@
+/*
+ * 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.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.BatteryConsumer;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.MultiStateStats;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MultiStateStatsTest {
+
+ public static final int DIMENSION_COUNT = 2;
+
+ @Test
+ public void compositeStateIndex_allEnabled() {
+ MultiStateStats.Factory factory = makeFactory(true, true, true);
+ assertThatCpuPerformanceStatsFactory(factory)
+ .hasSerialStateCount(BatteryConsumer.PROCESS_STATE_COUNT * 4)
+ .haveDifferentSerialStates(
+ state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(false, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+ state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+ state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+ state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
+ }
+
+ @Test
+ public void compositeStateIndex_procStateTrackingDisabled() {
+ MultiStateStats.Factory factory = makeFactory(true, false, true);
+ assertThatCpuPerformanceStatsFactory(factory)
+ .hasSerialStateCount(4)
+ .haveDifferentSerialStates(
+ state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND))
+ .haveSameSerialStates(
+ state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(false, false, BatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .haveSameSerialStates(
+ state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .haveSameSerialStates(
+ state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .haveSameSerialStates(
+ state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
+ }
+
+ @Test
+ public void compositeStateIndex_screenTrackingDisabled() {
+ MultiStateStats.Factory factory = makeFactory(true, true, false);
+ assertThatCpuPerformanceStatsFactory(factory)
+ .hasSerialStateCount(BatteryConsumer.PROCESS_STATE_COUNT * 2)
+ .haveDifferentSerialStates(
+ state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+ state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .haveSameSerialStates(
+ state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND))
+ .haveSameSerialStates(
+ state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+ state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
+ }
+
+ @Test
+ public void compositeStateIndex_allDisabled() {
+ MultiStateStats.Factory factory = makeFactory(false, false, false);
+ assertThatCpuPerformanceStatsFactory(factory)
+ .hasSerialStateCount(1)
+ .haveSameSerialStates(
+ state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(false, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+ state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+ state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+ state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
+ }
+
+ @Test
+ public void tooManyStates() {
+ // 4 bits needed to represent
+ String[] labels = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};
+ // 4 * 10 = 40 bits needed to represent the composite state
+ MultiStateStats.States[] states = new MultiStateStats.States[10];
+ for (int i = 0; i < states.length; i++) {
+ states[i] = new MultiStateStats.States(true, labels);
+ }
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> new MultiStateStats.Factory(DIMENSION_COUNT, states));
+ assertThat(e.getMessage()).contains("40");
+ }
+
+ @Test
+ public void multiStateStats_aggregation() {
+ MultiStateStats.Factory factory = makeFactory(true, true, false);
+ MultiStateStats multiStateStats = factory.create();
+ multiStateStats.setState(0 /* batteryState */, 1 /* on */, 1000);
+ multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_FOREGROUND, 1000);
+ multiStateStats.setState(2 /* screenState */, 0 /* off */, 1000);
+
+ multiStateStats.increment(new long[]{100, 200}, 1000);
+
+ multiStateStats.setState(0 /* batteryState */, 0 /* off */, 2000);
+ multiStateStats.setState(2 /* screenState */, 1 /* on */, 2000); // untracked
+
+ multiStateStats.increment(new long[]{300, 500}, 3000);
+
+ multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_BACKGROUND, 4000);
+
+ multiStateStats.increment(new long[]{200, 200}, 5000);
+
+ long[] stats = new long[DIMENSION_COUNT];
+ multiStateStats.getStats(stats, new int[]{0, BatteryConsumer.PROCESS_STATE_FOREGROUND, 0});
+ // (400 - 100) * 0.5 + (600 - 400) * 0.5
+ assertThat(stats).isEqualTo(new long[]{250, 350});
+
+ multiStateStats.getStats(stats, new int[]{1, BatteryConsumer.PROCESS_STATE_FOREGROUND, 0});
+ // (400 - 100) * 0.5 + (600 - 400) * 0
+ assertThat(stats).isEqualTo(new long[]{150, 250});
+
+ // Note that screen state does not affect the result, as it is untracked
+ multiStateStats.getStats(stats, new int[]{0, BatteryConsumer.PROCESS_STATE_BACKGROUND, 1});
+ // (400 - 100) * 0 + (600 - 400) * 0.5
+ assertThat(stats).isEqualTo(new long[]{100, 100});
+
+ multiStateStats.getStats(stats, new int[]{1, BatteryConsumer.PROCESS_STATE_BACKGROUND, 0});
+ // Never been in this composite state
+ assertThat(stats).isEqualTo(new long[]{0, 0});
+ }
+
+ @Test
+ public void dump() {
+ MultiStateStats.Factory factory = makeFactory(true, true, false);
+ MultiStateStats multiStateStats = factory.create();
+ multiStateStats.setState(0 /* batteryState */, 0 /* off */, 1000);
+ multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_FOREGROUND, 1000);
+ multiStateStats.setState(2 /* screenState */, 0 /* off */, 1000);
+ multiStateStats.setState(0 /* batteryState */, 1 /* on */, 2000);
+ multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_BACKGROUND, 3000);
+ multiStateStats.increment(new long[]{100, 200}, 5000);
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw, true);
+ multiStateStats.dump(pw);
+ assertThat(sw.toString()).isEqualTo(
+ "plugged-in fg [25, 50]\n"
+ + "on-battery fg [25, 50]\n"
+ + "on-battery bg [50, 100]\n"
+ );
+ }
+
+ private static MultiStateStats.Factory makeFactory(boolean trackBatteryState,
+ boolean trackProcState, boolean trackScreenState) {
+ return new MultiStateStats.Factory(DIMENSION_COUNT,
+ new MultiStateStats.States(trackBatteryState, "plugged-in", "on-battery"),
+ new MultiStateStats.States(trackProcState,
+ BatteryConsumer.processStateToString(
+ BatteryConsumer.PROCESS_STATE_UNSPECIFIED),
+ BatteryConsumer.processStateToString(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND),
+ BatteryConsumer.processStateToString(
+ BatteryConsumer.PROCESS_STATE_BACKGROUND),
+ BatteryConsumer.processStateToString(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE),
+ BatteryConsumer.processStateToString(
+ BatteryConsumer.PROCESS_STATE_CACHED)),
+ new MultiStateStats.States(trackScreenState, "screen-off", "plugged-in"));
+ }
+
+ private FactorySubject assertThatCpuPerformanceStatsFactory(
+ MultiStateStats.Factory factory) {
+ FactorySubject subject = new FactorySubject();
+ subject.mFactory = factory;
+ return subject;
+ }
+
+ private static class FactorySubject {
+ private MultiStateStats.Factory mFactory;
+
+ FactorySubject hasSerialStateCount(int stateCount) {
+ assertThat(mFactory.getSerialStateCount()).isEqualTo(stateCount);
+ return this;
+ }
+
+ public FactorySubject haveDifferentSerialStates(State... states) {
+ int[] serialStates = getSerialStates(states);
+ assertWithMessage("Expected all to be different: " + Arrays.toString(serialStates))
+ .that(Arrays.stream(serialStates).distinct().toArray())
+ .hasLength(states.length);
+ return this;
+ }
+
+ public FactorySubject haveSameSerialStates(State... states) {
+ int[] serialStates = getSerialStates(states);
+ assertWithMessage("Expected all to be the same: " + Arrays.toString(serialStates))
+ .that(Arrays.stream(serialStates).distinct().toArray())
+ .hasLength(1);
+ return this;
+ }
+
+ private int[] getSerialStates(State[] states) {
+ int[] serialStates = new int[states.length];
+ for (int i = 0; i < states.length; i++) {
+ serialStates[i] = mFactory.getSerialState(
+ new int[]{
+ states[i].batteryState ? 0 : 1,
+ states[i].procstate,
+ states[i].screenState ? 0 : 1
+ });
+ }
+ return serialStates;
+ }
+ }
+
+ private State state(boolean batteryState, boolean screenState, int procstate) {
+ State state = new State();
+ state.batteryState = batteryState;
+ state.screenState = screenState;
+ state.procstate = procstate;
+ return state;
+ }
+
+ private static class State {
+ public boolean batteryState;
+ public boolean screenState;
+ public int procstate;
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
new file mode 100644
index 0000000..47de443
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
@@ -0,0 +1,232 @@
+/*
+ * 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.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PowerStatsAggregatorTest {
+ private static final int TEST_POWER_COMPONENT = 77;
+ private static final int TEST_UID = 42;
+
+ private final MockClock mClock = new MockClock();
+ private long mStartTime;
+ private BatteryStatsHistory mHistory;
+ private PowerStatsAggregator mAggregator;
+ private int mAggregatedStatsCount;
+
+ @Before
+ public void setup() throws ParseException {
+ mHistory = new BatteryStatsHistory(32, 1024,
+ mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock);
+ mStartTime = new SimpleDateFormat("yyyy-MM-dd HH:mm")
+ .parse("2008-09-23 08:00").getTime();
+ mClock.currentTime = mStartTime;
+
+ PowerStatsAggregator.Builder builder = new PowerStatsAggregator.Builder(mHistory);
+ builder.trackPowerComponent(TEST_POWER_COMPONENT)
+ .trackDeviceStates(
+ PowerStatsAggregator.STATE_POWER,
+ PowerStatsAggregator.STATE_SCREEN)
+ .trackUidStates(
+ PowerStatsAggregator.STATE_POWER,
+ PowerStatsAggregator.STATE_SCREEN,
+ PowerStatsAggregator.STATE_PROCESS_STATE);
+ mAggregator = builder.build();
+ }
+
+ @Test
+ public void stateUpdates() {
+ mHistory.forceRecordAllHistory();
+ mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
+ mHistory.recordStateStartEvent(mClock.realtime, mClock.uptime,
+ BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+ mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+
+ advance(1000);
+
+ PowerStats.Descriptor descriptor =
+ new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
+ new PersistableBundle());
+ PowerStats powerStats = new PowerStats(descriptor);
+ powerStats.stats = new long[]{10000};
+ powerStats.uidStats.put(TEST_UID, new long[]{1234});
+ mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+ mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 90, /* plugged */ false);
+ mHistory.recordStateStopEvent(mClock.realtime, mClock.uptime,
+ BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+
+ advance(1000);
+
+ mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+
+ advance(3000);
+
+ powerStats.stats = new long[]{20000};
+ powerStats.uidStats.put(TEST_UID, new long[]{4444});
+ mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+ mAggregator.aggregateBatteryStats(0, 0, stats -> {
+ assertThat(mAggregatedStatsCount++).isEqualTo(0);
+ assertThat(stats.getStartTime()).isEqualTo(mStartTime);
+ assertThat(stats.getDuration()).isEqualTo(5000);
+
+ long[] values = new long[1];
+
+ PowerComponentAggregatedPowerStats powerComponentStats = stats.getPowerComponentStats(
+ TEST_POWER_COMPONENT);
+
+ assertThat(powerComponentStats.getDeviceStats(values, new int[]{
+ PowerStatsAggregator.POWER_STATE_OTHER,
+ PowerStatsAggregator.SCREEN_STATE_ON}))
+ .isTrue();
+ assertThat(values).isEqualTo(new long[]{10000});
+
+ assertThat(powerComponentStats.getDeviceStats(values, new int[]{
+ PowerStatsAggregator.POWER_STATE_BATTERY,
+ PowerStatsAggregator.SCREEN_STATE_OTHER}))
+ .isTrue();
+ assertThat(values).isEqualTo(new long[]{20000});
+
+ assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
+ PowerStatsAggregator.POWER_STATE_OTHER,
+ PowerStatsAggregator.SCREEN_STATE_ON,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND}))
+ .isTrue();
+ assertThat(values).isEqualTo(new long[]{1234});
+
+ assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
+ PowerStatsAggregator.POWER_STATE_BATTERY,
+ PowerStatsAggregator.SCREEN_STATE_OTHER,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND}))
+ .isTrue();
+ assertThat(values).isEqualTo(new long[]{1111});
+
+ assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
+ PowerStatsAggregator.POWER_STATE_BATTERY,
+ PowerStatsAggregator.SCREEN_STATE_OTHER,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND}))
+ .isTrue();
+ assertThat(values).isEqualTo(new long[]{3333});
+ });
+ }
+
+ @Test
+ public void incompatiblePowerStats() {
+ mHistory.forceRecordAllHistory();
+ mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
+ mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+
+ advance(1000);
+
+ PowerStats.Descriptor descriptor =
+ new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
+ new PersistableBundle());
+ PowerStats powerStats = new PowerStats(descriptor);
+ powerStats.stats = new long[]{10000};
+ powerStats.uidStats.put(TEST_UID, new long[]{1234});
+ mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+ mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 90, /* plugged */ false);
+
+ advance(1000);
+
+ descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
+ PersistableBundle.forPair("something", "changed"));
+ powerStats = new PowerStats(descriptor);
+ powerStats.stats = new long[]{20000};
+ powerStats.uidStats.put(TEST_UID, new long[]{4444});
+ mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+ advance(1000);
+
+ mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 50, /* plugged */ true);
+
+ mAggregator.aggregateBatteryStats(0, 0, stats -> {
+ long[] values = new long[1];
+
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ stats.getPowerComponentStats(TEST_POWER_COMPONENT);
+
+ if (mAggregatedStatsCount == 0) {
+ assertThat(stats.getStartTime()).isEqualTo(mStartTime);
+ assertThat(stats.getDuration()).isEqualTo(2000);
+
+ assertThat(powerComponentStats.getDeviceStats(values, new int[]{
+ PowerStatsAggregator.POWER_STATE_OTHER,
+ PowerStatsAggregator.SCREEN_STATE_ON}))
+ .isTrue();
+ assertThat(values).isEqualTo(new long[]{10000});
+ assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
+ PowerStatsAggregator.POWER_STATE_OTHER,
+ PowerStatsAggregator.SCREEN_STATE_ON,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND}))
+ .isTrue();
+ assertThat(values).isEqualTo(new long[]{1234});
+ } else if (mAggregatedStatsCount == 1) {
+ assertThat(stats.getStartTime()).isEqualTo(mStartTime + 2000);
+ assertThat(stats.getDuration()).isEqualTo(1000);
+
+ assertThat(powerComponentStats.getDeviceStats(values, new int[]{
+ PowerStatsAggregator.POWER_STATE_BATTERY,
+ PowerStatsAggregator.SCREEN_STATE_ON}))
+ .isTrue();
+ assertThat(values).isEqualTo(new long[]{20000});
+ assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
+ PowerStatsAggregator.POWER_STATE_BATTERY,
+ PowerStatsAggregator.SCREEN_STATE_ON,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND}))
+ .isTrue();
+ assertThat(values).isEqualTo(new long[]{4444});
+ } else {
+ fail();
+ }
+ mAggregatedStatsCount++;
+ });
+ }
+
+ private void advance(long durationMs) {
+ mClock.realtime += durationMs;
+ mClock.uptime += durationMs;
+ mClock.currentTime += durationMs;
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
new file mode 100644
index 0000000..330f698
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.PersistableBundle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PowerStatsCollectorTest {
+ private final MockClock mMockClock = new MockClock();
+ private final HandlerThread mHandlerThread = new HandlerThread("test");
+ private Handler mHandler;
+ private PowerStatsCollector mCollector;
+ private PowerStats mCollectedStats;
+
+ @Before
+ public void setup() {
+ mHandlerThread.start();
+ mHandler = mHandlerThread.getThreadHandler();
+ mCollector = new PowerStatsCollector(mHandler,
+ 60000,
+ mMockClock) {
+ @Override
+ protected PowerStats collectStats() {
+ return new PowerStats(new PowerStats.Descriptor(0, 0, 0, new PersistableBundle()));
+ }
+ };
+ mCollector.addConsumer(stats -> mCollectedStats = stats);
+ mCollector.setEnabled(true);
+ }
+
+ @Test
+ public void throttlePeriod() {
+ mMockClock.uptime = 1000;
+ mCollector.schedule();
+ waitForIdle();
+
+ assertThat(mCollectedStats).isNotNull();
+
+ mMockClock.uptime += 1000;
+ mCollectedStats = null;
+ mCollector.schedule(); // Should be throttled
+ waitForIdle();
+
+ assertThat(mCollectedStats).isNull();
+
+ // Should be allowed to run
+ mMockClock.uptime += 100_000;
+ mCollector.schedule();
+ waitForIdle();
+
+ assertThat(mCollectedStats).isNotNull();
+ }
+
+ private void waitForIdle() {
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ done.block();
+ }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 92ff7ab..20d8a5d 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -68,6 +68,7 @@
"ActivityContext",
"coretests-aidl",
"securebox",
+ "flag-junit",
],
libs: [
diff --git a/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java b/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java
index 90df786..45e7f35 100644
--- a/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/CertBlacklisterTest.java
@@ -34,10 +34,10 @@
*/
public class CertBlacklisterTest extends AndroidTestCase {
- private static final String BLACKLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/";
+ private static final String DENYLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/";
- public static final String PUBKEY_PATH = BLACKLIST_ROOT + "pubkey_blacklist.txt";
- public static final String SERIAL_PATH = BLACKLIST_ROOT + "serial_blacklist.txt";
+ public static final String PUBKEY_PATH = DENYLIST_ROOT + "pubkey_blacklist.txt";
+ public static final String SERIAL_PATH = DENYLIST_ROOT + "serial_blacklist.txt";
public static final String PUBKEY_KEY = "pubkey_blacklist";
public static final String SERIAL_KEY = "serial_blacklist";
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 0a8c570..9fe743d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -30,6 +30,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -43,6 +44,7 @@
import android.animation.ValueAnimator;
import android.annotation.NonNull;
+import android.content.pm.PackageManager;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
@@ -252,7 +254,11 @@
detectTripleTap, detectShortcutTrigger,
mWindowMagnificationPromptController, DISPLAY_0,
mMockFullScreenMagnificationVibrationHelper);
- h.setSinglePanningEnabled(true);
+ if (isWatch()) {
+ h.setSinglePanningEnabled(true);
+ } else {
+ h.setSinglePanningEnabled(false);
+ }
mHandler = new TestHandler(h.mDetectingState, mClock) {
@Override
protected String messageToString(Message m) {
@@ -569,6 +575,7 @@
@Test
public void testActionUpNotAtEdge_singlePanningState_detectingState() {
+ assumeTrue(mMgh.mIsSinglePanningEnabled);
goFromStateIdleTo(STATE_SINGLE_PANNING);
send(upEvent());
@@ -579,6 +586,7 @@
@Test
public void testScroll_SinglePanningDisabled_delegatingState() {
+ assumeTrue(mMgh.mIsSinglePanningEnabled);
mMgh.setSinglePanningEnabled(false);
goFromStateIdleTo(STATE_ACTIVATED);
@@ -591,6 +599,7 @@
@Test
@FlakyTest
public void testScroll_singleHorizontalPanningAndAtEdge_leftEdgeOverscroll() {
+ assumeTrue(mMgh.mIsSinglePanningEnabled);
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerY =
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f;
@@ -614,6 +623,7 @@
@Test
@FlakyTest
public void testScroll_singleHorizontalPanningAndAtEdge_rightEdgeOverscroll() {
+ assumeTrue(mMgh.mIsSinglePanningEnabled);
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerY =
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f;
@@ -637,6 +647,7 @@
@Test
@FlakyTest
public void testScroll_singleVerticalPanningAndAtEdge_verticalOverscroll() {
+ assumeTrue(mMgh.mIsSinglePanningEnabled);
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerX =
(INITIAL_MAGNIFICATION_BOUNDS.right + INITIAL_MAGNIFICATION_BOUNDS.left) / 2.0f;
@@ -658,6 +669,7 @@
@Test
public void testScroll_singlePanningAndAtEdge_noOverscroll() {
+ assumeTrue(mMgh.mIsSinglePanningEnabled);
goFromStateIdleTo(STATE_SINGLE_PANNING);
float centerY =
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f;
@@ -679,6 +691,7 @@
@Test
public void testScroll_singleHorizontalPanningAndAtEdge_vibrate() {
+ assumeTrue(mMgh.mIsSinglePanningEnabled);
goFromStateIdleTo(STATE_SINGLE_PANNING);
mFullScreenMagnificationController.setCenter(
DISPLAY_0,
@@ -702,6 +715,7 @@
@Test
public void testScroll_singleVerticalPanningAndAtEdge_doNotVibrate() {
+ assumeTrue(mMgh.mIsSinglePanningEnabled);
goFromStateIdleTo(STATE_SINGLE_PANNING);
mFullScreenMagnificationController.setCenter(
DISPLAY_0,
@@ -868,6 +882,10 @@
mFullScreenMagnificationController.onUserContextChanged(DISPLAY_0);
}
+ private boolean isWatch() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+
/**
* Asserts that {@link #mMgh the handler} is in the given {@code state}
*/
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
index 285a20c..746fb53 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -36,6 +36,9 @@
import android.content.res.Resources;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.server.biometrics.sensors.BiometricNotification;
@@ -47,6 +50,8 @@
import java.io.File;
+@Presubmit
+@SmallTest
public class AuthenticationStatsCollectorTest {
private AuthenticationStatsCollector mAuthenticationStatsCollector;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
index 455625c..dde2a3c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
@@ -29,6 +29,9 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
import org.json.JSONException;
import org.json.JSONObject;
@@ -45,6 +48,8 @@
import java.util.List;
import java.util.Set;
+@Presubmit
+@SmallTest
public class AuthenticationStatsPersisterTest {
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@@ -211,6 +216,20 @@
assertThat(mStringSetArgumentCaptor.getValue()).contains(expectedFrrStats);
}
+ @Test
+ public void removeFrrStats_existingUser_shouldUpdateRecord() throws JSONException {
+ AuthenticationStats authenticationStats = new AuthenticationStats(USER_ID_1,
+ 300 /* totalAttempts */, 10 /* rejectedAttempts */,
+ 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE);
+ when(mSharedPreferences.getStringSet(eq(KEY), anySet())).thenReturn(
+ Set.of(buildFrrStats(authenticationStats)));
+
+ mAuthenticationStatsPersister.removeFrrStats(USER_ID_1);
+
+ verify(mEditor).putStringSet(eq(KEY), mStringSetArgumentCaptor.capture());
+ assertThat(mStringSetArgumentCaptor.getValue()).doesNotContain(authenticationStats);
+ }
+
private String buildFrrStats(AuthenticationStats authenticationStats)
throws JSONException {
if (authenticationStats.getModality() == BiometricsProtoEnums.MODALITY_FACE) {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index bcbbcd4..908afc8 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -61,6 +61,7 @@
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensorConfig;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
@@ -1729,7 +1730,9 @@
private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid,
VirtualDeviceParams params) {
VirtualDeviceImpl virtualDeviceImpl = new VirtualDeviceImpl(mContext,
- mAssociationInfo, mVdms, new Binder(), ownerUid, virtualDeviceId,
+ mAssociationInfo, mVdms, new Binder(),
+ new AttributionSource(ownerUid, "com.android.virtualdevice.test", "virtualdevice"),
+ virtualDeviceId,
mInputController, mCameraAccessController,
mPendingTrampolineCallback, mActivityListener, mSoundEffectListener,
mRunningAppsChangedCallback, params, new DisplayManagerGlobal(mIDisplayManager));
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 9ca84d3..ce15c6d 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -218,7 +218,7 @@
@Test
public void updateRuleSet_notSystemApp() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp(false);
Rule rule =
new Rule(
@@ -237,7 +237,7 @@
@Test
public void updateRuleSet_authorized() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
Rule rule =
new Rule(
@@ -251,7 +251,7 @@
@Test
public void updateRuleSet_correctMethodCall() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
IntentSender mockReceiver = mock(IntentSender.class);
List<Rule> rules =
@@ -271,7 +271,7 @@
@Test
public void updateRuleSet_fail() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
doThrow(new IOException()).when(mIntegrityFileManager).writeRules(any(), any(), any());
IntentSender mockReceiver = mock(IntentSender.class);
@@ -292,7 +292,7 @@
@Test
public void broadcastReceiverRegistration() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
ArgumentCaptor<IntentFilter> intentFilterCaptor =
ArgumentCaptor.forClass(IntentFilter.class);
@@ -308,7 +308,7 @@
@Test
public void handleBroadcast_correctArgs() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -345,7 +345,7 @@
@Test
public void handleBroadcast_correctArgs_multipleCerts() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -368,7 +368,7 @@
@Test
public void handleBroadcast_correctArgs_sourceStamp() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -393,7 +393,7 @@
@Test
public void handleBroadcast_allow() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -412,7 +412,7 @@
@Test
public void handleBroadcast_reject() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -438,7 +438,7 @@
@Test
public void handleBroadcast_notInitialized() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
when(mIntegrityFileManager.initialized()).thenReturn(false);
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
@@ -459,7 +459,7 @@
@Test
public void verifierAsInstaller_skipIntegrityVerification() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
setIntegrityCheckIncludesRuleProvider(false);
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
@@ -480,7 +480,7 @@
@Test
public void getCurrentRules() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
Rule rule = new Rule(IntegrityFormula.Application.packageNameEquals("package"), Rule.DENY);
when(mIntegrityFileManager.readRules(any())).thenReturn(Arrays.asList(rule));
@@ -490,7 +490,7 @@
@Test
public void getWhitelistedRuleProviders_returnsEmptyForNonSystemApps() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp(false);
assertThat(mService.getWhitelistedRuleProviders()).isEmpty();
@@ -498,13 +498,13 @@
@Test
public void getWhitelistedRuleProviders() throws Exception {
- whitelistUsAsRuleProvider();
+ allowlistUsAsRuleProvider();
makeUsSystemApp();
assertThat(mService.getWhitelistedRuleProviders()).containsExactly(TEST_FRAMEWORK_PACKAGE);
}
- private void whitelistUsAsRuleProvider() {
+ private void allowlistUsAsRuleProvider() {
Resources mockResources = mock(Resources.class);
when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
.thenReturn(new String[] {TEST_FRAMEWORK_PACKAGE});
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index e5909a4..80fb5e3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -512,7 +512,7 @@
verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
.isTestOnlyCertificateAlias(eq(TEST_ROOT_CERT_ALIAS));
- // no whitelists check
+ // no allowlists check
verify(mTestOnlyInsecureCertificateHelper, never())
.doesCredentialSupportInsecureMode(anyInt(), any());
verify(mTestOnlyInsecureCertificateHelper, never())
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index cb659b6..ecd35a5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -1565,6 +1565,25 @@
assertThat(userInfo.name).isEqualTo(newName);
}
+ @Test
+ public void testCannotCreateAdditionalMainUser() {
+ UserHandle mainUser = mUserManager.getMainUser();
+ assumeTrue("There is no main user", mainUser != null);
+
+ // Users with FLAG_MAIN can't be removed, so no point using the local createUser method.
+ UserInfo newMainUser = mUserManager.createUser("test", UserInfo.FLAG_MAIN);
+ assertThat(newMainUser).isNull();
+
+ List<UserInfo> users = mUserManager.getUsers();
+ int mainUserCount = 0;
+ for (UserInfo user : users) {
+ if (user.isMain()) {
+ mainUserCount++;
+ }
+ }
+ assertThat(mainUserCount).isEqualTo(1);
+ }
+
private boolean isPackageInstalledForUser(String packageName, int userId) {
try {
return mPackageManager.getPackageInfoAsUser(packageName, 0, userId) != null;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index 07d4065e..a8e3c7e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -117,7 +117,7 @@
for (int userId : mRemoveUsers) {
um.removeUser(userId);
}
- setUserTypePackageWhitelistMode(mOriginalWhitelistMode);
+ setUserTypePackageAllowlistMode(mOriginalWhitelistMode);
}
/**
@@ -184,7 +184,7 @@
}
}
- final ArrayMap<String, Long> expectedOutput = getNewPackageToWhitelistedBitSetMap();
+ final ArrayMap<String, Long> expectedOutput = getNewPackageToAllowlistedBitSetMap();
expectedOutput.put("com.android.package1", expectedUserTypeBitSet1);
expectedOutput.put("com.android.package2", expectedUserTypeBitSet2);
expectedOutput.put("com.android.package3", expectedUserTypeBitSet3);
@@ -227,7 +227,7 @@
}
};
- final ArrayMap<String, Long> expectedOutput = getNewPackageToWhitelistedBitSetMap();
+ final ArrayMap<String, Long> expectedOutput = getNewPackageToAllowlistedBitSetMap();
expectedOutput.put("com.android.package2", 0L);
expectedOutput.put("com.android.package3", 0L);
expectedOutput.put("com.android.package4", 0L);
@@ -340,7 +340,7 @@
public void testPackagesForCreateUser_full() {
final String userTypeToCreate = USER_TYPE_FULL_SECONDARY;
final long userTypeMask = mUserSystemPackageInstaller.getUserTypeMask(userTypeToCreate);
- setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
+ setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
PackageManager pm = mContext.getPackageManager();
final SystemConfig sysConfig = new SystemConfigTestClass(true);
@@ -384,7 +384,7 @@
*/
@Test
public void testInstallOverlayPackagesExplicitMode() {
- setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
+ setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
final String[] userTypes = new String[]{"type"};
final long maskOfType = 0b0001L;
@@ -453,49 +453,49 @@
*/
@Test
public void testSetWhitelistEnabledMode() {
- setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE);
+ setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertFalse(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode());
assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
- setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_LOG);
+ setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_LOG);
assertTrue(mUserSystemPackageInstaller.isLogMode());
assertFalse(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode());
assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
- setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
+ setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertTrue(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode());
assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
- setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST);
+ setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertFalse(mUserSystemPackageInstaller.isEnforceMode());
assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode());
assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
- setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST_SYSTEM);
+ setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST_SYSTEM);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertFalse(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode());
assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
- setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA);
+ setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertFalse(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode());
assertTrue(mUserSystemPackageInstaller.isIgnoreOtaMode());
- setUserTypePackageWhitelistMode(
+ setUserTypePackageAllowlistMode(
USER_TYPE_PACKAGE_WHITELIST_MODE_LOG | USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
assertTrue(mUserSystemPackageInstaller.isLogMode());
assertTrue(mUserSystemPackageInstaller.isEnforceMode());
@@ -503,7 +503,7 @@
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistSystemMode());
assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
- setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST
+ setUserTypePackageAllowlistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST
| USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertTrue(mUserSystemPackageInstaller.isEnforceMode());
@@ -513,7 +513,7 @@
}
/** Sets the allowlist mode to the desired value via adb's setprop. */
- private void setUserTypePackageWhitelistMode(int mode) {
+ private void setUserTypePackageAllowlistMode(int mode) {
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
try {
String result = uiDevice.executeShellCommand(String.format("setprop %s %d",
@@ -526,7 +526,7 @@
}
/** @see UserSystemPackageInstaller#mWhitelistedPackagesForUserTypes */
- private ArrayMap<String, Long> getNewPackageToWhitelistedBitSetMap() {
+ private ArrayMap<String, Long> getNewPackageToAllowlistedBitSetMap() {
final ArrayMap<String, Long> pkgBitSetMap = new ArrayMap<>();
// "android" is always treated as allowlisted for all types, regardless of the xml file.
pkgBitSetMap.put("android", ~0L);
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
index 560a919..20c0b0a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -529,7 +529,6 @@
"}",
":",
"?",
- "-",
"%",
"^",
"*",
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 9fca513..ee3ab97 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -149,6 +149,32 @@
}
@Test
+ public void testCreateHintSession_exceedsLimit() throws Exception {
+ HintManagerService service = createService();
+ IBinder token1 = new Binder();
+ IBinder token2 = new Binder();
+
+ for (int i = 0; i < 10; i++) {
+ IHintSession a = service.getBinderServiceInstance().createHintSession(token1,
+ SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
+ assertNotNull(a);
+ }
+
+ for (int i = 0; i < 10; i++) {
+ IHintSession b = service.getBinderServiceInstance().createHintSession(token2,
+ SESSION_TIDS_B, DEFAULT_TARGET_DURATION);
+ assertNotNull(b);
+ }
+
+ assertThrows(IllegalStateException.class,
+ () -> service.getBinderServiceInstance().createHintSession(token1, SESSION_TIDS_A,
+ DEFAULT_TARGET_DURATION));
+ assertThrows(IllegalStateException.class,
+ () -> service.getBinderServiceInstance().createHintSession(token2, SESSION_TIDS_B,
+ DEFAULT_TARGET_DURATION));
+ }
+
+ @Test
public void testPauseResumeHintSession() throws Exception {
HintManagerService service = createService();
IBinder token = new Binder();
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/OWNERS b/services/tests/servicestests/src/com/android/server/power/hint/OWNERS
new file mode 100644
index 0000000..c28c07a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/hint/OWNERS
@@ -0,0 +1,2 @@
+include /ADPF_OWNERS
+
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
index 22d383a..fc5213c 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
@@ -82,7 +82,7 @@
uptimeMillis = uptimeMillis - SystemClock.uptimeMillis() + mClock.getAsLong();
}
- // post a dummy queue entry to keep track of message removal
+ // post a sentinel queue entry to keep track of message removal
return super.sendMessageAtTime(msg, Long.MAX_VALUE)
&& mMessages.add(new MsgInfo(Message.obtain(msg), uptimeMillis, mMessageCount));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e1f3c2b..579bbc8 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6044,6 +6044,49 @@
}
@Test
+ public void testVisitUris_styleExtrasWithoutStyle() {
+ Notification notification = new Notification.Builder(mContext, "a")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .build();
+
+ Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(
+ personWithIcon("content://user"))
+ .addHistoricMessage(new Notification.MessagingStyle.Message("Heyhey!",
+ System.currentTimeMillis(),
+ personWithIcon("content://historicalMessenger")))
+ .addMessage(new Notification.MessagingStyle.Message("Are you there",
+ System.currentTimeMillis(),
+ personWithIcon("content://messenger")))
+ .setShortcutIcon(
+ Icon.createWithContentUri("content://conversationShortcut"));
+ messagingStyle.addExtras(notification.extras); // Instead of Builder.setStyle(style).
+
+ Notification.CallStyle callStyle = Notification.CallStyle.forOngoingCall(
+ personWithIcon("content://caller"),
+ PendingIntent.getActivity(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE))
+ .setVerificationIcon(Icon.createWithContentUri("content://callVerification"));
+ callStyle.addExtras(notification.extras); // Same.
+
+ Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
+ notification.visitUris(visitor);
+
+ verify(visitor).accept(eq(Uri.parse("content://user")));
+ verify(visitor).accept(eq(Uri.parse("content://historicalMessenger")));
+ verify(visitor).accept(eq(Uri.parse("content://messenger")));
+ verify(visitor).accept(eq(Uri.parse("content://conversationShortcut")));
+ verify(visitor).accept(eq(Uri.parse("content://caller")));
+ verify(visitor).accept(eq(Uri.parse("content://callVerification")));
+ }
+
+ private static Person personWithIcon(String iconUri) {
+ return new Person.Builder()
+ .setName("Mr " + iconUri)
+ .setIcon(Icon.createWithContentUri(iconUri))
+ .build();
+ }
+
+ @Test
public void testVisitUris_wearableExtender() {
Icon actionIcon = Icon.createWithContentUri("content://media/action");
Icon wearActionIcon = Icon.createWithContentUri("content://media/wearAction");
@@ -7753,7 +7796,8 @@
public void testOnNotificationActionClick() {
final int actionIndex = 2;
final Notification.Action action =
- new Notification.Action.Builder(null, "text", null).build();
+ new Notification.Action.Builder(null, "text", PendingIntent.getActivity(
+ mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build();
final boolean generatedByAssistant = false;
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
@@ -7777,7 +7821,8 @@
public void testOnAssistantNotificationActionClick() {
final int actionIndex = 1;
final Notification.Action action =
- new Notification.Action.Builder(null, "text", null).build();
+ new Notification.Action.Builder(null, "text", PendingIntent.getActivity(
+ mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build();
final boolean generatedByAssistant = true;
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index fae92d9..f83a1df 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -36,7 +36,7 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -44,7 +44,6 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
-import android.app.IActivityManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
@@ -76,6 +75,7 @@
import com.android.internal.R;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.LocalServices;
import com.android.server.UiServiceTestCase;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -850,84 +850,78 @@
@Test
public void testCalculateGrantableUris_PappProvided() {
- IActivityManager am = mock(IActivityManager.class);
UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
anyInt(), anyInt())).thenThrow(new SecurityException());
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugm);
+
channel.setSound(null, null);
Notification n = new Notification.Builder(mContext, channel.getId())
.setSmallIcon(Icon.createWithContentUri(Uri.parse("content://something")))
.build();
StatusBarNotification sbn =
new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- record.mAm = am;
- record.mUgmInternal = ugm;
- try {
- record.calculateGrantableUris();
- fail("App provided uri for p targeting app should throw exception");
- } catch (SecurityException e) {
- // expected
- }
+ assertThrows("App provided uri for p targeting app should throw exception",
+ SecurityException.class,
+ () -> new NotificationRecord(mMockContext, sbn, channel));
}
@Test
public void testCalculateGrantableUris_PappProvided_invalidSound() {
- IActivityManager am = mock(IActivityManager.class);
UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
anyInt(), anyInt())).thenThrow(new SecurityException());
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugm);
+
channel.setSound(Uri.parse("content://something"), mock(AudioAttributes.class));
Notification n = mock(Notification.class);
when(n.getChannelId()).thenReturn(channel.getId());
StatusBarNotification sbn =
new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- record.mAm = am;
- record.mUgmInternal = ugm;
- record.calculateGrantableUris();
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound());
}
@Test
public void testCalculateGrantableUris_PuserOverridden() {
- IActivityManager am = mock(IActivityManager.class);
UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
anyInt(), anyInt())).thenThrow(new SecurityException());
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugm);
+
channel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
Notification n = mock(Notification.class);
when(n.getChannelId()).thenReturn(channel.getId());
StatusBarNotification sbn =
new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- record.mAm = am;
- record.calculateGrantableUris();
+ new NotificationRecord(mMockContext, sbn, channel); // should not throw
}
@Test
public void testCalculateGrantableUris_prePappProvided() {
- IActivityManager am = mock(IActivityManager.class);
UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
anyInt(), anyInt())).thenThrow(new SecurityException());
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugm);
+
Notification n = mock(Notification.class);
when(n.getChannelId()).thenReturn(channel.getId());
StatusBarNotification sbn =
new StatusBarNotification(PKG_O, PKG_O, id1, tag1, uid, uid, n, mUser, null, uid);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
- record.mAm = am;
- record.calculateGrantableUris();
- // should not throw
+ new NotificationRecord(mMockContext, sbn, channel); // should not throw
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 3bb86a7..b9492e9 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -170,10 +170,15 @@
}
private void pressKey(int keyCode, long pressTime, boolean interactive) {
+ pressKey(keyCode, pressTime, interactive, false /* defaultDisplayOn */);
+ }
+
+ private void pressKey(
+ int keyCode, long pressTime, boolean interactive, boolean defaultDisplayOn) {
long eventTime = SystemClock.uptimeMillis();
final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN,
keyCode, 0 /* repeat */, 0 /* metaState */);
- mDetector.interceptKey(keyDown, interactive);
+ mDetector.interceptKey(keyDown, interactive, defaultDisplayOn);
// keep press down.
try {
@@ -186,7 +191,7 @@
final KeyEvent keyUp = new KeyEvent(eventTime, eventTime, ACTION_UP,
keyCode, 0 /* repeat */, 0 /* metaState */);
- mDetector.interceptKey(keyUp, interactive);
+ mDetector.interceptKey(keyUp, interactive, defaultDisplayOn);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
index ce1a46b..1f9e8c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -16,10 +16,8 @@
package com.android.server.wm;
-import static android.Manifest.permission.ACCESS_SURFACE_FLINGER;
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
-import static android.server.wm.CtsWindowInfoUtils.waitForWindowVisible;
import static android.server.wm.CtsWindowInfoUtils.waitForWindowFocus;
+import static android.server.wm.CtsWindowInfoUtils.waitForWindowVisible;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static org.junit.Assert.assertTrue;
@@ -78,17 +76,11 @@
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
-
- // ACCESS_SURFACE_FLINGER is necessary to call waitForWindow
- // INTERNAL_SYSTEM_WINDOW is necessary to add SCVH with no host parent
- mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(ACCESS_SURFACE_FLINGER,
- INTERNAL_SYSTEM_WINDOW);
mActivity = mActivityRule.launchActivity(null);
}
@After
public void tearDown() {
- mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
CommonUtils.waitUntilActivityRemoved(mActivity);
}
@@ -102,14 +94,15 @@
mView2 = new Button(mActivity);
mInstrumentation.runOnMainSync(() -> {
- TestWindowlessWindowManager wwm = new TestWindowlessWindowManager(
- mActivity.getResources().getConfiguration(), sc, null);
-
try {
mActivity.attachToSurfaceView(sc);
} catch (InterruptedException e) {
}
+ TestWindowlessWindowManager wwm = new TestWindowlessWindowManager(
+ mActivity.getResources().getConfiguration(), sc,
+ mActivity.mSurfaceView.getHostToken());
+
mScvh1 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
wwm, "requestFocusWithMultipleWindows");
mScvh2 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
@@ -130,11 +123,13 @@
assertTrue("Failed to wait for view1", waitForWindowVisible(mView1));
assertTrue("Failed to wait for view2", waitForWindowVisible(mView2));
- WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+ IWindow window = IWindow.Stub.asInterface(mActivity.mSurfaceView.getWindowToken());
+
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
mScvh1.getFocusGrantToken(), true);
assertTrue("Failed to gain focus for view1", waitForWindowFocus(mView1, true));
- WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
mScvh2.getFocusGrantToken(), true);
assertTrue("Failed to gain focus for view2", waitForWindowFocus(mView2, true));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index 16c38ac..08438c8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -354,25 +354,37 @@
}
});
- final LandscapeActivity activity =
- (LandscapeActivity) startTestActivity(LandscapeActivity.class);
+ final boolean isIgnoringOrientationRequest =
+ CommonUtils.getIgnoreOrientationRequest(Display.DEFAULT_DISPLAY);
+ if (isIgnoringOrientationRequest) {
+ CommonUtils.setIgnoreOrientationRequest(Display.DEFAULT_DISPLAY, false);
+ }
- int[] taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue,
- candidate -> candidate[0] == activity.getTaskId());
- assertNotNull(taskIdAndOrientation);
- assertEquals(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE, taskIdAndOrientation[1]);
+ try {
+ final LandscapeActivity activity =
+ (LandscapeActivity) startTestActivity(LandscapeActivity.class);
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
- taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue,
- candidate -> candidate[0] == activity.getTaskId());
- assertNotNull(taskIdAndOrientation);
- assertEquals(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT, taskIdAndOrientation[1]);
+ int[] taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue,
+ candidate -> candidate[0] == activity.getTaskId());
+ assertNotNull(taskIdAndOrientation);
+ assertEquals(
+ ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE, taskIdAndOrientation[1]);
- activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue,
- candidate -> candidate[0] == activity.getTaskId());
- assertNotNull(taskIdAndOrientation);
- assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, taskIdAndOrientation[1]);
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
+ taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue,
+ candidate -> candidate[0] == activity.getTaskId());
+ assertNotNull(taskIdAndOrientation);
+ assertEquals(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT, taskIdAndOrientation[1]);
+
+ activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+ taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue,
+ candidate -> candidate[0] == activity.getTaskId());
+ assertNotNull(taskIdAndOrientation);
+ assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, taskIdAndOrientation[1]);
+ } finally {
+ CommonUtils.setIgnoreOrientationRequest(
+ Display.DEFAULT_DISPLAY, isIgnoringOrientationRequest);
+ }
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 07cdfaf..873d09b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -39,6 +39,7 @@
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_SYNC;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static android.window.TransitionInfo.isIndependent;
@@ -1190,6 +1191,7 @@
final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
makeWindowVisible(statusBar);
mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
+ final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
final ActivityRecord app = createActivityRecord(mDisplayContent);
final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
app.mTransitionController.requestStartTransition(transition, app.getTask(),
@@ -1219,9 +1221,17 @@
mDisplayContent.mTransitionController.dispatchLegacyAppTransitionFinished(app);
assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
+ // The bar was invisible so it is not handled by the controller. But if it becomes visible
+ // and drawn before the transition starts,
+ assertFalse(asyncRotationController.isTargetToken(navBar.mToken));
+ navBar.finishDrawing(null /* postDrawTransaction */, Integer.MAX_VALUE);
+ assertTrue(asyncRotationController.isTargetToken(navBar.mToken));
+
player.startTransition();
// Non-app windows should not be collected.
assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken));
+ // Avoid DeviceStateController disturbing the test by triggering another rotation change.
+ doReturn(false).when(mDisplayContent).updateRotationUnchecked();
onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted();
assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(
@@ -2390,6 +2400,37 @@
assertFalse(controller.isCollecting());
}
+ @Test
+ public void testNoSyncFlagIfOneTrack() {
+ final TransitionController controller = mAtm.getTransitionController();
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+
+ mSyncEngine = createTestBLASTSyncEngine();
+ controller.setSyncEngine(mSyncEngine);
+
+ final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
+ final Transition transitB = createTestTransition(TRANSIT_OPEN, controller);
+ final Transition transitC = createTestTransition(TRANSIT_OPEN, controller);
+
+ controller.startCollectOrQueue(transitA, (deferred) -> {});
+ controller.startCollectOrQueue(transitB, (deferred) -> {});
+ controller.startCollectOrQueue(transitC, (deferred) -> {});
+
+ // Verify that, as-long as there is <= 1 track, we won't get a SYNC flag
+ transitA.start();
+ transitA.setAllReady();
+ mSyncEngine.tryFinishForTest(transitA.getSyncId());
+ assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
+ transitB.start();
+ transitB.setAllReady();
+ mSyncEngine.tryFinishForTest(transitB.getSyncId());
+ assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
+ transitC.start();
+ transitC.setAllReady();
+ mSyncEngine.tryFinishForTest(transitC.getSyncId());
+ assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
+ }
+
private static void makeTaskOrganized(Task... tasks) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
for (Task t : tasks) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/CommonUtils.java b/services/tests/wmtests/src/com/android/server/wm/utils/CommonUtils.java
index ed23296..bfbba5f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/CommonUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/CommonUtils.java
@@ -21,9 +21,12 @@
import android.app.Activity;
import android.app.KeyguardManager;
import android.app.UiAutomation;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
+import android.view.IWindowManager;
import android.view.KeyEvent;
+import android.view.WindowManagerGlobal;
import androidx.test.uiautomator.UiDevice;
@@ -48,6 +51,37 @@
}
}
+ public static boolean getIgnoreOrientationRequest(int displayId) {
+ final UiDevice uiDevice = UiDevice.getInstance(getInstrumentation());
+ final String result;
+ try {
+ result = uiDevice.executeShellCommand("cmd window get-ignore-orientation-request -d "
+ + displayId);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ final String[] tokens = result.split(" ");
+ if (tokens.length != 4) {
+ throw new RuntimeException("Expecting a result with 4 tokens, but got " + result);
+ }
+
+ // The output looks like "ignoreOrientationRequest true for displayId=0"
+ return Boolean.parseBoolean(tokens[1]);
+ }
+
+ public static void setIgnoreOrientationRequest(
+ int displayId, boolean ignoreOrientationRequest) {
+ runWithShellPermissionIdentity(() -> {
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ try {
+ wm.setIgnoreOrientationRequest(displayId, ignoreOrientationRequest);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
/** Dismisses the Keyguard if it is locked. */
public static void dismissKeyguard() {
final KeyguardManager keyguardManager = getInstrumentation().getContext().getSystemService(
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 58da4b43..3d78a1d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -54,6 +54,7 @@
import android.service.voice.HotwordDetectionService;
import android.service.voice.HotwordDetectionServiceFailure;
import android.service.voice.HotwordDetector;
+import android.service.voice.IDetectorSessionStorageService;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.ISandboxedDetectionService;
import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
@@ -69,6 +70,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVisualQueryDetectionAttentionListener;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
import com.android.server.LocalServices;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -157,6 +159,8 @@
@NonNull private ServiceConnection mRemoteVisualQueryDetectionService;
@GuardedBy("mLock")
@Nullable private IBinder mAudioFlinger;
+
+ @Nullable private IHotwordRecognitionStatusCallback mHotwordRecognitionCallback;
@GuardedBy("mLock")
private boolean mDebugHotwordLogging = false;
@@ -694,6 +698,7 @@
updateContentCaptureManager(connection);
updateSpeechService(connection);
updateServiceIdentity(connection);
+ updateStorageService(connection);
return connection;
}
}
@@ -910,6 +915,7 @@
mVoiceInteractionServiceUid, mVoiceInteractorIdentity,
mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener);
}
+ mHotwordRecognitionCallback = callback;
mDetectorSessions.put(detectorType, session);
session.initialize(options, sharedMemory);
}
@@ -1035,6 +1041,23 @@
}));
}
+ private void updateStorageService(ServiceConnection connection) {
+ connection.run(service -> {
+ service.registerRemoteStorageService(new IDetectorSessionStorageService.Stub() {
+ @Override
+ public void openFile(String filename, AndroidFuture future)
+ throws RemoteException {
+ Slog.v(TAG, "BinderCallback#onFileOpen");
+ try {
+ mHotwordRecognitionCallback.onOpenFile(filename, future);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ });
+ });
+ }
+
private void addServiceUidForAudioPolicy(int uid) {
mScheduledExecutorService.execute(() -> {
AudioManagerInternal audioManager =
diff --git a/telecomm/OWNERS b/telecomm/OWNERS
index dcaf858..b57b7c7 100644
--- a/telecomm/OWNERS
+++ b/telecomm/OWNERS
@@ -4,7 +4,6 @@
tgunn@google.com
xiaotonj@google.com
rgreenwalt@google.com
-chinmayd@google.com
grantmenke@google.com
pmadapurmath@google.com
tjstuart@google.com
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 5bdcdf4..eb0a1e1 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -361,18 +361,6 @@
"android.telecom.extra.DIAGNOSTIC_MESSAGE";
/**
- * Event reported from the Telecom stack to indicate that the {@link Connection} is not able to
- * find any network and likely will not get connected. Upon receiving this event, the dialer
- * app should show satellite SOS button if satellite is provisioned.
- * <p>
- * The dialer app receives this event via
- * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
- * @hide
- */
- public static final String EVENT_DISPLAY_SOS_MESSAGE =
- "android.telecom.event.DISPLAY_SOS_MESSAGE";
-
- /**
* Reject reason used with {@link #reject(int)} to indicate that the user is rejecting this
* call because they have declined to answer it. This typically means that they are unable
* to answer the call at this time and would prefer it be sent to voicemail.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 943d8d6..4a541da 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3388,7 +3388,11 @@
public void onAbort() {}
/**
- * Notifies this Connection of a request to hold.
+ * Notifies this Connection of a request to hold. {@link Connection#setOnHold} should be within
+ * the onHold() body in order to transition the call state to {@link Connection#STATE_HOLDING}.
+ * <p>
+ * Note: If the Connection does not transition to {@link Connection#STATE_HOLDING} within 2
+ * seconds, the call will be disconnected.
*/
public void onHold() {}
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 6997f3c7..f9844bc 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -362,7 +362,6 @@
/**
* Indicates that the call was unable to be made because the satellite modem is enabled.
- * @hide
*/
public static final int SATELLITE_ENABLED = 82;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f1ee76e..5f67441 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -76,7 +76,9 @@
import android.service.carrier.CarrierIdentifier;
import android.service.carrier.CarrierService;
import android.sysprop.TelephonyProperties;
+import android.telecom.Call;
import android.telecom.CallScreeningService;
+import android.telecom.Connection;
import android.telecom.InCallService;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
@@ -1186,6 +1188,17 @@
"android.telephony.event.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION";
/**
+ * Event reported from the Telephony stack to indicate that the {@link Connection} is not
+ * able to find any network and likely will not get connected. Upon receiving this event,
+ * the dialer app should show satellite SOS button if satellite is provisioned.
+ * <p>
+ * The dialer app receives this event via
+ * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
+ */
+ public static final String EVENT_DISPLAY_SOS_MESSAGE =
+ "android.telephony.event.DISPLAY_SOS_MESSAGE";
+
+ /**
* Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates
* the type of supplementary service notification which occurred.
* Will be either
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java b/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java
index 0f694c2..fe91260 100644
--- a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java
+++ b/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java
@@ -18,5 +18,5 @@
import android.app.Activity;
-/** Dummy class just to generate some dex */
+/** Placeholder class just to generate some dex */
public class DummyActivity extends Activity {}
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java b/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java
index 837c7be..a7bd771 100644
--- a/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java
+++ b/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java
@@ -18,5 +18,5 @@
import android.app.Activity;
-/** Dummy class just to generate some dex */
+/** Placeholder class just to generate some dex */
public class DummyActivity extends Activity {}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphExporter.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphExporter.java
index 0013965..4b7ca53 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphExporter.java
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/GraphExporter.java
@@ -113,7 +113,7 @@
getDotName(target.getFilter().getName()) + ":" +
getDotName(target.getName()) + "_IN;\n" );
} else {
- // Found a unconnected output port, add dummy node
+ // Found a unconnected output port, add placeholder node
String color = filter.getSignature().getOutputPortInfo(portName).isRequired()
? "red" : "blue"; // red for unconnected, required ports
dotFile.write(" " +
@@ -131,7 +131,7 @@
if(target != null) {
// Found a connection -- nothing to do, connections have been written out above
} else {
- // Found a unconnected input port, add dummy node
+ // Found a unconnected input port, add placeholder node
String color = filter.getSignature().getInputPortInfo(portName).isRequired()
? "red" : "blue"; // red for unconnected, required ports
dotFile.write(" " +
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MffContext.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MffContext.java
index b7212f9..6bd6c18 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MffContext.java
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MffContext.java
@@ -66,9 +66,9 @@
/**
* On older Android versions the Camera may need a SurfaceView to render into in order to
- * function. You may specify a dummy SurfaceView here if you do not want the context to
+ * function. You may specify a placeholder SurfaceView here if you do not want the context to
* create its own view. Note, that your view may or may not be used. You cannot rely on
- * your dummy view to be used by the Camera. If you pass null, no dummy view will be used.
+ * your placeholder view to be used by the Camera. If you pass null, no placeholder view will be used.
* In this case your application may not run correctly on older devices if you use the
* camera. This flag has no effect if you do not require the camera.
*/
@@ -104,7 +104,7 @@
/** The current context state. */
private State mState = new State();
- /** A dummy SurfaceView that is required for Camera operation on older devices. */
+ /** A placeholder SurfaceView that is required for Camera operation on older devices. */
private SurfaceView mDummySurfaceView = null;
/** Handler to execute code in the context's thread, such as issuing callbacks. */
@@ -126,7 +126,7 @@
* multiple MffContexts, however data between them cannot be shared. The context must be
* created in a thread with a Looper (such as the main/UI thread).
*
- * On older versions of Android, the MffContext may create a visible dummy view for the
+ * On older versions of Android, the MffContext may create a visible placeholder view for the
* camera to render into. This is a 1x1 SurfaceView that is placed into the top-left corner.
*
* @param context The application context to attach the MffContext to.
@@ -142,7 +142,7 @@
* multiple MffContexts, however data between them cannot be shared. The context must be
* created in a thread with a Looper (such as the main/UI thread).
*
- * On older versions of Android, the MffContext may create a visible dummy view for the
+ * On older versions of Android, the MffContext may create a visible placeholder view for the
* camera to render into. This is a 1x1 SurfaceView that is placed into the top-left corner.
* You may alternatively specify your own SurfaceView in the configuration.
*
diff --git a/tests/DynamicCodeLoggerIntegrationTests/src/com/android/dcl/Simple.java b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/dcl/Simple.java
index e995a26..2ca91fb 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/src/com/android/dcl/Simple.java
+++ b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/dcl/Simple.java
@@ -16,7 +16,7 @@
package com.android.dcl;
-/** Dummy class which is built into a jar purely so we can pass it to DexClassLoader. */
+/** Placeholder class which is built into a jar purely so we can pass it to DexClassLoader. */
public final class Simple {
public Simple() {}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
new file mode 100644
index 0000000..87231c8
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -0,0 +1,189 @@
+/*
+ * 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.wm.flicker.activityembedding.splitscreen
+
+import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
+import android.tools.common.datatypes.Rect
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.device.traces.parsers.toFlickerComponent
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.flicker.utils.*
+import org.junit.FixMethodOrder
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/***
+ * Test entering System SplitScreen with Activity Embedding Split and another app.
+ *
+ * Setup: Launch A|B in split and secondaryApp, return to home.
+ * Transitions: Let AE Split A|B enter splitscreen with secondaryApp. Resulting in A|B|secondaryApp.
+ *
+ * To run this test: `atest FlickerTestsOther:EnterSystemSplitTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class EnterSystemSplitTest(flicker: LegacyFlickerTest) :
+ ActivityEmbeddingTestBase(flicker) {
+
+ private val secondaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ testApp.launchViaIntent(wmHelper)
+ testApp.launchSecondaryActivity(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds ?:
+ error("Display not found")
+ }
+ transitions {
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, testApp, secondaryApp)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, testApp, secondaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = flicker.splitScreenDividerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun activityEmbeddingSplitLayerBecomesVisible() {
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ testApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
+ }
+
+ @Presubmit
+ @Test
+ fun activityEmbeddingSplitWindowBecomesVisible() = flicker.appWindowIsVisibleAtEnd(testApp)
+
+ @Presubmit
+ @Test
+ fun secondaryLayerBecomesVisible() {
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
+ }
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = flicker.appWindowIsVisibleAtEnd(secondaryApp)
+
+ /**
+ * After the transition there should be both ActivityEmbedding activities,
+ * SplitScreenPrimaryActivity and the system split divider on screen.
+ * Verify the layers are in expected sizes.
+ */
+ @Presubmit
+ @Test
+ fun activityEmbeddingSplitSurfaceAreEven() {
+ flicker.assertLayersEnd {
+ val leftAELayerRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ val rightAELayerRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ val secondaryAppLayerRegion =
+ visibleRegion(
+ ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
+ val systemDivider = visibleRegion(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ leftAELayerRegion
+ .plus(rightAELayerRegion.region)
+ .plus(secondaryAppLayerRegion.region)
+ .plus(systemDivider.region)
+ .coversExactly(startDisplayBounds)
+ check { "ActivityEmbeddingSplitHeight" }
+ .that(leftAELayerRegion.region.height)
+ .isEqual(rightAELayerRegion.region.height)
+ check { "SystemSplitHeight" }
+ .that(rightAELayerRegion.region.height)
+ .isEqual(secondaryAppLayerRegion.region.height)
+ // TODO(b/292283182): Remove this special case handling.
+ check { "ActivityEmbeddingSplitWidth" }
+ .that(Math.abs(
+ leftAELayerRegion.region.width - rightAELayerRegion.region.width))
+ .isLower(2)
+ check { "SystemSplitWidth" }
+ .that(Math.abs(secondaryAppLayerRegion.region.width -
+ 2 * rightAELayerRegion.region.width))
+ .isLower(2)
+ }
+ }
+
+ /**
+ * Verify the windows are in expected sizes.
+ */
+ @Presubmit
+ @Test
+ fun activityEmbeddingSplitWindowsAreEven() {
+ flicker.assertWmEnd {
+ val leftAEWindowRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ val rightAEWindowRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ // There's no window for the divider bar.
+ val secondaryAppLayerRegion =
+ visibleRegion(
+ ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
+ check { "ActivityEmbeddingSplitHeight" }
+ .that(leftAEWindowRegion.region.height)
+ .isEqual(rightAEWindowRegion.region.height)
+ check { "SystemSplitHeight" }
+ .that(rightAEWindowRegion.region.height)
+ .isEqual(secondaryAppLayerRegion.region.height)
+ check { "ActivityEmbeddingSplitWidth" }
+ .that(Math.abs(
+ leftAEWindowRegion.region.width - rightAEWindowRegion.region.width))
+ .isLower(2)
+ check { "SystemSplitWidth" }
+ .that(Math.abs(secondaryAppLayerRegion.region.width -
+ 2 * rightAEWindowRegion.region.width))
+ .isLower(2)
+ }
+ }
+
+ @Ignore("Not applicable to this CUJ.")
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {}
+
+ companion object {
+ /** {@inheritDoc} */
+ private var startDisplayBounds = Rect.EMPTY
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 704798e..f867c98 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -70,7 +70,7 @@
<activity android:name=".SeamlessRotationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
android:theme="@style/CutoutShortEdges"
- android:configChanges="orientation|screenSize"
+ android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"
android:label="SeamlessActivity"
android:exported="true">
<intent-filter>
diff --git a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java
new file mode 100644
index 0000000..1b98887
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 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.input;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+import android.view.ViewConfiguration;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest FocusEventDebugViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class FocusEventDebugViewTest {
+
+ private FocusEventDebugView mFocusEventDebugView;
+ private FocusEventDebugView.RotaryInputValueView mRotaryInputValueView;
+ private FocusEventDebugView.RotaryInputGraphView mRotaryInputGraphView;
+ private float mScaledVerticalScrollFactor;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ mScaledVerticalScrollFactor =
+ ViewConfiguration.get(context).getScaledVerticalScrollFactor();
+ InputManagerService mockService = mock(InputManagerService.class);
+ when(mockService.monitorInput(anyString(), anyInt()))
+ .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]);
+
+ mRotaryInputValueView = new FocusEventDebugView.RotaryInputValueView(context);
+ mRotaryInputGraphView = new FocusEventDebugView.RotaryInputGraphView(context);
+ mFocusEventDebugView = new FocusEventDebugView(context, mockService,
+ () -> mRotaryInputValueView, () -> mRotaryInputGraphView);
+ }
+
+ @Test
+ public void startsRotaryInputValueViewWithDefaultValue() {
+ assertEquals("+0.0", mRotaryInputValueView.getText());
+ }
+
+ @Test
+ public void startsRotaryInputGraphViewWithDefaultFrameCenter() {
+ assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01);
+ }
+
+ @Test
+ public void handleRotaryInput_updatesRotaryInputValueViewWithScrollValue() {
+ mFocusEventDebugView.handleUpdateShowRotaryInput(true);
+
+ mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f));
+
+ assertEquals(String.format("+%.1f", 0.5f * mScaledVerticalScrollFactor),
+ mRotaryInputValueView.getText());
+ }
+
+ @Test
+ public void handleRotaryInput_translatesRotaryInputGraphViewWithHighScrollValue() {
+ mFocusEventDebugView.handleUpdateShowRotaryInput(true);
+
+ mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(1000f));
+
+ assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0);
+ }
+
+ @Test
+ public void updateActivityStatus_setsAndRemovesColorFilter() {
+ // It should not be active initially.
+ assertNull(mRotaryInputValueView.getBackground().getColorFilter());
+
+ mRotaryInputValueView.updateActivityStatus(true);
+ // It should be active after rotary input.
+ assertNotNull(mRotaryInputValueView.getBackground().getColorFilter());
+
+ mRotaryInputValueView.updateActivityStatus(false);
+ // It should not be active after waiting for mUpdateActivityStatusCallback.
+ assertNull(mRotaryInputValueView.getBackground().getColorFilter());
+ }
+
+ private MotionEvent createRotaryMotionEvent(float scrollAxisValue) {
+ PointerCoords pointerCoords = new PointerCoords();
+ pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue);
+ PointerProperties pointerProperties = new PointerProperties();
+
+ return MotionEvent.obtain(
+ /* downTime */ 0,
+ /* eventTime */ 0,
+ /* action */ MotionEvent.ACTION_SCROLL,
+ /* pointerCount */ 1,
+ /* pointerProperties */ new PointerProperties[] {pointerProperties},
+ /* pointerCoords */ new PointerCoords[] {pointerCoords},
+ /* metaState */ 0,
+ /* buttonState */ 0,
+ /* xPrecision */ 0,
+ /* yPrecision */ 0,
+ /* deviceId */ 0,
+ /* edgeFlags */ 0,
+ /* source */ InputDevice.SOURCE_ROTARY_ENCODER,
+ /* flags */ 0
+ );
+ }
+}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
index c9b5c96..12556bc 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
@@ -154,7 +154,7 @@
* <p>The given {@code pred} will be called on the main thread.
*/
public static void waitOnMainUntil(String message, Callable<Boolean> pred) {
- eventually(() -> assertWithMessage(message).that(pred.call()).isTrue(), TIMEOUT);
+ eventually(() -> assertWithMessage(message).that(callOnMainSync(pred)).isTrue(), TIMEOUT);
}
/** Waits until IME is shown, or throws on timeout. */
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
index 31ea832..c92d768 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
@@ -16,10 +16,6 @@
package com.android.test.silkfx.hdr
-import android.animation.AnimatorSet
-import android.animation.ObjectAnimator
-import android.animation.ValueAnimator
-import android.animation.ValueAnimator.AnimatorUpdateListener
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
@@ -46,7 +42,7 @@
private var selectedImage = -1
private var outputMode = R.id.output_hdr
private var bitmap: Bitmap? = null
- private var gainmap: Gainmap? = null
+ private var originalGainmap: Gainmap? = null
private var gainmapVisualizer: Bitmap? = null
private lateinit var imageView: SubsamplingScaleImageView
private lateinit var gainmapMetadataEditor: GainmapMetadataEditor
@@ -70,7 +66,6 @@
it.check(outputMode)
it.setOnCheckedChangeListener { _, checkedId ->
outputMode = checkedId
- // Intentionally don't do anything fancy so that mode A/B comparisons are easy
updateDisplay()
}
}
@@ -101,41 +96,10 @@
imageView.apply {
isClickable = true
- // Example of animating between SDR and HDR using gainmap params; animates HDR->SDR->HDR
- // with a brief pause on SDR. The key thing here is that the gainmap's
- // minDisplayRatioForHdrTransition is animated between its original value (for full HDR)
- // and displayRatioForFullHdr (for full SDR). The view must also be invalidated during
- // the animation for the updates to take effect.
setOnClickListener {
- if (gainmap != null && (outputMode == R.id.output_hdr ||
- outputMode == R.id.output_hdr_test)) {
- val animationLengthMs: Long = 500
- val updateListener = object : AnimatorUpdateListener {
- override fun onAnimationUpdate(animation: ValueAnimator) {
- imageView.invalidate()
- }
- }
- val hdrToSdr = ObjectAnimator.ofFloat(
- gainmap, "minDisplayRatioForHdrTransition",
- gainmap!!.minDisplayRatioForHdrTransition,
- gainmap!!.displayRatioForFullHdr).apply {
- duration = animationLengthMs
- addUpdateListener(updateListener)
- }
- val sdrToHdr = ObjectAnimator.ofFloat(
- gainmap, "minDisplayRatioForHdrTransition",
- gainmap!!.displayRatioForFullHdr,
- gainmap!!.minDisplayRatioForHdrTransition).apply {
- duration = animationLengthMs
- addUpdateListener(updateListener)
- }
-
- AnimatorSet().apply {
- play(hdrToSdr)
- play(sdrToHdr).after(animationLengthMs)
- start()
- }
- }
+ animate().alpha(.5f).withEndAction {
+ animate().alpha(1f).start()
+ }.start()
}
}
}
@@ -149,7 +113,7 @@
}
private fun doDecode(source: ImageDecoder.Source) {
- gainmap = null
+ originalGainmap = null
bitmap = ImageDecoder.decodeBitmap(source) { decoder, info, source ->
decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
}
@@ -167,9 +131,10 @@
findViewById<TextView>(R.id.error_msg)!!.visibility = View.GONE
findViewById<RadioGroup>(R.id.output_mode)!!.visibility = View.VISIBLE
- gainmap = bitmap!!.gainmap
- gainmapMetadataEditor.setGainmap(gainmap)
- val map = gainmap!!.gainmapContents
+ val gainmap = bitmap!!.gainmap!!
+ originalGainmap = gainmap
+ gainmapMetadataEditor.setGainmap(Gainmap(gainmap, gainmap.gainmapContents))
+ val map = gainmap.gainmapContents
if (map.config != Bitmap.Config.ALPHA_8) {
gainmapVisualizer = map
} else {
@@ -198,14 +163,12 @@
imageView.setImage(ImageSource.cachedBitmap(when (outputMode) {
R.id.output_hdr -> {
- gainmapMetadataEditor.useOriginalMetadata()
- bitmap!!.gainmap = gainmap
+ bitmap!!.gainmap = originalGainmap
bitmap!!
}
R.id.output_hdr_test -> {
- gainmapMetadataEditor.useEditMetadata()
- bitmap!!.gainmap = gainmap
+ bitmap!!.gainmap = gainmapMetadataEditor.editedGainmap()
bitmap!!
}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
index 8a65304..c4bc600 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
@@ -28,23 +28,27 @@
import com.android.test.silkfx.R
data class GainmapMetadata(
- var ratioMin: Float,
- var ratioMax: Float,
- var capacityMin: Float,
- var capacityMax: Float,
- var gamma: Float,
- var offsetSdr: Float,
- var offsetHdr: Float
+ var ratioMin: Float,
+ var ratioMax: Float,
+ var capacityMin: Float,
+ var capacityMax: Float,
+ var gamma: Float,
+ var offsetSdr: Float,
+ var offsetHdr: Float
)
+/**
+ * Note: This can only handle single-channel gainmaps nicely. It will force all 3-channel
+ * metadata to have the same value single value and is not intended to be a robust demonstration
+ * of gainmap metadata editing
+ */
class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
- private var gainmap: Gainmap? = null
- private var showingEdits = false
+ private lateinit var gainmap: Gainmap
private var metadataPopup: PopupWindow? = null
private var originalMetadata: GainmapMetadata = GainmapMetadata(
- 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f)
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f)
private var currentMetadata: GainmapMetadata = originalMetadata.copy()
private val maxProgress = 100.0f
@@ -61,23 +65,18 @@
private val maxGamma = 3.0f
// Min and max offsets are 0.0 and 1.0 respectively
- fun setGainmap(newGainmap: Gainmap?) {
+ fun setGainmap(newGainmap: Gainmap) {
gainmap = newGainmap
- originalMetadata = GainmapMetadata(gainmap!!.getRatioMin()[0],
- gainmap!!.getRatioMax()[0], gainmap!!.getMinDisplayRatioForHdrTransition(),
- gainmap!!.getDisplayRatioForFullHdr(), gainmap!!.getGamma()[0],
- gainmap!!.getEpsilonSdr()[0], gainmap!!.getEpsilonHdr()[0])
+ originalMetadata = GainmapMetadata(gainmap.getRatioMin()[0],
+ gainmap.getRatioMax()[0], gainmap.getMinDisplayRatioForHdrTransition(),
+ gainmap.getDisplayRatioForFullHdr(), gainmap.getGamma()[0],
+ gainmap.getEpsilonSdr()[0], gainmap.getEpsilonHdr()[0])
currentMetadata = originalMetadata.copy()
}
- fun useOriginalMetadata() {
- showingEdits = false
- applyMetadata(originalMetadata)
- }
-
- fun useEditMetadata() {
- showingEdits = true
+ fun editedGainmap(): Gainmap {
applyMetadata(currentMetadata)
+ return gainmap
}
fun closeEditor() {
@@ -93,7 +92,7 @@
val view = LayoutInflater.from(parent.getContext()).inflate(R.layout.gainmap_metadata, null)
metadataPopup = PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT)
+ ViewGroup.LayoutParams.WRAP_CONTENT)
metadataPopup!!.showAtLocation(view, Gravity.CENTER, 0, 0)
(view.getParent() as ViewGroup).removeView(view)
@@ -117,7 +116,7 @@
val offsetSdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
val offsetHdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
arrayOf(gainmapMinSeek, gainmapMaxSeek, capacityMinSeek, capacityMaxSeek, gammaSeek,
- offsetSdrSeek, offsetHdrSeek).forEach {
+ offsetSdrSeek, offsetHdrSeek).forEach {
it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (!fromUser) return
@@ -149,37 +148,37 @@
val offsetHdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
gainmapMinSeek.setProgress(
- ((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt())
+ ((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt())
gainmapMaxSeek.setProgress(
- ((currentMetadata.ratioMax - minRatioMax) / maxRatioMax * maxProgress).toInt())
+ ((currentMetadata.ratioMax - minRatioMax) / maxRatioMax * maxProgress).toInt())
capacityMinSeek.setProgress(
- ((currentMetadata.capacityMin - minCapacityMin) / maxCapacityMin * maxProgress).toInt())
+ ((currentMetadata.capacityMin - minCapacityMin) / maxCapacityMin * maxProgress).toInt())
capacityMaxSeek.setProgress(
- ((currentMetadata.capacityMax - minCapacityMax) / maxCapacityMax * maxProgress).toInt())
+ ((currentMetadata.capacityMax - minCapacityMax) / maxCapacityMax * maxProgress).toInt())
gammaSeek.setProgress(
- ((currentMetadata.gamma - minGamma) / maxGamma * maxProgress).toInt())
+ ((currentMetadata.gamma - minGamma) / maxGamma * maxProgress).toInt())
// Log base 3 via: log_b(x) = log_y(x) / log_y(b)
offsetSdrSeek.setProgress(
- ((1.0 - Math.log(currentMetadata.offsetSdr.toDouble() / Math.log(3.0)) / -11.0)
- .toFloat() * maxProgress).toInt())
+ ((1.0 - Math.log(currentMetadata.offsetSdr.toDouble() / Math.log(3.0)) / -11.0)
+ .toFloat() * maxProgress).toInt())
offsetHdrSeek.setProgress(
- ((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0)
- .toFloat() * maxProgress).toInt())
+ ((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0)
+ .toFloat() * maxProgress).toInt())
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
- "%.3f".format(currentMetadata.ratioMin))
+ "%.3f".format(currentMetadata.ratioMin))
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
- "%.3f".format(currentMetadata.ratioMax))
+ "%.3f".format(currentMetadata.ratioMax))
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
- "%.3f".format(currentMetadata.capacityMin))
+ "%.3f".format(currentMetadata.capacityMin))
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
- "%.3f".format(currentMetadata.capacityMax))
+ "%.3f".format(currentMetadata.capacityMax))
parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
- "%.3f".format(currentMetadata.gamma))
+ "%.3f".format(currentMetadata.gamma))
parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
- "%.5f".format(currentMetadata.offsetSdr))
+ "%.5f".format(currentMetadata.offsetSdr))
parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
- "%.5f".format(currentMetadata.offsetHdr))
+ "%.5f".format(currentMetadata.offsetHdr))
}
private fun resetGainmapMetadata() {
@@ -189,69 +188,59 @@
}
private fun applyMetadata(newMetadata: GainmapMetadata) {
- gainmap!!.setRatioMin(newMetadata.ratioMin, newMetadata.ratioMin, newMetadata.ratioMin)
- gainmap!!.setRatioMax(newMetadata.ratioMax, newMetadata.ratioMax, newMetadata.ratioMax)
- gainmap!!.setMinDisplayRatioForHdrTransition(newMetadata.capacityMin)
- gainmap!!.setDisplayRatioForFullHdr(newMetadata.capacityMax)
- gainmap!!.setGamma(newMetadata.gamma, newMetadata.gamma, newMetadata.gamma)
- gainmap!!.setEpsilonSdr(newMetadata.offsetSdr, newMetadata.offsetSdr, newMetadata.offsetSdr)
- gainmap!!.setEpsilonHdr(newMetadata.offsetHdr, newMetadata.offsetHdr, newMetadata.offsetHdr)
+ gainmap.setRatioMin(newMetadata.ratioMin, newMetadata.ratioMin, newMetadata.ratioMin)
+ gainmap.setRatioMax(newMetadata.ratioMax, newMetadata.ratioMax, newMetadata.ratioMax)
+ gainmap.setMinDisplayRatioForHdrTransition(newMetadata.capacityMin)
+ gainmap.setDisplayRatioForFullHdr(newMetadata.capacityMax)
+ gainmap.setGamma(newMetadata.gamma, newMetadata.gamma, newMetadata.gamma)
+ gainmap.setEpsilonSdr(newMetadata.offsetSdr, newMetadata.offsetSdr, newMetadata.offsetSdr)
+ gainmap.setEpsilonHdr(newMetadata.offsetHdr, newMetadata.offsetHdr, newMetadata.offsetHdr)
renderView.invalidate()
}
private fun updateGainmapMin(normalized: Float) {
val newValue = minRatioMin + normalized * (maxRatioMin - minRatioMin)
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.ratioMin = newValue
- if (showingEdits) {
- gainmap!!.setRatioMin(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setRatioMin(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateGainmapMax(normalized: Float) {
val newValue = minRatioMax + normalized * (maxRatioMax - minRatioMax)
parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.ratioMax = newValue
- if (showingEdits) {
- gainmap!!.setRatioMax(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setRatioMax(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateCapacityMin(normalized: Float) {
val newValue = minCapacityMin + normalized * (maxCapacityMin - minCapacityMin)
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.capacityMin = newValue
- if (showingEdits) {
- gainmap!!.setMinDisplayRatioForHdrTransition(newValue)
- renderView.invalidate()
- }
+ gainmap.setMinDisplayRatioForHdrTransition(newValue)
+ renderView.invalidate()
}
private fun updateCapacityMax(normalized: Float) {
val newValue = minCapacityMax + normalized * (maxCapacityMax - minCapacityMax)
parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.capacityMax = newValue
- if (showingEdits) {
- gainmap!!.setDisplayRatioForFullHdr(newValue)
- renderView.invalidate()
- }
+ gainmap.setDisplayRatioForFullHdr(newValue)
+ renderView.invalidate()
}
private fun updateGamma(normalized: Float) {
val newValue = minGamma + normalized * (maxGamma - minGamma)
parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.gamma = newValue
- if (showingEdits) {
- gainmap!!.setGamma(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setGamma(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateOffsetSdr(normalized: Float) {
@@ -260,12 +249,10 @@
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
- "%.5f".format(newValue))
+ "%.5f".format(newValue))
currentMetadata.offsetSdr = newValue
- if (showingEdits) {
- gainmap!!.setEpsilonSdr(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setEpsilonSdr(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateOffsetHdr(normalized: Float) {
@@ -274,11 +261,9 @@
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
- "%.5f".format(newValue))
+ "%.5f".format(newValue))
currentMetadata.offsetHdr = newValue
- if (showingEdits) {
- gainmap!!.setEpsilonHdr(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setEpsilonHdr(newValue, newValue, newValue)
+ renderView.invalidate()
}
}
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
index 3567c08..cc6cebf 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -469,7 +469,7 @@
}
}
- // Create a few dummy models if we didn't load anything.
+ // Create a few placeholder models if we didn't load anything.
if (!loadedModel) {
Properties dummyModelProperties = new Properties();
for (String name : new String[]{"1", "2", "3"}) {
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
index ba12acb..2b605c5 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
@@ -18,6 +18,7 @@
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -41,11 +42,11 @@
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
+import androidx.appcompat.app.AppCompatActivity;
+
import java.util.ArrayList;
import java.util.List;
-import androidx.appcompat.app.AppCompatActivity;
-
public class ChatActivity extends AppCompatActivity {
private View mRoot;
@@ -148,7 +149,7 @@
inset = min(inset, shown);
mAnimationController.setInsetsAndAlpha(
Insets.of(0, 0, 0, inset),
- 1f, (inset - start) / (float)(end - start));
+ 1f, start == end ? 1f : (inset - start) / (float) (end - start));
}
});
diff --git a/tests/libs-permissions/system_ext/java/com/android/test/libs/system_ext/LibsSystemExtTest.java b/tests/libs-permissions/system_ext/java/com/android/test/libs/system_ext/LibsSystemExtTest.java
index 9999aba..673c73a 100644
--- a/tests/libs-permissions/system_ext/java/com/android/test/libs/system_ext/LibsSystemExtTest.java
+++ b/tests/libs-permissions/system_ext/java/com/android/test/libs/system_ext/LibsSystemExtTest.java
@@ -22,7 +22,7 @@
public class LibsSystemExtTest {
/**
- * Dummy method for testing.
+ * Placeholder method for testing.
*/
public static void test() {
}
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 899d268..b94d14f 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -219,7 +219,7 @@
if (numTags >= 1) {
const String8& lang = parts[0];
if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
- setLanguage(lang.string());
+ setLanguage(lang.c_str());
valid = true;
}
}
@@ -232,11 +232,11 @@
const String8& part2 = parts[1];
if ((part2.length() == 2 && isAlpha(part2)) ||
(part2.length() == 3 && isNumber(part2))) {
- setRegion(part2.string());
+ setRegion(part2.c_str());
} else if (part2.length() == 4 && isAlpha(part2)) {
- setScript(part2.string());
+ setScript(part2.c_str());
} else if (part2.length() >= 4 && part2.length() <= 8) {
- setVariant(part2.string());
+ setVariant(part2.c_str());
} else {
valid = false;
}
@@ -249,9 +249,9 @@
const String8& part3 = parts[2];
if (((part3.length() == 2 && isAlpha(part3)) ||
(part3.length() == 3 && isNumber(part3))) && script[0]) {
- setRegion(part3.string());
+ setRegion(part3.c_str());
} else if (part3.length() >= 4 && part3.length() <= 8) {
- setVariant(part3.string());
+ setVariant(part3.c_str());
} else {
valid = false;
}
@@ -262,7 +262,7 @@
const String8& part4 = parts[3];
if (part4.length() >= 4 && part4.length() <= 8) {
- setVariant(part4.string());
+ setVariant(part4.c_str());
} else {
valid = false;
}
@@ -310,7 +310,7 @@
break;
default:
fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n",
- part.string());
+ part.c_str());
return -1;
}
} else if (subtags.size() == 3) {
@@ -324,7 +324,7 @@
} else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
setRegion(subtags[1]);
} else {
- fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n", part.string());
+ fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name %s\n", part.c_str());
return -1;
}
@@ -341,14 +341,14 @@
setRegion(subtags[2]);
setVariant(subtags[3]);
} else {
- fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name: %s\n", part.string());
+ fprintf(stderr, "ERROR: Invalid BCP 47 tag in directory name: %s\n", part.c_str());
return -1;
}
return ++currentIndex;
} else {
if ((part.length() == 2 || part.length() == 3)
- && isAlpha(part) && strcmp("car", part.string())) {
+ && isAlpha(part) && strcmp("car", part.c_str())) {
setLanguage(part);
if (++currentIndex == size) {
return size;
@@ -358,8 +358,8 @@
}
part = parts[currentIndex];
- if (part.string()[0] == 'r' && part.length() == 3) {
- setRegion(part.string() + 1);
+ if (part.c_str()[0] == 'r' && part.length() == 3) {
+ setRegion(part.c_str() + 1);
if (++currentIndex == size) {
return size;
}
@@ -524,8 +524,8 @@
ssize_t index = mFiles.indexOfKey(file->getGroupEntry());
if (index >= 0 && overwriteDuplicate) {
fprintf(stderr, "warning: overwriting '%s' with '%s'\n",
- mFiles[index]->getSourceFile().string(),
- file->getSourceFile().string());
+ mFiles[index]->getSourceFile().c_str(),
+ file->getSourceFile().c_str());
removeFile(index);
index = -1;
}
@@ -545,7 +545,7 @@
const sp<AaptFile>& originalFile = mFiles.valueAt(index);
SourcePos(file->getSourceFile(), -1)
.error("Duplicate file.\n%s: Original is here. %s",
- originalFile->getPrintableSource().string(),
+ originalFile->getPrintableSource().c_str(),
(withoutVersion.version != 0) ? "The version qualifier may be implied." : "");
return UNKNOWN_ERROR;
}
@@ -557,21 +557,21 @@
void AaptGroup::print(const String8& prefix) const
{
- printf("%s%s\n", prefix.string(), getPath().string());
+ printf("%s%s\n", prefix.c_str(), getPath().c_str());
const size_t N=mFiles.size();
size_t i;
for (i=0; i<N; i++) {
sp<AaptFile> file = mFiles.valueAt(i);
const AaptGroupEntry& e = file->getGroupEntry();
if (file->hasData()) {
- printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
+ printf("%s Gen: (%s) %d bytes\n", prefix.c_str(), e.toDirName(String8()).c_str(),
(int)file->getSize());
} else {
- printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
- file->getPrintableSource().string());
+ printf("%s Src: (%s) %s\n", prefix.c_str(), e.toDirName(String8()).c_str(),
+ file->getPrintableSource().c_str());
}
- //printf("%s File Group Entry: %s\n", prefix.string(),
- // file->getGroupEntry().toDirName(String8()).string());
+ //printf("%s File Group Entry: %s\n", prefix.c_str(),
+ // file->getGroupEntry().toDirName(String8()).c_str());
}
}
@@ -660,9 +660,9 @@
{
DIR* dir = NULL;
- dir = opendir(srcDir.string());
+ dir = opendir(srcDir.c_str());
if (dir == NULL) {
- fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
+ fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.c_str(), strerror(errno));
return UNKNOWN_ERROR;
}
@@ -676,7 +676,7 @@
if (entry == NULL)
break;
- if (isHidden(srcDir.string(), entry->d_name))
+ if (isHidden(srcDir.c_str(), entry->d_name))
continue;
String8 name(entry->d_name);
@@ -701,8 +701,8 @@
String8 pathName(srcDir);
FileType type;
- pathName.appendPath(fileNames[i].string());
- type = getFileType(pathName.string());
+ pathName.appendPath(fileNames[i].c_str());
+ type = getFileType(pathName.c_str());
if (type == kFileTypeDirectory) {
sp<AaptDir> subdir;
bool notAdded = false;
@@ -732,7 +732,7 @@
} else {
if (bundle->getVerbose())
- printf(" (ignoring non-file/dir '%s')\n", pathName.string());
+ printf(" (ignoring non-file/dir '%s')\n", pathName.c_str());
}
}
@@ -745,7 +745,7 @@
const size_t ND = mDirs.size();
size_t i;
for (i = 0; i < NF; i++) {
- if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
+ if (!validateFileName(mFiles.valueAt(i)->getLeaf().c_str())) {
SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
"Invalid filename. Unable to add.");
return UNKNOWN_ERROR;
@@ -753,11 +753,11 @@
size_t j;
for (j = i+1; j < NF; j++) {
- if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
- mFiles.valueAt(j)->getLeaf().string()) == 0) {
+ if (strcasecmp(mFiles.valueAt(i)->getLeaf().c_str(),
+ mFiles.valueAt(j)->getLeaf().c_str()) == 0) {
SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
"File is case-insensitive equivalent to: %s",
- mFiles.valueAt(j)->getPrintableSource().string());
+ mFiles.valueAt(j)->getPrintableSource().c_str());
return UNKNOWN_ERROR;
}
@@ -766,18 +766,18 @@
}
for (j = 0; j < ND; j++) {
- if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
- mDirs.valueAt(j)->getLeaf().string()) == 0) {
+ if (strcasecmp(mFiles.valueAt(i)->getLeaf().c_str(),
+ mDirs.valueAt(j)->getLeaf().c_str()) == 0) {
SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
"File conflicts with dir from: %s",
- mDirs.valueAt(j)->getPrintableSource().string());
+ mDirs.valueAt(j)->getPrintableSource().c_str());
return UNKNOWN_ERROR;
}
}
}
for (i = 0; i < ND; i++) {
- if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
+ if (!validateFileName(mDirs.valueAt(i)->getLeaf().c_str())) {
SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
"Invalid directory name, unable to add.");
return UNKNOWN_ERROR;
@@ -785,11 +785,11 @@
size_t j;
for (j = i+1; j < ND; j++) {
- if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
- mDirs.valueAt(j)->getLeaf().string()) == 0) {
+ if (strcasecmp(mDirs.valueAt(i)->getLeaf().c_str(),
+ mDirs.valueAt(j)->getLeaf().c_str()) == 0) {
SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
"Directory is case-insensitive equivalent to: %s",
- mDirs.valueAt(j)->getPrintableSource().string());
+ mDirs.valueAt(j)->getPrintableSource().c_str());
return UNKNOWN_ERROR;
}
}
@@ -846,12 +846,12 @@
const AaptSymbolEntry& entry = javaSymbols->mSymbols.valueAt(i);
ssize_t pos = mSymbols.indexOfKey(name);
if (pos < 0) {
- entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.string());
+ entry.sourcePos.error("Symbol '%s' declared with <java-symbol> not defined\n", name.c_str());
err = UNKNOWN_ERROR;
continue;
}
//printf("**** setting symbol #%d/%d %s to isJavaSymbol=%d\n",
- // i, N, name.string(), entry.isJavaSymbol ? 1 : 0);
+ // i, N, name.c_str(), entry.isJavaSymbol ? 1 : 0);
mSymbols.editValueAt(pos).isJavaSymbol = entry.isJavaSymbol;
}
@@ -862,11 +862,11 @@
ssize_t pos = mNestedSymbols.indexOfKey(name);
if (pos < 0) {
SourcePos pos;
- pos.error("Java symbol dir %s not defined\n", name.string());
+ pos.error("Java symbol dir %s not defined\n", name.c_str());
err = UNKNOWN_ERROR;
continue;
}
- //printf("**** applying java symbols in dir %s\n", name.string());
+ //printf("**** applying java symbols in dir %s\n", name.c_str());
status_t myerr = mNestedSymbols.valueAt(pos)->applyJavaSymbols(symbols);
if (myerr != NO_ERROR) {
err = myerr;
@@ -1131,9 +1131,9 @@
{
ssize_t err = 0;
- DIR* dir = opendir(srcDir.string());
+ DIR* dir = opendir(srcDir.c_str());
if (dir == NULL) {
- fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
+ fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.c_str(), strerror(errno));
return UNKNOWN_ERROR;
}
@@ -1149,7 +1149,7 @@
break;
}
- if (isHidden(srcDir.string(), entry->d_name)) {
+ if (isHidden(srcDir.c_str(), entry->d_name)) {
continue;
}
@@ -1160,7 +1160,7 @@
String8 resType;
bool b = group.initFromDirName(entry->d_name, &resType);
if (!b) {
- fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(),
+ fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.c_str(),
entry->d_name);
err = -1;
continue;
@@ -1168,7 +1168,7 @@
if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
int maxResInt = atoi(bundle->getMaxResVersion());
- const char *verString = group.getVersionString().string();
+ const char *verString = group.getVersionString().c_str();
int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
if (dirVersionInt > maxResInt) {
fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
@@ -1176,7 +1176,7 @@
}
}
- FileType type = getFileType(subdirName.string());
+ FileType type = getFileType(subdirName.c_str());
if (type == kFileTypeDirectory) {
sp<AaptDir> dir = makeDir(resType);
@@ -1200,7 +1200,7 @@
}
} else {
if (bundle->getVerbose()) {
- fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
+ fprintf(stderr, " (ignoring file '%s')\n", subdirName.c_str());
}
}
}
@@ -1248,7 +1248,7 @@
String8 remain;
if (entryName.walkPath(&remain) == kResourceDir) {
// these are the resources, pull their type out of the directory name
- kind.initFromDirName(remain.walkPath().string(), &resType);
+ kind.initFromDirName(remain.walkPath().c_str(), &resType);
} else {
// these are untyped and don't have an AaptGroupEntry
}
@@ -1263,7 +1263,7 @@
sp<AaptFile> file = new AaptFile(entryName, kind, resType);
status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
if (err != NO_ERROR) {
- fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
+ fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.c_str());
count = err;
goto bail;
}
@@ -1273,7 +1273,7 @@
if (entryName == "AndroidManifest.xml") {
printf("AndroidManifest.xml\n");
}
- printf("\n\nfile: %s\n", entryName.string());
+ printf("\n\nfile: %s\n", entryName.c_str());
#endif
size_t len = entry->getUncompressedLen();
@@ -1317,9 +1317,9 @@
uint32_t preferredDensity = 0;
if (bundle->getPreferredDensity().size() > 0) {
ResTable_config preferredConfig;
- if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) {
+ if (!AaptConfig::parseDensity(bundle->getPreferredDensity().c_str(), &preferredConfig)) {
fprintf(stderr, "Error parsing preferred density: %s\n",
- bundle->getPreferredDensity().string());
+ bundle->getPreferredDensity().c_str());
return UNKNOWN_ERROR;
}
preferredDensity = preferredConfig.density;
@@ -1332,11 +1332,11 @@
if (bundle->getVerbose()) {
if (!reqFilter->isEmpty()) {
printf("Applying required filter: %s\n",
- bundle->getConfigurations().string());
+ bundle->getConfigurations().c_str());
}
if (preferredDensity > 0) {
printf("Applying preferred density filter: %s\n",
- bundle->getPreferredDensity().string());
+ bundle->getPreferredDensity().c_str());
}
}
@@ -1385,7 +1385,7 @@
if (!reqFilter->match(config)) {
if (bundle->getVerbose()) {
printf("Pruning unneeded resource: %s\n",
- file->getPrintableSource().string());
+ file->getPrintableSource().c_str());
}
grp->removeFile(k);
k--;
@@ -1453,7 +1453,7 @@
if (bestDensity != config.density) {
if (bundle->getVerbose()) {
printf("Pruning unneeded resource: %s\n",
- file->getPrintableSource().string());
+ file->getPrintableSource().c_str());
}
grp->removeFile(k);
k--;
@@ -1495,10 +1495,10 @@
ssize_t pos = mSymbols.indexOfKey(name);
if (pos < 0) {
SourcePos pos;
- pos.error("Java symbol dir %s not defined\n", name.string());
+ pos.error("Java symbol dir %s not defined\n", name.c_str());
return UNKNOWN_ERROR;
}
- //printf("**** applying java symbols in dir %s\n", name.string());
+ //printf("**** applying java symbols in dir %s\n", name.c_str());
status_t err = mSymbols.valueAt(pos)->applyJavaSymbols(symbols);
if (err != NO_ERROR) {
return err;
@@ -1510,7 +1510,7 @@
bool AaptAssets::isJavaSymbol(const AaptSymbolEntry& sym, bool includePrivate) const {
//printf("isJavaSymbol %s: public=%d, includePrivate=%d, isJavaSymbol=%d\n",
- // sym.name.string(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
+ // sym.name.c_str(), sym.isPublic ? 1 : 0, includePrivate ? 1 : 0,
// sym.isJavaSymbol ? 1 : 0);
if (!mHavePrivateSymbols) return true;
if (sym.isPublic) return true;
@@ -1529,12 +1529,12 @@
const size_t packageIncludeCount = includes.size();
for (size_t i = 0; i < packageIncludeCount; i++) {
if (bundle->getVerbose()) {
- printf("Including resources from package: %s\n", includes[i].string());
+ printf("Including resources from package: %s\n", includes[i].c_str());
}
if (!mIncludedAssets.addAssetPath(includes[i], NULL)) {
fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
- includes[i].string());
+ includes[i].c_str());
return UNKNOWN_ERROR;
}
}
@@ -1543,12 +1543,12 @@
if (!featureOfBase.isEmpty()) {
if (bundle->getVerbose()) {
printf("Including base feature resources from package: %s\n",
- featureOfBase.string());
+ featureOfBase.c_str());
}
if (!mIncludedAssets.addAssetPath(featureOfBase, NULL)) {
fprintf(stderr, "ERROR: base feature package '%s' not found.\n",
- featureOfBase.string());
+ featureOfBase.c_str());
return UNKNOWN_ERROR;
}
}
@@ -1581,23 +1581,23 @@
innerPrefix.append(" ");
String8 innerInnerPrefix(innerPrefix);
innerInnerPrefix.append(" ");
- printf("%sConfigurations:\n", prefix.string());
+ printf("%sConfigurations:\n", prefix.c_str());
const size_t N=mGroupEntries.size();
for (size_t i=0; i<N; i++) {
String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
- printf("%s %s\n", prefix.string(),
- cname != "" ? cname.string() : "(default)");
+ printf("%s %s\n", prefix.c_str(),
+ cname != "" ? cname.c_str() : "(default)");
}
- printf("\n%sFiles:\n", prefix.string());
+ printf("\n%sFiles:\n", prefix.c_str());
AaptDir::print(innerPrefix);
- printf("\n%sResource Dirs:\n", prefix.string());
+ printf("\n%sResource Dirs:\n", prefix.c_str());
const Vector<sp<AaptDir> >& resdirs = mResDirs;
const size_t NR = resdirs.size();
for (size_t i=0; i<NR; i++) {
const sp<AaptDir>& d = resdirs.itemAt(i);
- printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
+ printf("%s Type %s\n", prefix.c_str(), d->getLeaf().c_str());
d->print(innerInnerPrefix);
}
}
@@ -1631,7 +1631,7 @@
NULL
};
const char*const* k = KEYWORDS;
- const char*const s = symbol.string();
+ const char*const s = symbol.c_str();
while (*k) {
if (0 == strcmp(s, *k)) {
return false;
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index eadd48a..498fc4e 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -463,7 +463,7 @@
if (valid_symbol_name(symbol)) {
return true;
}
- pos.error("invalid %s: '%s'\n", label, symbol.string());
+ pos.error("invalid %s: '%s'\n", label, symbol.c_str());
return false;
}
AaptSymbolEntry& edit_symbol(const String8& symbol, const SourcePos* pos) {
diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp
index 0aca45e..7578f79 100644
--- a/tools/aapt/AaptConfig.cpp
+++ b/tools/aapt/AaptConfig.cpp
@@ -39,7 +39,7 @@
ssize_t index = 0;
ssize_t localeIndex = 0;
const ssize_t N = parts.size();
- const char* part = parts[index].string();
+ const char* part = parts[index].c_str();
if (str.length() == 0) {
goto success;
@@ -50,7 +50,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseMnc(part, &config)) {
@@ -58,7 +58,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
// Locale spans a few '-' separators, so we let it
@@ -72,7 +72,7 @@
if (index >= N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseLayoutDirection(part, &config)) {
@@ -80,7 +80,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseSmallestScreenWidthDp(part, &config)) {
@@ -88,7 +88,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseScreenWidthDp(part, &config)) {
@@ -96,7 +96,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseScreenHeightDp(part, &config)) {
@@ -104,7 +104,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseScreenLayoutSize(part, &config)) {
@@ -112,7 +112,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseScreenLayoutLong(part, &config)) {
@@ -120,7 +120,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseScreenRound(part, &config)) {
@@ -128,7 +128,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseWideColorGamut(part, &config)) {
@@ -136,7 +136,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseHdr(part, &config)) {
@@ -144,7 +144,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseOrientation(part, &config)) {
@@ -152,7 +152,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseUiModeType(part, &config)) {
@@ -160,7 +160,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseUiModeNight(part, &config)) {
@@ -168,7 +168,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseDensity(part, &config)) {
@@ -176,7 +176,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseTouchscreen(part, &config)) {
@@ -184,7 +184,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseKeysHidden(part, &config)) {
@@ -192,7 +192,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseKeyboard(part, &config)) {
@@ -200,7 +200,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseNavHidden(part, &config)) {
@@ -208,7 +208,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseNavigation(part, &config)) {
@@ -216,7 +216,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseScreenSize(part, &config)) {
@@ -224,7 +224,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
if (parseVersion(part, &config)) {
@@ -232,7 +232,7 @@
if (index == N) {
goto success;
}
- part = parts[index].string();
+ part = parts[index].c_str();
}
// Unrecognized.
@@ -773,8 +773,8 @@
if (y == name || *y != 0) return false;
String8 yName(x, y-x);
- uint16_t w = (uint16_t)atoi(xName.string());
- uint16_t h = (uint16_t)atoi(yName.string());
+ uint16_t w = (uint16_t)atoi(xName.c_str());
+ uint16_t h = (uint16_t)atoi(yName.c_str());
if (w < h) {
return false;
}
@@ -805,7 +805,7 @@
String8 xName(name, x-name);
if (out) {
- out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
+ out->smallestScreenWidthDp = (uint16_t)atoi(xName.c_str());
}
return true;
@@ -827,7 +827,7 @@
String8 xName(name, x-name);
if (out) {
- out->screenWidthDp = (uint16_t)atoi(xName.string());
+ out->screenWidthDp = (uint16_t)atoi(xName.c_str());
}
return true;
@@ -849,7 +849,7 @@
String8 xName(name, x-name);
if (out) {
- out->screenHeightDp = (uint16_t)atoi(xName.string());
+ out->screenHeightDp = (uint16_t)atoi(xName.c_str());
}
return true;
@@ -875,7 +875,7 @@
String8 sdkName(name, s-name);
if (out) {
- out->sdkVersion = (uint16_t)atoi(sdkName.string());
+ out->sdkVersion = (uint16_t)atoi(sdkName.c_str());
out->minorVersion = 0;
}
diff --git a/tools/aapt/AaptUtil.cpp b/tools/aapt/AaptUtil.cpp
index 293e144..e82860d 100644
--- a/tools/aapt/AaptUtil.cpp
+++ b/tools/aapt/AaptUtil.cpp
@@ -23,7 +23,7 @@
Vector<String8> split(const String8& str, const char sep) {
Vector<String8> parts;
- const char* p = str.string();
+ const char* p = str.c_str();
const char* q;
while (true) {
@@ -41,7 +41,7 @@
Vector<String8> splitAndLowerCase(const String8& str, const char sep) {
Vector<String8> parts;
- const char* p = str.string();
+ const char* p = str.c_str();
const char* q;
while (true) {
diff --git a/tools/aapt/ApkBuilder.cpp b/tools/aapt/ApkBuilder.cpp
index 01e02e2..335c43b 100644
--- a/tools/aapt/ApkBuilder.cpp
+++ b/tools/aapt/ApkBuilder.cpp
@@ -36,7 +36,7 @@
if (splitConfigs.count(*iter) > 0) {
// Can't have overlapping configurations.
fprintf(stderr, "ERROR: Split configuration '%s' is already defined "
- "in another split.\n", iter->toString().string());
+ "in another split.\n", iter->toString().c_str());
return ALREADY_EXISTS;
}
}
@@ -115,10 +115,10 @@
}
void ApkSplit::print() const {
- fprintf(stderr, "APK Split '%s'\n", mName.string());
+ fprintf(stderr, "APK Split '%s'\n", mName.c_str());
std::set<OutputEntry>::const_iterator iter = mFiles.begin();
for (; iter != mFiles.end(); iter++) {
- fprintf(stderr, " %s (%s)\n", iter->getPath().string(), iter->getFile()->getSourceFile().string());
+ fprintf(stderr, " %s (%s)\n", iter->getPath().c_str(), iter->getFile()->getSourceFile().c_str());
}
}
diff --git a/tools/aapt/CacheUpdater.h b/tools/aapt/CacheUpdater.h
index 6fa96d6..2dc143c 100644
--- a/tools/aapt/CacheUpdater.h
+++ b/tools/aapt/CacheUpdater.h
@@ -66,7 +66,7 @@
// Check optomistically to see if all directories exist.
// If something in the path doesn't exist, then walk the path backwards
// and find the place to start creating directories forward.
- if (stat(path.string(),&s) == -1) {
+ if (stat(path.c_str(),&s) == -1) {
// Walk backwards to find place to start creating directories
existsPath = path;
do {
@@ -74,7 +74,7 @@
// the string of paths to create.
toCreate = existsPath.getPathLeaf().appendPath(toCreate);
existsPath = existsPath.getPathDir();
- } while (stat(existsPath.string(),&s) == -1);
+ } while (stat(existsPath.c_str(),&s) == -1);
// Walk forwards and build directories as we go
do {
@@ -82,9 +82,9 @@
existsPath.appendPath(toCreate.walkPath(&remains));
toCreate = remains;
#ifdef _WIN32
- _mkdir(existsPath.string());
+ _mkdir(existsPath.c_str());
#else
- mkdir(existsPath.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
+ mkdir(existsPath.c_str(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
#endif
} while (remains.length() > 0);
} //if
@@ -93,8 +93,8 @@
// Delete a file
virtual void deleteFile(String8 path)
{
- if (remove(path.string()) != 0)
- fprintf(stderr,"ERROR DELETING %s\n",path.string());
+ if (remove(path.c_str()) != 0)
+ fprintf(stderr,"ERROR DELETING %s\n",path.c_str());
};
// Process an image from source out to dest
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 51cf38b..5a06b10 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -240,13 +240,13 @@
}
if (value.dataType == Res_value::TYPE_STRING) {
String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError);
- printf("%s='%s'", attrLabel.string(),
- ResTable::normalizeForOutput(result.string()).string());
+ printf("%s='%s'", attrLabel.c_str(),
+ ResTable::normalizeForOutput(result.c_str()).c_str());
} else if (Res_value::TYPE_FIRST_INT <= value.dataType &&
value.dataType <= Res_value::TYPE_LAST_INT) {
- printf("%s='%d'", attrLabel.string(), value.data);
+ printf("%s='%d'", attrLabel.c_str(), value.data);
} else {
- printf("%s='0x%x'", attrLabel.string(), (int)value.data);
+ printf("%s='0x%x'", attrLabel.c_str(), (int)value.data);
}
}
@@ -355,21 +355,21 @@
static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1,
const String8& requiredFeature = String8(),
const String8& requiredNotFeature = String8()) {
- printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
+ printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.c_str()).c_str());
if (maxSdkVersion != -1) {
printf(" maxSdkVersion='%d'", maxSdkVersion);
}
if (requiredFeature.length() > 0) {
- printf(" requiredFeature='%s'", requiredFeature.string());
+ printf(" requiredFeature='%s'", requiredFeature.c_str());
}
if (requiredNotFeature.length() > 0) {
- printf(" requiredNotFeature='%s'", requiredNotFeature.string());
+ printf(" requiredNotFeature='%s'", requiredNotFeature.c_str());
}
printf("\n");
if (optional) {
printf("optional-permission: name='%s'",
- ResTable::normalizeForOutput(name.string()).string());
+ ResTable::normalizeForOutput(name.c_str()).c_str());
if (maxSdkVersion != -1) {
printf(" maxSdkVersion='%d'", maxSdkVersion);
}
@@ -380,7 +380,7 @@
static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
printf("uses-permission-sdk-23: ");
- printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
+ printf("name='%s'", ResTable::normalizeForOutput(name.c_str()).c_str());
if (maxSdkVersion != -1) {
printf(" maxSdkVersion='%d'", maxSdkVersion);
}
@@ -390,11 +390,11 @@
static void printUsesImpliedPermission(const String8& name, const String8& reason,
const int32_t maxSdkVersion = -1) {
printf("uses-implied-permission: name='%s'",
- ResTable::normalizeForOutput(name.string()).string());
+ ResTable::normalizeForOutput(name.c_str()).c_str());
if (maxSdkVersion != -1) {
printf(" maxSdkVersion='%d'", maxSdkVersion);
}
- printf(" reason='%s'\n", ResTable::normalizeForOutput(reason.string()).string());
+ printf(" reason='%s'\n", ResTable::normalizeForOutput(reason.c_str()).c_str());
}
Vector<String8> getNfcAidCategories(AssetManager& assets, const String8& xmlPath, bool offHost,
@@ -556,7 +556,7 @@
static void printFeatureGroupImpl(const FeatureGroup& grp,
const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
- printf("feature-group: label='%s'\n", grp.label.string());
+ printf("feature-group: label='%s'\n", grp.label.c_str());
if (grp.openGLESVersion > 0) {
printf(" uses-gl-es: '0x%x'\n", grp.openGLESVersion);
@@ -570,7 +570,7 @@
const String8& featureName = grp.features.keyAt(i);
printf(" uses-feature%s: name='%s'", (required ? "" : "-not-required"),
- ResTable::normalizeForOutput(featureName.string()).string());
+ ResTable::normalizeForOutput(featureName.c_str()).c_str());
if (version > 0) {
printf(" version='%d'", version);
@@ -589,15 +589,15 @@
}
String8 printableFeatureName(ResTable::normalizeForOutput(
- impliedFeature.name.string()));
+ impliedFeature.name.c_str()));
const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
- printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
+ printf(" uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.c_str());
printf(" uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
- printableFeatureName.string());
+ printableFeatureName.c_str());
const size_t numReasons = impliedFeature.reasons.size();
for (size_t j = 0; j < numReasons; j++) {
- printf("%s", impliedFeature.reasons[j].string());
+ printf("%s", impliedFeature.reasons[j].c_str());
if (j + 2 < numReasons) {
printf(", ");
} else if (j + 1 < numReasons) {
@@ -649,43 +649,43 @@
bool impliedBySdk23Permission) {
if (name == "android.permission.CAMERA") {
addImpliedFeature(impliedFeatures, "android.hardware.camera",
- String8::format("requested %s permission", name.string()),
+ String8::format("requested %s permission", name.c_str()),
impliedBySdk23Permission);
} else if (name == "android.permission.ACCESS_FINE_LOCATION") {
if (targetSdk < SDK_LOLLIPOP) {
addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
- String8::format("requested %s permission", name.string()),
+ String8::format("requested %s permission", name.c_str()),
impliedBySdk23Permission);
addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
impliedBySdk23Permission);
}
addImpliedFeature(impliedFeatures, "android.hardware.location",
- String8::format("requested %s permission", name.string()),
+ String8::format("requested %s permission", name.c_str()),
impliedBySdk23Permission);
} else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
if (targetSdk < SDK_LOLLIPOP) {
addImpliedFeature(impliedFeatures, "android.hardware.location.network",
- String8::format("requested %s permission", name.string()),
+ String8::format("requested %s permission", name.c_str()),
impliedBySdk23Permission);
addImpliedFeature(impliedFeatures, "android.hardware.location.network",
String8::format("targetSdkVersion < %d", SDK_LOLLIPOP),
impliedBySdk23Permission);
}
addImpliedFeature(impliedFeatures, "android.hardware.location",
- String8::format("requested %s permission", name.string()),
+ String8::format("requested %s permission", name.c_str()),
impliedBySdk23Permission);
} else if (name == "android.permission.ACCESS_MOCK_LOCATION" ||
name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
name == "android.permission.INSTALL_LOCATION_PROVIDER") {
addImpliedFeature(impliedFeatures, "android.hardware.location",
- String8::format("requested %s permission", name.string()),
+ String8::format("requested %s permission", name.c_str()),
impliedBySdk23Permission);
} else if (name == "android.permission.BLUETOOTH" ||
name == "android.permission.BLUETOOTH_ADMIN") {
if (targetSdk > SDK_DONUT) {
addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
- String8::format("requested %s permission", name.string()),
+ String8::format("requested %s permission", name.c_str()),
impliedBySdk23Permission);
addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
String8::format("targetSdkVersion > %d", SDK_DONUT),
@@ -693,13 +693,13 @@
}
} else if (name == "android.permission.RECORD_AUDIO") {
addImpliedFeature(impliedFeatures, "android.hardware.microphone",
- String8::format("requested %s permission", name.string()),
+ String8::format("requested %s permission", name.c_str()),
impliedBySdk23Permission);
} else if (name == "android.permission.ACCESS_WIFI_STATE" ||
name == "android.permission.CHANGE_WIFI_STATE" ||
name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
addImpliedFeature(impliedFeatures, "android.hardware.wifi",
- String8::format("requested %s permission", name.string()),
+ String8::format("requested %s permission", name.c_str()),
impliedBySdk23Permission);
} else if (name == "android.permission.CALL_PHONE" ||
name == "android.permission.CALL_PRIVILEGED" ||
@@ -746,7 +746,7 @@
for (size_t i = 0; i < bundle->getPackageIncludes().size(); i++) {
const String8& assetPath = bundle->getPackageIncludes()[i];
if (!assets.addAssetPath(assetPath, NULL)) {
- fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.string());
+ fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.c_str());
return 1;
}
}
@@ -890,7 +890,7 @@
goto bail;
}
String8 tag(ctag16);
- //printf("Depth %d tag %s\n", depth, tag.string());
+ //printf("Depth %d tag %s\n", depth, tag.c_str());
if (depth == 1) {
if (tag != "manifest") {
SourcePos(manifestFile, tree.getLineNumber()).error(
@@ -898,14 +898,14 @@
goto bail;
}
String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
- printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
+ printf("package: %s\n", ResTable::normalizeForOutput(pkg.c_str()).c_str());
} else if (depth == 2) {
if (tag == "permission") {
String8 error;
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:name': %s", error.string());
+ "ERROR getting 'android:name': %s", error.c_str());
goto bail;
}
@@ -915,13 +915,13 @@
goto bail;
}
printf("permission: %s\n",
- ResTable::normalizeForOutput(name.string()).string());
+ ResTable::normalizeForOutput(name.c_str()).c_str());
} else if (tag == "uses-permission") {
String8 error;
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:name' attribute: %s", error.string());
+ "ERROR getting 'android:name' attribute: %s", error.c_str());
goto bail;
}
@@ -938,7 +938,7 @@
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:name' attribute: %s", error.string());
+ "ERROR getting 'android:name' attribute: %s", error.c_str());
goto bail;
}
@@ -1138,7 +1138,7 @@
const size_t N = supportedInput.size();
for (size_t i=0; i<N; i++) {
printf("%s", ResTable::normalizeForOutput(
- supportedInput[i].string()).string());
+ supportedInput[i].c_str()).c_str());
if (i != N - 1) {
printf("' '");
} else {
@@ -1157,27 +1157,27 @@
printf("launchable-activity:");
if (aName.length() > 0) {
printf(" name='%s' ",
- ResTable::normalizeForOutput(aName.string()).string());
+ ResTable::normalizeForOutput(aName.c_str()).c_str());
}
printf(" label='%s' icon='%s'\n",
- ResTable::normalizeForOutput(activityLabel.string())
- .string(),
- ResTable::normalizeForOutput(activityIcon.string())
- .string());
+ ResTable::normalizeForOutput(activityLabel.c_str())
+ .c_str(),
+ ResTable::normalizeForOutput(activityIcon.c_str())
+ .c_str());
}
if (isLeanbackLauncherActivity) {
printf("leanback-launchable-activity:");
if (aName.length() > 0) {
printf(" name='%s' ",
- ResTable::normalizeForOutput(aName.string()).string());
+ ResTable::normalizeForOutput(aName.c_str()).c_str());
}
printf(" label='%s' icon='%s' banner='%s'\n",
- ResTable::normalizeForOutput(activityLabel.string())
- .string(),
- ResTable::normalizeForOutput(activityIcon.string())
- .string(),
- ResTable::normalizeForOutput(activityBanner.string())
- .string());
+ ResTable::normalizeForOutput(activityLabel.c_str())
+ .c_str(),
+ ResTable::normalizeForOutput(activityIcon.c_str())
+ .c_str(),
+ ResTable::normalizeForOutput(activityBanner.c_str())
+ .c_str());
}
}
if (!hasIntentFilter) {
@@ -1265,7 +1265,7 @@
goto bail;
}
String8 tag(ctag16);
- //printf("Depth %d, %s\n", depth, tag.string());
+ //printf("Depth %d, %s\n", depth, tag.c_str());
if (depth == 1) {
if (tag != "manifest") {
SourcePos(manifestFile, tree.getLineNumber()).error(
@@ -1274,13 +1274,13 @@
}
pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
printf("package: name='%s' ",
- ResTable::normalizeForOutput(pkg.string()).string());
+ ResTable::normalizeForOutput(pkg.c_str()).c_str());
int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
&error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:versionCode' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
if (versionCode > 0) {
@@ -1293,16 +1293,16 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:versionName' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
printf("versionName='%s'",
- ResTable::normalizeForOutput(versionName.string()).string());
+ ResTable::normalizeForOutput(versionName.c_str()).c_str());
String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
if (!splitName.isEmpty()) {
printf(" split='%s'", ResTable::normalizeForOutput(
- splitName.string()).string());
+ splitName.c_str()).c_str());
}
// For 'platformBuildVersionName', using both string and int type as a fallback
@@ -1313,7 +1313,7 @@
AaptXml::getIntegerAttribute(tree, NULL, "platformBuildVersionName", 0,
NULL);
if (platformBuildVersionName != "") {
- printf(" platformBuildVersionName='%s'", platformBuildVersionName.string());
+ printf(" platformBuildVersionName='%s'", platformBuildVersionName.c_str());
} else if (platformBuildVersionNameInt > 0) {
printf(" platformBuildVersionName='%d'", platformBuildVersionNameInt);
}
@@ -1326,7 +1326,7 @@
AaptXml::getIntegerAttribute(tree, NULL, "platformBuildVersionCode", 0,
NULL);
if (platformBuildVersionCode != "") {
- printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.string());
+ printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.c_str());
} else if (platformBuildVersionCodeInt > 0) {
printf(" platformBuildVersionCode='%d'", platformBuildVersionCodeInt);
}
@@ -1336,7 +1336,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:compileSdkVersion' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
if (compileSdkVersion > 0) {
@@ -1347,7 +1347,7 @@
COMPILE_SDK_VERSION_CODENAME_ATTR, &error);
if (compileSdkVersionCodename != "") {
printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput(
- compileSdkVersionCodename.string()).string());
+ compileSdkVersionCodename.c_str()).c_str());
}
printf("\n");
@@ -1357,7 +1357,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:installLocation' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
@@ -1387,7 +1387,7 @@
String8 label;
const size_t NL = locales.size();
for (size_t i=0; i<NL; i++) {
- const char* localeStr = locales[i].string();
+ const char* localeStr = locales[i].c_str();
assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
&error);
@@ -1395,13 +1395,13 @@
if (localeStr == NULL || strlen(localeStr) == 0) {
label = llabel;
printf("application-label:'%s'\n",
- ResTable::normalizeForOutput(llabel.string()).string());
+ ResTable::normalizeForOutput(llabel.c_str()).c_str());
} else {
if (label == "") {
label = llabel;
}
printf("application-label-%s:'%s'\n", localeStr,
- ResTable::normalizeForOutput(llabel.string()).string());
+ ResTable::normalizeForOutput(llabel.c_str()).c_str());
}
}
}
@@ -1415,7 +1415,7 @@
&error);
if (icon != "") {
printf("application-icon-%d:'%s'\n", densities[i],
- ResTable::normalizeForOutput(icon.string()).string());
+ ResTable::normalizeForOutput(icon.c_str()).c_str());
}
}
assets.setConfiguration(config);
@@ -1423,7 +1423,7 @@
String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:icon' attribute: %s", error.string());
+ "ERROR getting 'android:icon' attribute: %s", error.c_str());
goto bail;
}
int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0,
@@ -1431,7 +1431,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:testOnly' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
@@ -1439,15 +1439,15 @@
&error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:banner' attribute: %s", error.string());
+ "ERROR getting 'android:banner' attribute: %s", error.c_str());
goto bail;
}
printf("application: label='%s' ",
- ResTable::normalizeForOutput(label.string()).string());
- printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
+ ResTable::normalizeForOutput(label.c_str()).c_str());
+ printf("icon='%s'", ResTable::normalizeForOutput(icon.c_str()).c_str());
if (banner != "") {
printf(" banner='%s'",
- ResTable::normalizeForOutput(banner.string()).string());
+ ResTable::normalizeForOutput(banner.c_str()).c_str());
}
printf("\n");
if (testOnly != 0) {
@@ -1458,7 +1458,7 @@
ISGAME_ATTR, 0, &error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:isGame' attribute: %s", error.string());
+ "ERROR getting 'android:isGame' attribute: %s", error.c_str());
goto bail;
}
if (isGame != 0) {
@@ -1470,7 +1470,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:debuggable' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
if (debuggable != 0) {
@@ -1500,12 +1500,12 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:minSdkVersion' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
if (name == "Donut") targetSdk = SDK_DONUT;
printf("sdkVersion:'%s'\n",
- ResTable::normalizeForOutput(name.string()).string());
+ ResTable::normalizeForOutput(name.c_str()).c_str());
} else if (code != -1) {
targetSdk = code;
printf("sdkVersion:'%d'\n", code);
@@ -1522,7 +1522,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:targetSdkVersion' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
if (name == "Donut" && targetSdk < SDK_DONUT) {
@@ -1532,7 +1532,7 @@
targetSdk = SDK_CUR_DEVELOPMENT;
}
printf("targetSdkVersion:'%s'\n",
- ResTable::normalizeForOutput(name.string()).string());
+ ResTable::normalizeForOutput(name.c_str()).c_str());
} else if (code != -1) {
if (targetSdk < code) {
targetSdk = code;
@@ -1592,7 +1592,7 @@
group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:label' attribute: %s", error.string());
+ "ERROR getting 'android:label' attribute: %s", error.c_str());
goto bail;
}
featureGroups.add(group);
@@ -1608,7 +1608,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"failed to read attribute 'android:required': %s",
- error.string());
+ error.c_str());
goto bail;
}
@@ -1617,7 +1617,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"failed to read attribute 'android:version': %s",
- error.string());
+ error.c_str());
goto bail;
}
@@ -1638,7 +1638,7 @@
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:name' attribute: %s", error.string());
+ "ERROR getting 'android:name' attribute: %s", error.c_str());
goto bail;
}
@@ -1682,7 +1682,7 @@
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:name' attribute: %s", error.string());
+ "ERROR getting 'android:name' attribute: %s", error.c_str());
goto bail;
}
@@ -1701,37 +1701,37 @@
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
printf("uses-package:'%s'\n",
- ResTable::normalizeForOutput(name.string()).string());
+ ResTable::normalizeForOutput(name.c_str()).c_str());
} else {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:name' attribute: %s", error.string());
+ "ERROR getting 'android:name' attribute: %s", error.c_str());
goto bail;
}
} else if (tag == "original-package") {
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
printf("original-package:'%s'\n",
- ResTable::normalizeForOutput(name.string()).string());
+ ResTable::normalizeForOutput(name.c_str()).c_str());
} else {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:name' attribute: %s", error.string());
+ "ERROR getting 'android:name' attribute: %s", error.c_str());
goto bail;
}
} else if (tag == "supports-gl-texture") {
String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (name != "" && error == "") {
printf("supports-gl-texture:'%s'\n",
- ResTable::normalizeForOutput(name.string()).string());
+ ResTable::normalizeForOutput(name.c_str()).c_str());
} else {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:name' attribute: %s", error.string());
+ "ERROR getting 'android:name' attribute: %s", error.c_str());
goto bail;
}
} else if (tag == "compatible-screens") {
printCompatibleScreens(tree, &error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting compatible screens: %s", error.string());
+ "ERROR getting compatible screens: %s", error.c_str());
goto bail;
}
depth--;
@@ -1742,8 +1742,8 @@
&error);
if (publicKey != "" && error == "") {
printf("package-verifier: name='%s' publicKey='%s'\n",
- ResTable::normalizeForOutput(name.string()).string(),
- ResTable::normalizeForOutput(publicKey.string()).string());
+ ResTable::normalizeForOutput(name.c_str()).c_str(),
+ ResTable::normalizeForOutput(publicKey.c_str()).c_str());
}
}
}
@@ -1769,7 +1769,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:name' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
@@ -1778,7 +1778,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:label' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
@@ -1787,7 +1787,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:icon' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
@@ -1796,7 +1796,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:banner' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
@@ -1824,14 +1824,14 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:name' attribute for uses-library"
- " %s", error.string());
+ " %s", error.c_str());
goto bail;
}
int req = AaptXml::getIntegerAttribute(tree,
REQUIRED_ATTR, 1);
printf("uses-library%s:'%s'\n",
req ? "" : "-not-required", ResTable::normalizeForOutput(
- libraryName.string()).string());
+ libraryName.c_str()).c_str());
} else if (tag == "receiver") {
withinReceiver = true;
receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error);
@@ -1839,7 +1839,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:name' attribute for receiver:"
- " %s", error.string());
+ " %s", error.c_str());
goto bail;
}
@@ -1853,7 +1853,7 @@
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:permission' attribute for"
" receiver '%s': %s",
- receiverName.string(), error.string());
+ receiverName.c_str(), error.c_str());
}
} else if (tag == "service") {
withinService = true;
@@ -1862,7 +1862,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:name' attribute for "
- "service:%s", error.string());
+ "service:%s", error.c_str());
goto bail;
}
@@ -1887,7 +1887,7 @@
} else {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:permission' attribute for "
- "service '%s': %s", serviceName.string(), error.string());
+ "service '%s': %s", serviceName.c_str(), error.c_str());
}
} else if (tag == "provider") {
withinProvider = true;
@@ -1897,7 +1897,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:exported' attribute for provider:"
- " %s", error.string());
+ " %s", error.c_str());
goto bail;
}
@@ -1906,7 +1906,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:grantUriPermissions' attribute for "
- "provider: %s", error.string());
+ "provider: %s", error.c_str());
goto bail;
}
@@ -1915,7 +1915,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:permission' attribute for "
- "provider: %s", error.string());
+ "provider: %s", error.c_str());
goto bail;
}
@@ -1928,11 +1928,11 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:name' attribute for "
- "meta-data: %s", error.string());
+ "meta-data: %s", error.c_str());
goto bail;
}
printf("meta-data: name='%s' ",
- ResTable::normalizeForOutput(metaDataName.string()).string());
+ ResTable::normalizeForOutput(metaDataName.c_str()).c_str());
printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"),
&error);
if (error != "") {
@@ -1944,7 +1944,7 @@
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:value' or "
"'android:resource' attribute for "
- "meta-data: %s", error.string());
+ "meta-data: %s", error.c_str());
goto bail;
}
}
@@ -1956,7 +1956,7 @@
} else {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:name' attribute: %s",
- error.string());
+ error.c_str());
goto bail;
}
}
@@ -1969,13 +1969,13 @@
Feature feature(true);
int32_t featureVers = AaptXml::getIntegerAttribute(
- tree, androidSchema.string(), "version", 0, &error);
+ tree, androidSchema.c_str(), "version", 0, &error);
if (error == "") {
feature.version = featureVers;
} else {
SourcePos(manifestFile, tree.getLineNumber()).error(
"failed to read attribute 'android:version': %s",
- error.string());
+ error.c_str());
goto bail;
}
@@ -2016,8 +2016,8 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:name' attribute for "
- "meta-data tag in service '%s': %s", serviceName.string(),
- error.string());
+ "meta-data tag in service '%s': %s", serviceName.c_str(),
+ error.c_str());
goto bail;
}
@@ -2034,7 +2034,7 @@
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting 'android:resource' attribute for "
"meta-data tag in service '%s': %s",
- serviceName.string(), error.string());
+ serviceName.c_str(), error.c_str());
goto bail;
}
@@ -2043,7 +2043,7 @@
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
"ERROR getting AID category for service '%s'",
- serviceName.string());
+ serviceName.c_str());
goto bail;
}
@@ -2064,7 +2064,7 @@
action = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'android:name' attribute: %s", error.string());
+ "ERROR getting 'android:name' attribute: %s", error.c_str());
goto bail;
}
@@ -2120,7 +2120,7 @@
String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
SourcePos(manifestFile, tree.getLineNumber()).error(
- "ERROR getting 'name' attribute: %s", error.string());
+ "ERROR getting 'name' attribute: %s", error.c_str());
goto bail;
}
if (withinActivity) {
@@ -2352,7 +2352,7 @@
printf("locales:");
const size_t NL = locales.size();
for (size_t i=0; i<NL; i++) {
- const char* localeStr = locales[i].string();
+ const char* localeStr = locales[i].c_str();
if (localeStr == NULL || strlen(localeStr) == 0) {
localeStr = "--_--";
}
@@ -2373,7 +2373,7 @@
SortedVector<String8> architectures;
for (size_t i=0; i<dir->getFileCount(); i++) {
architectures.add(ResTable::normalizeForOutput(
- dir->getFileName(i).string()));
+ dir->getFileName(i).c_str()));
}
bool outputAltNativeCode = false;
@@ -2401,7 +2401,7 @@
}
if (index >= 0) {
- printf("native-code: '%s'\n", architectures[index].string());
+ printf("native-code: '%s'\n", architectures[index].c_str());
architectures.removeAt(index);
outputAltNativeCode = true;
}
@@ -2414,7 +2414,7 @@
}
printf("native-code:");
for (size_t i = 0; i < archCount; i++) {
- printf(" '%s'", architectures[i].string());
+ printf(" '%s'", architectures[i].c_str());
}
printf("\n");
}
@@ -2428,7 +2428,7 @@
res.getConfigurations(&configs);
const size_t N = configs.size();
for (size_t i=0; i<N; i++) {
- printf("%s\n", configs[i].toString().string());
+ printf("%s\n", configs[i].toString().c_str());
}
} else {
fprintf(stderr, "ERROR: unknown dump option '%s'\n", option);
@@ -2486,15 +2486,15 @@
for (int i = 1; i < bundle->getFileSpecCount(); i++) {
const char* fileName = bundle->getFileSpecEntry(i);
- if (strcasecmp(String8(fileName).getPathExtension().string(), ".gz") == 0) {
+ if (strcasecmp(String8(fileName).getPathExtension().c_str(), ".gz") == 0) {
printf(" '%s'... (from gzip)\n", fileName);
- result = zip->addGzip(fileName, String8(fileName).getBasePath().string(), NULL);
+ result = zip->addGzip(fileName, String8(fileName).getBasePath().c_str(), NULL);
} else {
if (bundle->getJunkPath()) {
String8 storageName = String8(fileName).getPathLeaf();
printf(" '%s' as '%s'...\n", fileName,
- ResTable::normalizeForOutput(storageName.string()).string());
- result = zip->add(fileName, storageName.string(),
+ ResTable::normalizeForOutput(storageName.c_str()).c_str());
+ result = zip->add(fileName, storageName.c_str(),
bundle->getCompressionMethod(), NULL);
} else {
printf(" '%s'...\n", fileName);
@@ -2581,7 +2581,7 @@
for (size_t i = 0; i < numDirs; i++) {
bool ignore = ignoreConfig;
const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
- const char* dirStr = subDir->getLeaf().string();
+ const char* dirStr = subDir->getLeaf().c_str();
if (!ignore && strstr(dirStr, "mipmap") == dirStr) {
ignore = true;
}
@@ -2604,7 +2604,7 @@
}
if (err != NO_ERROR) {
fprintf(stderr, "Failed to add %s (%s) to builder.\n",
- gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
+ gp->getPath().c_str(), gp->getFiles()[j]->getPrintableSource().c_str());
return err;
}
}
@@ -2620,13 +2620,13 @@
String8 ext(original.getPathExtension());
if (ext == String8(".apk")) {
return String8::format("%s_%s%s",
- original.getBasePath().string(),
- split->getDirectorySafeName().string(),
- ext.string());
+ original.getBasePath().c_str(),
+ split->getDirectorySafeName().c_str(),
+ ext.c_str());
}
- return String8::format("%s_%s", original.string(),
- split->getDirectorySafeName().string());
+ return String8::format("%s_%s", original.c_str(),
+ split->getDirectorySafeName().c_str());
}
/*
@@ -2712,7 +2712,7 @@
for (size_t i = 0; i < numSplits; i++) {
std::set<ConfigDescription> configs;
if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
- fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
+ fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].c_str());
goto bail;
}
@@ -2835,7 +2835,7 @@
String8 outputPath = buildApkName(String8(outputAPKFile), split);
err = writeAPK(bundle, outputPath, split);
if (err != NO_ERROR) {
- fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
+ fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.c_str());
goto bail;
}
}
diff --git a/tools/aapt/CrunchCache.cpp b/tools/aapt/CrunchCache.cpp
index 7b8a576..1f2febe 100644
--- a/tools/aapt/CrunchCache.cpp
+++ b/tools/aapt/CrunchCache.cpp
@@ -44,7 +44,7 @@
// This efficiently strips the source directory prefix from our path.
// Also, String8 doesn't have a substring method so this is what we've
// got to work with.
- const char* rPathPtr = mSourceFiles.keyAt(0).string()+mSourcePath.length();
+ const char* rPathPtr = mSourceFiles.keyAt(0).c_str()+mSourcePath.length();
// Strip leading slash if present
int offset = 0;
if (rPathPtr[0] == OS_PATH_SEPARATOR)
diff --git a/tools/aapt/DirectoryWalker.h b/tools/aapt/DirectoryWalker.h
index 88031d0..cea3a6e 100644
--- a/tools/aapt/DirectoryWalker.h
+++ b/tools/aapt/DirectoryWalker.h
@@ -57,7 +57,7 @@
virtual bool openDir(String8 path) {
mBasePath = path;
dir = NULL;
- dir = opendir(mBasePath.string() );
+ dir = opendir(mBasePath.c_str() );
if (dir == NULL)
return false;
@@ -78,7 +78,7 @@
mEntry = *entryPtr;
// Get stats
String8 fullPath = mBasePath.appendPathCopy(mEntry.d_name);
- stat(fullPath.string(),&mStats);
+ stat(fullPath.c_str(),&mStats);
return &mEntry;
};
// Get the stats for the current entry
diff --git a/tools/aapt/FileFinder.cpp b/tools/aapt/FileFinder.cpp
index c9d0744..a5c19806 100644
--- a/tools/aapt/FileFinder.cpp
+++ b/tools/aapt/FileFinder.cpp
@@ -59,14 +59,14 @@
String8 fullPath = basePath.appendPathCopy(entryName);
// If this entry is a directory we'll recurse into it
- if (isDirectory(fullPath.string()) ) {
+ if (isDirectory(fullPath.c_str()) ) {
DirectoryWalker* copy = dw->clone();
findFiles(fullPath, extensions, fileStore,copy);
delete copy;
}
// If this entry is a file, we'll pass it over to checkAndAddFile
- if (isFile(fullPath.string()) ) {
+ if (isFile(fullPath.c_str()) ) {
checkAndAddFile(fullPath,dw->entryStats(),extensions,fileStore);
}
}
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 627a231..c6c7e96 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -1328,13 +1328,13 @@
png_init_io(read_ptr, fp);
- read_png(printableName.string(), read_ptr, read_info, imageInfo);
+ read_png(printableName.c_str(), read_ptr, read_info, imageInfo);
const size_t nameLen = file->getPath().length();
if (nameLen > 6) {
- const char* name = file->getPath().string();
+ const char* name = file->getPath().c_str();
if (name[nameLen-5] == '9' && name[nameLen-6] == '.') {
- if (do_9patch(printableName.string(), imageInfo) != NO_ERROR) {
+ if (do_9patch(printableName.c_str(), imageInfo) != NO_ERROR) {
return false;
}
}
@@ -1349,7 +1349,7 @@
return false;
}
- write_png(printableName.string(), write_ptr, write_info, *imageInfo, bundle);
+ write_png(printableName.c_str(), write_ptr, write_info, *imageInfo, bundle);
return true;
}
@@ -1360,7 +1360,7 @@
String8 ext(file->getPath().getPathExtension());
// We currently only process PNG images.
- if (strcmp(ext.string(), ".png") != 0) {
+ if (strcmp(ext.c_str(), ".png") != 0) {
return NO_ERROR;
}
@@ -1371,7 +1371,7 @@
String8 printableName(file->getPrintableSource());
if (bundle->getVerbose()) {
- printf("Processing image: %s\n", printableName.string());
+ printf("Processing image: %s\n", printableName.c_str());
}
png_structp read_ptr = NULL;
@@ -1385,9 +1385,9 @@
status_t error = UNKNOWN_ERROR;
- fp = fopen(file->getSourceFile().string(), "rb");
+ fp = fopen(file->getSourceFile().c_str(), "rb");
if (fp == NULL) {
- fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.string());
+ fprintf(stderr, "%s: ERROR: Unable to open PNG file\n", printableName.c_str());
goto bail;
}
@@ -1434,7 +1434,7 @@
size_t newSize = file->getSize();
float factor = ((float)newSize)/oldSize;
int percent = (int)(factor*100);
- printf(" (processed image %s: %d%% size of source)\n", printableName.string(), percent);
+ printf(" (processed image %s: %d%% size of source)\n", printableName.c_str(), percent);
}
bail:
@@ -1450,7 +1450,7 @@
if (error != NO_ERROR) {
fprintf(stderr, "ERROR: Failure processing PNG image %s\n",
- file->getPrintableSource().string());
+ file->getPrintableSource().c_str());
}
return error;
}
@@ -1470,13 +1470,13 @@
status_t error = UNKNOWN_ERROR;
if (bundle->getVerbose()) {
- printf("Processing image to cache: %s => %s\n", source.string(), dest.string());
+ printf("Processing image to cache: %s => %s\n", source.c_str(), dest.c_str());
}
// Get a file handler to read from
- fp = fopen(source.string(),"rb");
+ fp = fopen(source.c_str(),"rb");
if (fp == NULL) {
- fprintf(stderr, "%s ERROR: Unable to open PNG file\n", source.string());
+ fprintf(stderr, "%s ERROR: Unable to open PNG file\n", source.c_str());
return error;
}
@@ -1507,7 +1507,7 @@
png_init_io(read_ptr,fp);
// Actually read data from the file
- read_png(source.string(), read_ptr, read_info, &imageInfo);
+ read_png(source.c_str(), read_ptr, read_info, &imageInfo);
// We're done reading so we can clean up
// Find old file size before releasing handle
@@ -1519,7 +1519,7 @@
// Check to see if we're dealing with a 9-patch
// If we are, process appropriately
if (source.getBasePath().getPathExtension() == ".9") {
- if (do_9patch(source.string(), &imageInfo) != NO_ERROR) {
+ if (do_9patch(source.c_str(), &imageInfo) != NO_ERROR) {
return error;
}
}
@@ -1541,9 +1541,9 @@
}
// Open up our destination file for writing
- fp = fopen(dest.string(), "wb");
+ fp = fopen(dest.c_str(), "wb");
if (!fp) {
- fprintf(stderr, "%s ERROR: Unable to open PNG file\n", dest.string());
+ fprintf(stderr, "%s ERROR: Unable to open PNG file\n", dest.c_str());
png_destroy_write_struct(&write_ptr, &write_info);
return error;
}
@@ -1559,11 +1559,11 @@
}
// Actually write out to the new png
- write_png(dest.string(), write_ptr, write_info, imageInfo, bundle);
+ write_png(dest.c_str(), write_ptr, write_info, imageInfo, bundle);
if (bundle->getVerbose()) {
// Find the size of our new file
- FILE* reader = fopen(dest.string(), "rb");
+ FILE* reader = fopen(dest.c_str(), "rb");
fseek(reader, 0, SEEK_END);
size_t newSize = (size_t)ftell(reader);
fclose(reader);
@@ -1571,7 +1571,7 @@
float factor = ((float)newSize)/oldSize;
int percent = (int)(factor*100);
printf(" (processed image to cache entry %s: %d%% size of source)\n",
- dest.string(), percent);
+ dest.c_str(), percent);
}
//Clean up
@@ -1588,7 +1588,7 @@
// At this point, now that we have all the resource data, all we need to
// do is compile XML files.
- if (strcmp(ext.string(), ".xml") == 0) {
+ if (strcmp(ext.c_str(), ".xml") == 0) {
String16 resourceName(parseResourceName(file->getSourceFile().getPathLeaf()));
return compileXmlFile(bundle, assets, resourceName, file, table);
}
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index f06643dc..965655b 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -69,39 +69,39 @@
* If "update" is set, update the contents of the existing archive.
* Else, if "force" is set, remove the existing archive.
*/
- FileType fileType = getFileType(outputFile.string());
+ FileType fileType = getFileType(outputFile.c_str());
if (fileType == kFileTypeNonexistent) {
// okay, create it below
} else if (fileType == kFileTypeRegular) {
if (bundle->getUpdate()) {
// okay, open it below
} else if (bundle->getForce()) {
- if (unlink(outputFile.string()) != 0) {
- fprintf(stderr, "ERROR: unable to remove '%s': %s\n", outputFile.string(),
+ if (unlink(outputFile.c_str()) != 0) {
+ fprintf(stderr, "ERROR: unable to remove '%s': %s\n", outputFile.c_str(),
strerror(errno));
goto bail;
}
} else {
fprintf(stderr, "ERROR: '%s' exists (use '-f' to force overwrite)\n",
- outputFile.string());
+ outputFile.c_str());
goto bail;
}
} else {
- fprintf(stderr, "ERROR: '%s' exists and is not a regular file\n", outputFile.string());
+ fprintf(stderr, "ERROR: '%s' exists and is not a regular file\n", outputFile.c_str());
goto bail;
}
if (bundle->getVerbose()) {
printf("%s '%s'\n", (fileType == kFileTypeNonexistent) ? "Creating" : "Opening",
- outputFile.string());
+ outputFile.c_str());
}
status_t status;
zip = new ZipFile;
- status = zip->open(outputFile.string(), ZipFile::kOpenReadWrite | ZipFile::kOpenCreate);
+ status = zip->open(outputFile.c_str(), ZipFile::kOpenReadWrite | ZipFile::kOpenCreate);
if (status != NO_ERROR) {
fprintf(stderr, "ERROR: unable to open '%s' as Zip file for writing\n",
- outputFile.string());
+ outputFile.c_str());
goto bail;
}
@@ -112,7 +112,7 @@
count = processAssets(bundle, zip, outputSet);
if (count < 0) {
fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n",
- outputFile.string());
+ outputFile.c_str());
result = count;
goto bail;
}
@@ -124,7 +124,7 @@
count = processJarFiles(bundle, zip);
if (count < 0) {
fprintf(stderr, "ERROR: unable to process jar files while packaging '%s'\n",
- outputFile.string());
+ outputFile.c_str());
result = count;
goto bail;
}
@@ -169,12 +169,12 @@
/* anything here? */
if (zip->getNumEntries() == 0) {
if (bundle->getVerbose()) {
- printf("Archive is empty -- removing %s\n", outputFile.getPathLeaf().string());
+ printf("Archive is empty -- removing %s\n", outputFile.getPathLeaf().c_str());
}
delete zip; // close the file so we can remove it in Win32
zip = NULL;
- if (unlink(outputFile.string()) != 0) {
- fprintf(stderr, "warning: could not unlink '%s'\n", outputFile.string());
+ if (unlink(outputFile.c_str()) != 0) {
+ fprintf(stderr, "warning: could not unlink '%s'\n", outputFile.c_str());
}
}
@@ -187,9 +187,9 @@
String8 dependencyFile = outputFile;
dependencyFile.append(".d");
- FILE* fp = fopen(dependencyFile.string(), "a");
+ FILE* fp = fopen(dependencyFile.c_str(), "a");
// Add this file to the dependency file
- fprintf(fp, "%s \\\n", outputFile.string());
+ fprintf(fp, "%s \\\n", outputFile.c_str());
fclose(fp);
}
@@ -199,10 +199,10 @@
delete zip; // must close before remove in Win32
if (result != NO_ERROR) {
if (bundle->getVerbose()) {
- printf("Removing %s due to earlier failures\n", outputFile.string());
+ printf("Removing %s due to earlier failures\n", outputFile.c_str());
}
- if (unlink(outputFile.string()) != 0) {
- fprintf(stderr, "warning: could not unlink '%s'\n", outputFile.string());
+ if (unlink(outputFile.c_str()) != 0) {
+ fprintf(stderr, "warning: could not unlink '%s'\n", outputFile.c_str());
}
}
@@ -267,31 +267,31 @@
int fileNameLen = storageName.length();
int excludeExtensionLen = strlen(kExcludeExtension);
if (fileNameLen > excludeExtensionLen
- && (0 == strcmp(storageName.string() + (fileNameLen - excludeExtensionLen),
+ && (0 == strcmp(storageName.c_str() + (fileNameLen - excludeExtensionLen),
kExcludeExtension))) {
- fprintf(stderr, "warning: '%s' not added to Zip\n", storageName.string());
+ fprintf(stderr, "warning: '%s' not added to Zip\n", storageName.c_str());
return true;
}
- if (strcasecmp(storageName.getPathExtension().string(), ".gz") == 0) {
+ if (strcasecmp(storageName.getPathExtension().c_str(), ".gz") == 0) {
fromGzip = true;
storageName = storageName.getBasePath();
}
if (bundle->getUpdate()) {
- entry = zip->getEntryByName(storageName.string());
+ entry = zip->getEntryByName(storageName.c_str());
if (entry != NULL) {
/* file already exists in archive; there can be only one */
if (entry->getMarked()) {
fprintf(stderr,
"ERROR: '%s' exists twice (check for with & w/o '.gz'?)\n",
- file->getPrintableSource().string());
+ file->getPrintableSource().c_str());
return false;
}
if (!hasData) {
const String8& srcName = file->getSourceFile();
time_t fileModWhen;
- fileModWhen = getFileModDate(srcName.string());
+ fileModWhen = getFileModDate(srcName.c_str());
if (fileModWhen == (time_t) -1) { // file existence tested earlier,
return false; // not expecting an error here
}
@@ -299,14 +299,14 @@
if (fileModWhen > entry->getModWhen()) {
// mark as deleted so add() will succeed
if (bundle->getVerbose()) {
- printf(" (removing old '%s')\n", storageName.string());
+ printf(" (removing old '%s')\n", storageName.c_str());
}
zip->remove(entry);
} else {
// version in archive is newer
if (bundle->getVerbose()) {
- printf(" (not updating '%s')\n", storageName.string());
+ printf(" (not updating '%s')\n", storageName.c_str());
}
entry->setMarked(true);
return true;
@@ -321,22 +321,22 @@
//android_setMinPriority(NULL, ANDROID_LOG_VERBOSE);
if (fromGzip) {
- result = zip->addGzip(file->getSourceFile().string(), storageName.string(), &entry);
+ result = zip->addGzip(file->getSourceFile().c_str(), storageName.c_str(), &entry);
} else if (!hasData) {
/* don't compress certain files, e.g. PNGs */
int compressionMethod = bundle->getCompressionMethod();
if (!okayToCompress(bundle, storageName)) {
compressionMethod = ZipEntry::kCompressStored;
}
- result = zip->add(file->getSourceFile().string(), storageName.string(), compressionMethod,
+ result = zip->add(file->getSourceFile().c_str(), storageName.c_str(), compressionMethod,
&entry);
} else {
- result = zip->add(file->getData(), file->getSize(), storageName.string(),
+ result = zip->add(file->getData(), file->getSize(), storageName.c_str(),
file->getCompressionMethod(), &entry);
}
if (result == NO_ERROR) {
if (bundle->getVerbose()) {
- printf(" '%s'%s", storageName.string(), fromGzip ? " (from .gz)" : "");
+ printf(" '%s'%s", storageName.c_str(), fromGzip ? " (from .gz)" : "");
if (entry->getCompressionMethod() == ZipEntry::kCompressStored) {
printf(" (not compressed)\n");
} else {
@@ -348,10 +348,10 @@
} else {
if (result == ALREADY_EXISTS) {
fprintf(stderr, " Unable to add '%s': file already in archive (try '-u'?)\n",
- file->getPrintableSource().string());
+ file->getPrintableSource().c_str());
} else {
fprintf(stderr, " Unable to add '%s': Zip add failed (%d)\n",
- file->getPrintableSource().string(), result);
+ file->getPrintableSource().c_str(), result);
}
return false;
}
@@ -372,7 +372,7 @@
return true;
for (i = 0; i < NELEM(kNoCompressExt); i++) {
- if (strcasecmp(ext.string(), kNoCompressExt[i]) == 0)
+ if (strcasecmp(ext.c_str(), kNoCompressExt[i]) == 0)
return false;
}
@@ -383,7 +383,7 @@
if (pos < 0) {
continue;
}
- const char* path = pathName.string();
+ const char* path = pathName.c_str();
if (strcasecmp(path + pos, str) == 0) {
return false;
}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index dd3ebdb..4ca3a68 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -57,8 +57,8 @@
String8 parseResourceName(const String8& leaf)
{
- const char* firstDot = strchr(leaf.string(), '.');
- const char* str = leaf.string();
+ const char* firstDot = strchr(leaf.c_str(), '.');
+ const char* str = leaf.c_str();
if (firstDot) {
return String8(str, firstDot-str);
@@ -132,7 +132,7 @@
mParams = file->getGroupEntry().toParams();
if (kIsDebug) {
printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n",
- group->getPath().string(), mParams.mcc, mParams.mnc,
+ group->getPath().c_str(), mParams.mcc, mParams.mnc,
mParams.language[0] ? mParams.language[0] : '-',
mParams.language[1] ? mParams.language[1] : '-',
mParams.country[0] ? mParams.country[0] : '-',
@@ -147,12 +147,12 @@
mBaseName = parseResourceName(leaf);
if (mBaseName == "") {
fprintf(stderr, "Error: malformed resource filename %s\n",
- file->getPrintableSource().string());
+ file->getPrintableSource().c_str());
return UNKNOWN_ERROR;
}
if (kIsDebug) {
- printf("file name=%s\n", mBaseName.string());
+ printf("file name=%s\n", mBaseName.c_str());
}
return NO_ERROR;
@@ -222,7 +222,7 @@
{
if (grp->getFiles().size() != 1) {
fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
- grp->getFiles().valueAt(0)->getPrintableSource().string());
+ grp->getFiles().valueAt(0)->getPrintableSource().c_str());
}
sp<AaptFile> file = grp->getFiles().valueAt(0);
@@ -243,20 +243,20 @@
size_t len;
if (code != ResXMLTree::START_TAG) {
fprintf(stderr, "%s:%d: No start tag found\n",
- file->getPrintableSource().string(), block.getLineNumber());
+ file->getPrintableSource().c_str(), block.getLineNumber());
return UNKNOWN_ERROR;
}
- if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) {
+ if (strcmp16(block.getElementName(&len), String16("manifest").c_str()) != 0) {
fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n",
- file->getPrintableSource().string(), block.getLineNumber(),
- String8(block.getElementName(&len)).string());
+ file->getPrintableSource().c_str(), block.getLineNumber(),
+ String8(block.getElementName(&len)).c_str());
return UNKNOWN_ERROR;
}
ssize_t nameIndex = block.indexOfAttribute(NULL, "package");
if (nameIndex < 0) {
fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n",
- file->getPrintableSource().string(), block.getLineNumber());
+ file->getPrintableSource().c_str(), block.getLineNumber());
return UNKNOWN_ERROR;
}
@@ -264,19 +264,19 @@
ssize_t revisionCodeIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "revisionCode");
if (revisionCodeIndex >= 0) {
- bundle->setRevisionCode(String8(block.getAttributeStringValue(revisionCodeIndex, &len)).string());
+ bundle->setRevisionCode(String8(block.getAttributeStringValue(revisionCodeIndex, &len)).c_str());
}
String16 uses_sdk16("uses-sdk");
while ((code=block.next()) != ResXMLTree::END_DOCUMENT
&& code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::START_TAG) {
- if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), uses_sdk16.c_str()) == 0) {
ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE,
"minSdkVersion");
if (minSdkIndex >= 0) {
const char16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len);
- const char* minSdk8 = strdup(String8(minSdk16).string());
+ const char* minSdk8 = strdup(String8(minSdk16).c_str());
bundle->setManifestMinSdkVersion(minSdk8);
}
}
@@ -305,17 +305,17 @@
while ((res=it.next()) == NO_ERROR) {
if (bundle->getVerbose()) {
printf(" (new resource id %s from %s)\n",
- it.getBaseName().string(), it.getFile()->getPrintableSource().string());
+ it.getBaseName().c_str(), it.getFile()->getPrintableSource().c_str());
}
String16 baseName(it.getBaseName());
- const char16_t* str = baseName.string();
+ const char16_t* str = baseName.c_str();
const char16_t* const end = str + baseName.size();
while (str < end) {
if (!((*str >= 'a' && *str <= 'z')
|| (*str >= '0' && *str <= '9')
|| *str == '_' || *str == '.')) {
fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n",
- it.getPath().string());
+ it.getPath().c_str());
hasErrors = true;
}
str++;
@@ -413,7 +413,7 @@
sp<ResourceTypeSet> set = new ResourceTypeSet();
if (kIsDebug) {
printf("Creating new resource type set for leaf %s with group %s (%p)\n",
- leafName.string(), group->getPath().string(), group.get());
+ leafName.c_str(), group->getPath().c_str(), group.get());
}
set->add(leafName, group);
resources->add(resType, set);
@@ -423,21 +423,21 @@
if (index < 0) {
if (kIsDebug) {
printf("Adding to resource type set for leaf %s group %s (%p)\n",
- leafName.string(), group->getPath().string(), group.get());
+ leafName.c_str(), group->getPath().c_str(), group.get());
}
set->add(leafName, group);
} else {
sp<AaptGroup> existingGroup = set->valueAt(index);
if (kIsDebug) {
printf("Extending to resource type set for leaf %s group %s (%p)\n",
- leafName.string(), group->getPath().string(), group.get());
+ leafName.c_str(), group->getPath().c_str(), group.get());
}
for (size_t j=0; j<files.size(); j++) {
if (kIsDebug) {
printf("Adding file %s in group %s resType %s\n",
- files.valueAt(j)->getSourceFile().string(),
- files.keyAt(j).toDirName(String8()).string(),
- resType.string());
+ files.valueAt(j)->getSourceFile().c_str(),
+ files.keyAt(j).toDirName(String8()).c_str(),
+ resType.c_str());
}
existingGroup->addFile(files.valueAt(j));
}
@@ -455,14 +455,14 @@
for (int i=0; i<N; i++) {
const sp<AaptDir>& d = dirs.itemAt(i);
if (kIsDebug) {
- printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(),
- d->getLeaf().string());
+ printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().c_str(),
+ d->getLeaf().c_str());
}
collect_files(d, resources);
// don't try to include the res dir
if (kIsDebug) {
- printf("Removing dir leaf %s\n", d->getLeaf().string());
+ printf("Removing dir leaf %s\n", d->getLeaf().c_str());
}
ass->removeDir(d->getLeaf());
}
@@ -490,8 +490,8 @@
int strIdx;
if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) {
fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n",
- path.string(), parser.getLineNumber(),
- String8(parser.getElementName(&len)).string(), attr,
+ path.c_str(), parser.getLineNumber(),
+ String8(parser.getElementName(&len)).c_str(), attr,
value.data);
return ATTR_NOT_FOUND;
}
@@ -502,12 +502,12 @@
str = pool->stringAt(value.data, &len);
}
printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr,
- specFlags, strIdx, str != NULL ? String8(str).string() : "???");
+ specFlags, strIdx, str != NULL ? String8(str).c_str() : "???");
#endif
if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) {
fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n",
- path.string(), parser.getLineNumber(),
- String8(parser.getElementName(&len)).string(), attr,
+ path.c_str(), parser.getLineNumber(),
+ String8(parser.getElementName(&len)).c_str(), attr,
specFlags);
return ATTR_NOT_FOUND;
}
@@ -515,20 +515,20 @@
if (value.dataType == Res_value::TYPE_STRING) {
if (pool == NULL) {
fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n",
- path.string(), parser.getLineNumber(),
- String8(parser.getElementName(&len)).string(), attr);
+ path.c_str(), parser.getLineNumber(),
+ String8(parser.getElementName(&len)).c_str(), attr);
return ATTR_NOT_FOUND;
}
if ((str = UnpackOptionalString(pool->stringAt(value.data), &len)) == NULL) {
fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n",
- path.string(), parser.getLineNumber(),
- String8(parser.getElementName(&len)).string(), attr);
+ path.c_str(), parser.getLineNumber(),
+ String8(parser.getElementName(&len)).c_str(), attr);
return ATTR_NOT_FOUND;
}
} else {
fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n",
- path.string(), parser.getLineNumber(),
- String8(parser.getElementName(&len)).string(), attr,
+ path.c_str(), parser.getLineNumber(),
+ String8(parser.getElementName(&len)).c_str(), attr,
value.dataType);
return ATTR_NOT_FOUND;
}
@@ -546,30 +546,30 @@
}
if (!okay) {
fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n",
- path.string(), parser.getLineNumber(),
- String8(parser.getElementName(&len)).string(), attr, (char)str[i]);
+ path.c_str(), parser.getLineNumber(),
+ String8(parser.getElementName(&len)).c_str(), attr, (char)str[i]);
return (int)i;
}
}
}
if (*str == ' ') {
fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n",
- path.string(), parser.getLineNumber(),
- String8(parser.getElementName(&len)).string(), attr);
+ path.c_str(), parser.getLineNumber(),
+ String8(parser.getElementName(&len)).c_str(), attr);
return ATTR_LEADING_SPACES;
}
if (len != 0 && str[len-1] == ' ') {
fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n",
- path.string(), parser.getLineNumber(),
- String8(parser.getElementName(&len)).string(), attr);
+ path.c_str(), parser.getLineNumber(),
+ String8(parser.getElementName(&len)).c_str(), attr);
return ATTR_TRAILING_SPACES;
}
return ATTR_OKAY;
}
if (required) {
fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n",
- path.string(), parser.getLineNumber(),
- String8(parser.getElementName(&len)).string(), attr);
+ path.c_str(), parser.getLineNumber(),
+ String8(parser.getElementName(&len)).c_str(), attr);
return ATTR_NOT_FOUND;
}
return ATTR_OKAY;
@@ -584,7 +584,7 @@
ssize_t index = parser.indexOfAttribute(NULL, "id");
if (index >= 0) {
fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
- path.string(), parser.getLineNumber());
+ path.c_str(), parser.getLineNumber());
}
}
}
@@ -618,7 +618,7 @@
size_t overlayCount = overlaySet->size();
for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) {
if (bundle->getVerbose()) {
- printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
+ printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).c_str());
}
ssize_t baseIndex = -1;
if (baseSet->get() != NULL) {
@@ -638,11 +638,11 @@
baseGroup->getFiles();
for (size_t i=0; i < baseFiles.size(); i++) {
printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i,
- baseFiles.keyAt(i).toString().string());
+ baseFiles.keyAt(i).toString().c_str());
}
for (size_t i=0; i < overlayFiles.size(); i++) {
printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i,
- overlayFiles.keyAt(i).toString().string());
+ overlayFiles.keyAt(i).toString().c_str());
}
}
@@ -657,16 +657,16 @@
if (bundle->getVerbose()) {
printf("found a match (" ZD ") for overlay file %s, for flavor %s\n",
(ZD_TYPE) baseFileIndex,
- overlayGroup->getLeaf().string(),
- overlayFiles.keyAt(overlayGroupIndex).toString().string());
+ overlayGroup->getLeaf().c_str(),
+ overlayFiles.keyAt(overlayGroupIndex).toString().c_str());
}
baseGroup->removeFile(baseFileIndex);
} else {
// didn't find a match fall through and add it..
if (true || bundle->getVerbose()) {
printf("nothing matches overlay file %s, for flavor %s\n",
- overlayGroup->getLeaf().string(),
- overlayFiles.keyAt(overlayGroupIndex).toString().string());
+ overlayGroup->getLeaf().c_str(),
+ overlayFiles.keyAt(overlayGroupIndex).toString().c_str());
}
}
baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
@@ -728,7 +728,7 @@
if (errorOnFailedInsert) {
fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);"
" cannot insert new value %s.\n",
- String8(attr).string(), String8(ns).string(), value);
+ String8(attr).c_str(), String8(ns).c_str(), value);
return false;
}
@@ -763,7 +763,7 @@
// .asdf .a.b --> package.asdf package.a.b
// asdf.adsf --> asdf.asdf
String8 className;
- const char* p = name.string();
+ const char* p = name.c_str();
const char* q = strchr(p, '.');
if (p == q) {
className += package;
@@ -776,7 +776,7 @@
className += name;
}
if (kIsDebug) {
- printf("Qualifying class '%s' to '%s'", name.string(), className.string());
+ printf("Qualifying class '%s' to '%s'", name.c_str(), className.c_str());
}
attr->string.setTo(String16(className));
}
@@ -810,7 +810,7 @@
const char* err;
String16 iconPackage, iconType, iconName;
- if (!ResTable::expandResourceRef(iconRef.string(), iconRef.size(), &iconPackage, &iconType,
+ if (!ResTable::expandResourceRef(iconRef.c_str(), iconRef.size(), &iconPackage, &iconType,
&iconName, NULL, &table->getAssetsPackage(), &err,
&publicOnly)) {
// Errors will be raised in later XML compilation.
@@ -824,7 +824,7 @@
}
String16 roundIconPackage, roundIconType, roundIconName;
- if (!ResTable::expandResourceRef(roundIconRef.string(), roundIconRef.size(), &roundIconPackage,
+ if (!ResTable::expandResourceRef(roundIconRef.c_str(), roundIconRef.size(), &roundIconPackage,
&roundIconType, &roundIconName, NULL, &table->getAssetsPackage(),
&err, &publicOnly)) {
// Errors will be raised in later XML compilation.
@@ -839,9 +839,9 @@
return;
}
- String16 aliasValue = String16(String8::format("@%s:%s/%s", String8(iconPackage).string(),
- String8(iconType).string(),
- String8(iconName).string()));
+ String16 aliasValue = String16(String8::format("@%s:%s/%s", String8(iconPackage).c_str(),
+ String8(iconType).c_str(),
+ String8(iconName).c_str()));
// Add an equivalent v26 entry to the roundIcon for each v26 variant of the regular icon.
const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& configList =
@@ -872,7 +872,7 @@
const XMLNode::attribute_entry* attr = root->getAttribute(
String16(RESOURCES_ANDROID_NAMESPACE), String16("versionCode"));
if (attr != NULL) {
- bundle->setVersionCode(strdup(String8(attr->string).string()));
+ bundle->setVersionCode(strdup(String8(attr->string).c_str()));
}
}
@@ -883,7 +883,7 @@
const XMLNode::attribute_entry* attr = root->getAttribute(
String16(RESOURCES_ANDROID_NAMESPACE), String16("versionName"));
if (attr != NULL) {
- bundle->setVersionName(strdup(String8(attr->string).string()));
+ bundle->setVersionName(strdup(String8(attr->string).c_str()));
}
}
@@ -914,7 +914,7 @@
const XMLNode::attribute_entry* attr = vers->getAttribute(
String16(RESOURCES_ANDROID_NAMESPACE), String16("minSdkVersion"));
if (attr != NULL) {
- bundle->setMinSdkVersion(strdup(String8(attr->string).string()));
+ bundle->setMinSdkVersion(strdup(String8(attr->string).c_str()));
}
}
@@ -970,7 +970,7 @@
String8 origPackage(attr->string);
attr->string.setTo(String16(manifestPackageNameOverride));
if (kIsDebug) {
- printf("Overriding package '%s' to be '%s'\n", origPackage.string(),
+ printf("Overriding package '%s' to be '%s'\n", origPackage.c_str(),
manifestPackageNameOverride);
}
@@ -1071,7 +1071,7 @@
static ssize_t extractPlatformBuildVersion(const ResTable& table, ResXMLTree& tree, Bundle* bundle) {
// First check if we should be recording the compileSdkVersion* attributes.
static const String16 compileSdkVersionName("android:attr/compileSdkVersion");
- const bool useCompileSdkVersion = table.identifierForName(compileSdkVersionName.string(),
+ const bool useCompileSdkVersion = table.identifierForName(compileSdkVersionName.c_str(),
compileSdkVersionName.size()) != 0u;
size_t len;
@@ -1223,7 +1223,7 @@
// Add the 'revisionCode' attribute, which is set to the original revisionCode.
if (bundle->getRevisionCode().size() > 0) {
if (!addTagAttribute(manifest, RESOURCES_ANDROID_NAMESPACE, "revisionCode",
- bundle->getRevisionCode().string(), true, true)) {
+ bundle->getRevisionCode().c_str(), true, true)) {
return UNKNOWN_ERROR;
}
}
@@ -1270,7 +1270,7 @@
}
if (kIsDebug) {
- printf("Creating resources for package %s\n", assets->getPackage().string());
+ printf("Creating resources for package %s\n", assets->getPackage().c_str());
}
// Set the private symbols package if it was declared.
@@ -1804,7 +1804,7 @@
flattenedTable, split->isBase());
if (err != NO_ERROR) {
fprintf(stderr, "Failed to generate resource table for split '%s'\n",
- split->getPrintableName().string());
+ split->getPrintableName().c_str());
return err;
}
split->addEntry(String8("resources.arsc"), flattenedTable);
@@ -1821,7 +1821,7 @@
err = resTable.add(flattenedTable->getData(), flattenedTable->getSize());
if (err != NO_ERROR) {
fprintf(stderr, "Generated resource table for split '%s' is corrupt.\n",
- split->getPrintableName().string());
+ split->getPrintableName().c_str());
return err;
}
@@ -1849,7 +1849,7 @@
if (block < 0) {
hasError = true;
SourcePos().error("%s has no definition for density split '%s'",
- symbol.toString().string(), config.toString().string());
+ symbol.toString().c_str(), config.toString().c_str());
if (bundle->getVerbose()) {
const Vector<SymbolDefinition>& defs = densityVaryingResources[k];
@@ -1857,7 +1857,7 @@
for (size_t d = 0; d < defCount; d++) {
const SymbolDefinition& def = defs[d];
def.source.error("%s has definition for %s",
- symbol.toString().string(), def.config.toString().string());
+ symbol.toString().c_str(), def.config.toString().c_str());
}
if (defCount < defs.size()) {
@@ -1880,7 +1880,7 @@
generatedManifest, &table);
if (err != NO_ERROR) {
fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n",
- split->getPrintableName().string());
+ split->getPrintableName().c_str());
return err;
}
split->addEntry(String8("AndroidManifest.xml"), generatedManifest);
@@ -1960,7 +1960,7 @@
if (block.getElementNamespace(&len) != NULL) {
continue;
}
- if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), manifest16.c_str()) == 0) {
if (validateAttr(manifestPath, finalResTable, block, NULL, "package",
packageIdentChars, true) != ATTR_OKAY) {
hasErrors = true;
@@ -1969,10 +1969,10 @@
"sharedUserId", packageIdentChars, false) != ATTR_OKAY) {
hasErrors = true;
}
- } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0
- || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), permission16.c_str()) == 0
+ || strcmp16(block.getElementName(&len), permission_group16.c_str()) == 0) {
const bool isGroup = strcmp16(block.getElementName(&len),
- permission_group16.string()) == 0;
+ permission_group16.c_str()) == 0;
if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
"name", isGroup ? packageIdentCharsWithTheStupid
: packageIdentChars, true) != ATTR_OKAY) {
@@ -2002,8 +2002,8 @@
const char16_t* id = block.getAttributeStringValue(index, &len);
if (id == NULL) {
fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n",
- manifestPath.string(), block.getLineNumber(),
- String8(block.getElementName(&len)).string());
+ manifestPath.c_str(), block.getLineNumber(),
+ String8(block.getElementName(&len)).c_str());
hasErrors = true;
break;
}
@@ -2038,23 +2038,23 @@
if (begins_with_digit || (e != p && *(e-1) != '.')) {
fprintf(stderr,
"%s:%d: Permission name <%s> is not a valid Java symbol\n",
- manifestPath.string(), block.getLineNumber(), idStr.string());
+ manifestPath.c_str(), block.getLineNumber(), idStr.c_str());
hasErrors = true;
}
syms->addStringSymbol(String8(e), idStr, srcPos);
const char16_t* cmt = block.getComment(&len);
if (cmt != NULL && *cmt != 0) {
- //printf("Comment of %s: %s\n", String8(e).string(),
- // String8(cmt).string());
+ //printf("Comment of %s: %s\n", String8(e).c_str(),
+ // String8(cmt).c_str());
syms->appendComment(String8(e), String16(cmt), srcPos);
}
syms->makeSymbolPublic(String8(e), srcPos);
- } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), uses_permission16.c_str()) == 0) {
if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
"name", packageIdentChars, true) != ATTR_OKAY) {
hasErrors = true;
}
- } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), instrumentation16.c_str()) == 0) {
if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
"name", classIdentChars, true) != ATTR_OKAY) {
hasErrors = true;
@@ -2064,7 +2064,7 @@
packageIdentChars, true) != ATTR_OKAY) {
hasErrors = true;
}
- } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), application16.c_str()) == 0) {
if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
"name", classIdentChars, false) != ATTR_OKAY) {
hasErrors = true;
@@ -2084,7 +2084,7 @@
processIdentChars, false) != ATTR_OKAY) {
hasErrors = true;
}
- } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), provider16.c_str()) == 0) {
if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
"name", classIdentChars, true) != ATTR_OKAY) {
hasErrors = true;
@@ -2104,9 +2104,9 @@
processIdentChars, false) != ATTR_OKAY) {
hasErrors = true;
}
- } else if (strcmp16(block.getElementName(&len), service16.string()) == 0
- || strcmp16(block.getElementName(&len), receiver16.string()) == 0
- || strcmp16(block.getElementName(&len), activity16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), service16.c_str()) == 0
+ || strcmp16(block.getElementName(&len), receiver16.c_str()) == 0
+ || strcmp16(block.getElementName(&len), activity16.c_str()) == 0) {
if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE,
"name", classIdentChars, true) != ATTR_OKAY) {
hasErrors = true;
@@ -2126,14 +2126,14 @@
processIdentChars, false) != ATTR_OKAY) {
hasErrors = true;
}
- } else if (strcmp16(block.getElementName(&len), action16.string()) == 0
- || strcmp16(block.getElementName(&len), category16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), action16.c_str()) == 0
+ || strcmp16(block.getElementName(&len), category16.c_str()) == 0) {
if (validateAttr(manifestPath, finalResTable, block,
RESOURCES_ANDROID_NAMESPACE, "name",
packageIdentChars, true) != ATTR_OKAY) {
hasErrors = true;
}
- } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), data16.c_str()) == 0) {
if (validateAttr(manifestPath, finalResTable, block,
RESOURCES_ANDROID_NAMESPACE, "mimeType",
typeIdentChars, true) != ATTR_OKAY) {
@@ -2144,13 +2144,13 @@
schemeIdentChars, true) != ATTR_OKAY) {
hasErrors = true;
}
- } else if (strcmp16(block.getElementName(&len), feature_group16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), feature_group16.c_str()) == 0) {
int depth = 1;
while ((code=block.next()) != ResXMLTree::END_DOCUMENT
&& code > ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::START_TAG) {
depth++;
- if (strcmp16(block.getElementName(&len), uses_feature16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), uses_feature16.c_str()) == 0) {
ssize_t idx = block.indexOfAttribute(
RESOURCES_ANDROID_NAMESPACE, "required");
if (idx < 0) {
@@ -2162,7 +2162,7 @@
fprintf(stderr, "%s:%d: Tag <uses-feature> can not have "
"android:required=\"false\" when inside a "
"<feature-group> tag.\n",
- manifestPath.string(), block.getLineNumber());
+ manifestPath.c_str(), block.getLineNumber());
hasErrors = true;
}
}
@@ -2222,7 +2222,7 @@
static String8 getSymbolPackage(const String8& symbol, const sp<AaptAssets>& assets, bool pub) {
ssize_t colon = symbol.find(":", 0);
if (colon >= 0) {
- return String8(symbol.string(), colon);
+ return String8(symbol.c_str(), colon);
}
return pub ? assets->getPackage() : assets->getSymbolsPrivatePackage();
}
@@ -2230,7 +2230,7 @@
static String8 getSymbolName(const String8& symbol) {
ssize_t colon = symbol.find(":", 0);
if (colon >= 0) {
- return String8(symbol.string() + colon + 1);
+ return String8(symbol.c_str() + colon + 1);
}
return symbol;
}
@@ -2245,7 +2245,7 @@
asym = asym->getNestedSymbols().valueFor(String8("attr"));
if (asym != NULL) {
//printf("Got attrs symbols! comment %s=%s\n",
- // name.string(), String8(asym->getComment(name)).string());
+ // name.c_str(), String8(asym->getComment(name)).c_str());
if (outTypeComment != NULL) {
*outTypeComment = asym->getTypeComment(name);
}
@@ -2276,8 +2276,8 @@
"%sfor(int i = 0; i < styleable.%s.length; ++i) {\n"
"%sstyleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | (packageId << 24);\n"
"%s}\n",
- indentStr, nclassName.string(),
- getIndentSpace(indent+1), nclassName.string(), nclassName.string(),
+ indentStr, nclassName.c_str(),
+ getIndentSpace(indent+1), nclassName.c_str(), nclassName.c_str(),
indentStr);
}
@@ -2303,8 +2303,8 @@
String8 flat_name(flattenSymbol(sym.name));
fprintf(fp,
"%s%s.%s = (%s.%s & 0x00ffffff) | (packageId << 24);\n",
- getIndentSpace(indent), className.string(), flat_name.string(),
- className.string(), flat_name.string());
+ getIndentSpace(indent), className.c_str(), flat_name.c_str(),
+ className.c_str(), flat_name.c_str());
}
N = symbols->getNestedSymbols().size();
@@ -2365,12 +2365,12 @@
String16 name16(sym.name);
uint32_t typeSpecFlags;
code = assets->getIncludedResources().identifierForName(
- name16.string(), name16.size(),
- attr16.string(), attr16.size(),
- package16.string(), package16.size(), &typeSpecFlags);
+ name16.c_str(), name16.size(),
+ attr16.c_str(), attr16.size(),
+ package16.c_str(), package16.size(), &typeSpecFlags);
if (code == 0) {
fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
- nclassName.string(), sym.name.string());
+ nclassName.c_str(), sym.name.c_str());
hasErrors = true;
}
isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
@@ -2388,9 +2388,9 @@
if (comment.size() > 0) {
String8 cmt(comment);
ann.preprocessComment(cmt);
- fprintf(fp, "%s\n", cmt.string());
+ fprintf(fp, "%s\n", cmt.c_str());
} else {
- fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
+ fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.c_str());
}
bool hasTable = false;
for (a=0; a<NA; a++) {
@@ -2423,7 +2423,7 @@
continue;
}
if (comment.size() > 0) {
- const char16_t* p = comment.string();
+ const char16_t* p = comment.c_str();
while (*p != 0 && *p != '.') {
if (*p == '{') {
while (*p != 0 && *p != '}') {
@@ -2436,14 +2436,14 @@
if (*p == '.') {
p++;
}
- comment = String16(comment.string(), p-comment.string());
+ comment = String16(comment.c_str(), p-comment.c_str());
}
fprintf(fp, "%s <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n",
- indentStr, nclassName.string(),
- flattenSymbol(name8).string(),
- getSymbolPackage(name8, assets, true).string(),
- getSymbolName(name8).string(),
- String8(comment).string());
+ indentStr, nclassName.c_str(),
+ flattenSymbol(name8).c_str(),
+ getSymbolPackage(name8, assets, true).c_str(),
+ getSymbolName(name8).c_str(),
+ String8(comment).c_str());
}
}
if (hasTable) {
@@ -2457,8 +2457,8 @@
continue;
}
fprintf(fp, "%s @see #%s_%s\n",
- indentStr, nclassName.string(),
- flattenSymbol(sym.name).string());
+ indentStr, nclassName.c_str(),
+ flattenSymbol(sym.name).c_str());
}
}
fprintf(fp, "%s */\n", getIndentSpace(indent));
@@ -2468,7 +2468,7 @@
fprintf(fp,
"%spublic static final int[] %s = {\n"
"%s",
- indentStr, nclassName.string(),
+ indentStr, nclassName.c_str(),
getIndentSpace(indent+1));
for (a=0; a<NA; a++) {
@@ -2503,11 +2503,11 @@
uint32_t typeSpecFlags = 0;
String16 name16(sym.name);
assets->getIncludedResources().identifierForName(
- name16.string(), name16.size(),
- attr16.string(), attr16.size(),
- package16.string(), package16.size(), &typeSpecFlags);
- //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
- // String8(attr16).string(), String8(name16).string(), typeSpecFlags);
+ name16.c_str(), name16.size(),
+ attr16.c_str(), attr16.size(),
+ package16.c_str(), package16.size(), &typeSpecFlags);
+ //printf("%s:%s/%s: 0x%08x\n", String8(package16).c_str(),
+ // String8(attr16).c_str(), String8(name16).c_str(), typeSpecFlags);
const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
AnnotationProcessor ann;
@@ -2516,20 +2516,20 @@
String8 cmt(comment);
ann.preprocessComment(cmt);
fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr);
- fprintf(fp, "%s %s\n", indentStr, cmt.string());
+ fprintf(fp, "%s %s\n", indentStr, cmt.c_str());
} else {
fprintf(fp,
"%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
"%s attribute's value can be found in the {@link #%s} array.\n",
indentStr,
- getSymbolPackage(name8, assets, pub).string(),
- getSymbolName(name8).string(),
- indentStr, nclassName.string());
+ getSymbolPackage(name8, assets, pub).c_str(),
+ getSymbolName(name8).c_str(),
+ indentStr, nclassName.c_str());
}
if (typeComment.size() > 0) {
String8 cmt(typeComment);
ann.preprocessComment(cmt);
- fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string());
+ fprintf(fp, "\n\n%s %s\n", indentStr, cmt.c_str());
}
if (comment.size() > 0) {
if (pub) {
@@ -2537,16 +2537,16 @@
"%s <p>This corresponds to the global attribute\n"
"%s resource symbol {@link %s.R.attr#%s}.\n",
indentStr, indentStr,
- getSymbolPackage(name8, assets, true).string(),
- getSymbolName(name8).string());
+ getSymbolPackage(name8, assets, true).c_str(),
+ getSymbolName(name8).c_str());
} else {
fprintf(fp,
"%s <p>This is a private symbol.\n", indentStr);
}
}
fprintf(fp, "%s @attr name %s:%s\n", indentStr,
- getSymbolPackage(name8, assets, pub).string(),
- getSymbolName(name8).string());
+ getSymbolPackage(name8, assets, pub).c_str(),
+ getSymbolName(name8).c_str());
fprintf(fp, "%s*/\n", indentStr);
ann.printAnnotations(fp, indentStr);
@@ -2556,8 +2556,8 @@
fprintf(fp,
id_format,
- indentStr, nclassName.string(),
- flattenSymbol(name8).string(), (int)pos);
+ indentStr, nclassName.c_str(),
+ flattenSymbol(name8).c_str(), (int)pos);
}
}
}
@@ -2598,12 +2598,12 @@
String16 name16(sym.name);
uint32_t typeSpecFlags;
code = assets->getIncludedResources().identifierForName(
- name16.string(), name16.size(),
- attr16.string(), attr16.size(),
- package16.string(), package16.size(), &typeSpecFlags);
+ name16.c_str(), name16.size(),
+ attr16.c_str(), attr16.size(),
+ package16.c_str(), package16.size(), &typeSpecFlags);
if (code == 0) {
fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
- nclassName.string(), sym.name.string());
+ nclassName.c_str(), sym.name.c_str());
hasErrors = true;
}
isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
@@ -2615,7 +2615,7 @@
NA = idents.size();
- fprintf(fp, "int[] styleable %s {", nclassName.string());
+ fprintf(fp, "int[] styleable %s {", nclassName.c_str());
for (a=0; a<NA; a++) {
if (a != 0) {
@@ -2645,17 +2645,17 @@
uint32_t typeSpecFlags = 0;
String16 name16(sym.name);
assets->getIncludedResources().identifierForName(
- name16.string(), name16.size(),
- attr16.string(), attr16.size(),
- package16.string(), package16.size(), &typeSpecFlags);
- //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
- // String8(attr16).string(), String8(name16).string(), typeSpecFlags);
+ name16.c_str(), name16.size(),
+ attr16.c_str(), attr16.size(),
+ package16.c_str(), package16.size(), &typeSpecFlags);
+ //printf("%s:%s/%s: 0x%08x\n", String8(package16).c_str(),
+ // String8(attr16).c_str(), String8(name16).c_str(), typeSpecFlags);
//const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
fprintf(fp,
"int styleable %s_%s %d\n",
- nclassName.string(),
- flattenSymbol(name8).string(), (int)pos);
+ nclassName.c_str(),
+ flattenSymbol(name8).c_str(), (int)pos);
}
}
}
@@ -2670,7 +2670,7 @@
{
fprintf(fp, "%spublic %sfinal class %s {\n",
getIndentSpace(indent),
- indent != 0 ? "static " : "", className.string());
+ indent != 0 ? "static " : "", className.c_str());
indent++;
size_t i;
@@ -2699,7 +2699,7 @@
ann.preprocessComment(cmt);
fprintf(fp,
"%s/** %s\n",
- getIndentSpace(indent), cmt.string());
+ getIndentSpace(indent), cmt.c_str());
}
String16 typeComment(sym.typeComment);
if (typeComment.size() > 0) {
@@ -2708,10 +2708,10 @@
if (!haveComment) {
haveComment = true;
fprintf(fp,
- "%s/** %s\n", getIndentSpace(indent), cmt.string());
+ "%s/** %s\n", getIndentSpace(indent), cmt.c_str());
} else {
fprintf(fp,
- "%s %s\n", getIndentSpace(indent), cmt.string());
+ "%s %s\n", getIndentSpace(indent), cmt.c_str());
}
}
if (haveComment) {
@@ -2720,7 +2720,7 @@
ann.printAnnotations(fp, getIndentSpace(indent));
fprintf(fp, id_format,
getIndentSpace(indent),
- flattenSymbol(name8).string(), (int)sym.int32Val);
+ flattenSymbol(name8).c_str(), (int)sym.int32Val);
}
for (i=0; i<N; i++) {
@@ -2740,13 +2740,13 @@
fprintf(fp,
"%s/** %s\n"
"%s */\n",
- getIndentSpace(indent), cmt.string(),
+ getIndentSpace(indent), cmt.c_str(),
getIndentSpace(indent));
}
ann.printAnnotations(fp, getIndentSpace(indent));
fprintf(fp, "%spublic static final String %s=\"%s\";\n",
getIndentSpace(indent),
- flattenSymbol(name8).string(), sym.stringVal.string());
+ flattenSymbol(name8).c_str(), sym.stringVal.c_str());
}
sp<AaptSymbols> styleableSymbols;
@@ -2805,8 +2805,8 @@
String8 name8(sym.name);
fprintf(fp, "int %s %s 0x%08x\n",
- className.string(),
- flattenSymbol(name8).string(), (int)sym.int32Val);
+ className.c_str(),
+ flattenSymbol(name8).c_str(), (int)sym.int32Val);
}
N = symbols->getNestedSymbols().size();
@@ -2844,7 +2844,7 @@
if (bundle->getMakePackageDirs()) {
const String8& pkg(package);
- const char* last = pkg.string();
+ const char* last = pkg.c_str();
const char* s = last-1;
do {
s++;
@@ -2852,9 +2852,9 @@
String8 part(last, s-last);
dest.appendPath(part);
#ifdef _WIN32
- _mkdir(dest.string());
+ _mkdir(dest.c_str());
#else
- mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
+ mkdir(dest.c_str(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
#endif
last = s+1;
}
@@ -2862,14 +2862,14 @@
}
dest.appendPath(className);
dest.append(".java");
- FILE* fp = fopen(dest.string(), "w+");
+ FILE* fp = fopen(dest.c_str(), "w+");
if (fp == NULL) {
fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
- dest.string(), strerror(errno));
+ dest.c_str(), strerror(errno));
return UNKNOWN_ERROR;
}
if (bundle->getVerbose()) {
- printf(" Writing symbols for class %s.\n", className.string());
+ printf(" Writing symbols for class %s.\n", className.c_str());
}
fprintf(fp,
@@ -2880,7 +2880,7 @@
" * should not be modified by hand.\n"
" */\n"
"\n"
- "package %s;\n\n", package.string());
+ "package %s;\n\n", package.c_str());
status_t err = writeSymbolClass(fp, assets, includePrivate, symbols,
className, 0, bundle->getNonConstantId(), emitCallback);
@@ -2894,14 +2894,14 @@
textDest.appendPath(className);
textDest.append(".txt");
- FILE* fp = fopen(textDest.string(), "w+");
+ FILE* fp = fopen(textDest.c_str(), "w+");
if (fp == NULL) {
fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n",
- textDest.string(), strerror(errno));
+ textDest.c_str(), strerror(errno));
return UNKNOWN_ERROR;
}
if (bundle->getVerbose()) {
- printf(" Writing text symbols for class %s.\n", className.string());
+ printf(" Writing text symbols for class %s.\n", className.c_str());
}
status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols,
@@ -2919,8 +2919,8 @@
String8 dependencyFile(bundle->getRClassDir());
dependencyFile.appendPath("R.java.d");
- FILE *fp = fopen(dependencyFile.string(), "a");
- fprintf(fp,"%s \\\n", dest.string());
+ FILE *fp = fopen(dependencyFile.c_str(), "a");
+ fprintf(fp,"%s \\\n", dest.c_str());
fclose(fp);
}
}
@@ -2956,7 +2956,7 @@
// asdf --> package.asdf
// .asdf .a.b --> package.asdf package.a.b
// asdf.adsf --> asdf.asdf
- const char* p = className.string();
+ const char* p = className.c_str();
const char* q = strchr(p, '.');
if (p == q) {
className = pkg;
@@ -3023,7 +3023,7 @@
if (assGroup->getFiles().size() != 1) {
fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
- assGroup->getFiles().valueAt(0)->getPrintableSource().string());
+ assGroup->getFiles().valueAt(0)->getPrintableSource().c_str());
}
assFile = assGroup->getFiles().valueAt(0);
@@ -3048,7 +3048,7 @@
}
depth++;
String8 tag(tree.getElementName(&len));
- // printf("Depth %d tag %s\n", depth, tag.string());
+ // printf("Depth %d tag %s\n", depth, tag.c_str());
bool keepTag = false;
if (depth == 1) {
if (tag != "manifest") {
@@ -3065,7 +3065,7 @@
"http://schemas.android.com/apk/res/android",
"backupAgent", &error);
if (agent.length() > 0) {
- addProguardKeepRule(keep, agent, pkg.string(),
+ addProguardKeepRule(keep, agent, pkg.c_str(),
assFile->getPrintableSource(), tree.getLineNumber());
}
@@ -3073,7 +3073,7 @@
defaultProcess = AaptXml::getAttribute(tree,
"http://schemas.android.com/apk/res/android", "process", &error);
if (error != "") {
- fprintf(stderr, "ERROR: %s\n", error.string());
+ fprintf(stderr, "ERROR: %s\n", error.c_str());
return -1;
}
}
@@ -3089,7 +3089,7 @@
String8 componentProcess = AaptXml::getAttribute(tree,
"http://schemas.android.com/apk/res/android", "process", &error);
if (error != "") {
- fprintf(stderr, "ERROR: %s\n", error.string());
+ fprintf(stderr, "ERROR: %s\n", error.c_str());
return -1;
}
@@ -3103,14 +3103,14 @@
String8 name = AaptXml::getAttribute(tree,
"http://schemas.android.com/apk/res/android", "name", &error);
if (error != "") {
- fprintf(stderr, "ERROR: %s\n", error.string());
+ fprintf(stderr, "ERROR: %s\n", error.c_str());
return -1;
}
keepTag = name.length() > 0;
if (keepTag) {
- addProguardKeepRule(keep, name, pkg.string(),
+ addProguardKeepRule(keep, name, pkg.c_str(),
assFile->getPrintableSource(), tree.getLineNumber());
}
}
@@ -3170,7 +3170,7 @@
String8 tag(tree.getElementName(&len));
// If there is no '.', we'll assume that it's one of the built in names.
- if (strchr(tag.string(), '.')) {
+ if (strchr(tag.c_str(), '.')) {
addProguardKeepRule(keep, tag, NULL,
layoutFile->getPrintableSource(), tree.getLineNumber());
} else if (tagAttrPairs != NULL) {
@@ -3183,8 +3183,8 @@
ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr);
if (attrIndex < 0) {
// fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n",
- // layoutFile->getPrintableSource().string(), tree.getLineNumber(),
- // tag.string(), nsAttr.ns, nsAttr.attr);
+ // layoutFile->getPrintableSource().c_str(), tree.getLineNumber(),
+ // tag.c_str(), nsAttr.ns, nsAttr.attr);
} else {
size_t len;
addProguardKeepRule(keep,
@@ -3242,7 +3242,7 @@
// tag:attribute pairs that should be checked in transition files.
KeyedVector<String8, Vector<NamespaceAttributePair> > kTransitionTagAttrPairs;
- addTagAttrPair(&kTransitionTagAttrPairs, kTransition.string(), NULL, kClass);
+ addTagAttrPair(&kTransitionTagAttrPairs, kTransition.c_str(), NULL, kClass);
addTagAttrPair(&kTransitionTagAttrPairs, "pathMotion", NULL, kClass);
const Vector<sp<AaptDir> >& dirs = assets->resDirs();
@@ -3252,16 +3252,16 @@
const String8& dirName = d->getLeaf();
Vector<String8> startTags;
const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL;
- if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
+ if ((dirName == String8("layout")) || (strncmp(dirName.c_str(), "layout-", 7) == 0)) {
tagAttrPairs = &kLayoutTagAttrPairs;
- } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
+ } else if ((dirName == String8("xml")) || (strncmp(dirName.c_str(), "xml-", 4) == 0)) {
startTags.add(String8("PreferenceScreen"));
startTags.add(String8("preference-headers"));
tagAttrPairs = &kXmlTagAttrPairs;
- } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
+ } else if ((dirName == String8("menu")) || (strncmp(dirName.c_str(), "menu-", 5) == 0)) {
startTags.add(String8("menu"));
tagAttrPairs = NULL;
- } else if (dirName == kTransition || (strncmp(dirName.string(), kTransitionPrefix.string(),
+ } else if (dirName == kTransition || (strncmp(dirName.c_str(), kTransitionPrefix.c_str(),
kTransitionPrefix.size()) == 0)) {
tagAttrPairs = &kTransitionTagAttrPairs;
} else {
@@ -3307,9 +3307,9 @@
const SortedVector<String8>& locations = rules.valueAt(i);
const size_t M = locations.size();
for (size_t j=0; j<M; j++) {
- fprintf(fp, "# %s\n", locations.itemAt(j).string());
+ fprintf(fp, "# %s\n", locations.itemAt(j).c_str());
}
- fprintf(fp, "%s\n\n", rules.keyAt(i).string());
+ fprintf(fp, "%s\n\n", rules.keyAt(i).c_str());
}
fclose(fp);
@@ -3366,7 +3366,7 @@
status_t deps = -1;
for (size_t file_i = 0; file_i < files->size(); ++file_i) {
// Add the full file path to the dependency file
- fprintf(fp, "%s \\\n", files->itemAt(file_i).string());
+ fprintf(fp, "%s \\\n", files->itemAt(file_i).c_str());
deps++;
}
return deps;
diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp
index ed06f60..cc8dce7e 100644
--- a/tools/aapt/ResourceFilter.cpp
+++ b/tools/aapt/ResourceFilter.cpp
@@ -32,7 +32,7 @@
// only specify locale in the standard 'en_US' format.
val.writeTo(&entry.first);
} else if (!AaptConfig::parse(part, &entry.first)) {
- fprintf(stderr, "Invalid configuration: %s\n", part.string());
+ fprintf(stderr, "Invalid configuration: %s\n", part.c_str());
return UNKNOWN_ERROR;
}
@@ -43,7 +43,7 @@
// Ignore any densities. Those are best handled in --preferred-density
if ((entry.second & ResTable_config::CONFIG_DENSITY) != 0) {
- fprintf(stderr, "warning: ignoring flag -c %s. Use --preferred-density instead.\n", entry.first.toString().string());
+ fprintf(stderr, "warning: ignoring flag -c %s. Use --preferred-density instead.\n", entry.first.toString().c_str());
entry.first.density = 0;
entry.second &= ~ResTable_config::CONFIG_DENSITY;
}
@@ -148,7 +148,7 @@
mConfigs.clear();
for (size_t i = 0; i < configStrs.size(); i++) {
if (!AaptConfig::parse(configStrs[i], &config)) {
- fprintf(stderr, "Invalid configuration: %s\n", configStrs[i].string());
+ fprintf(stderr, "Invalid configuration: %s\n", configStrs[i].c_str());
return UNKNOWN_ERROR;
}
mConfigs.insert(config);
diff --git a/tools/aapt/ResourceIdCache.cpp b/tools/aapt/ResourceIdCache.cpp
index 8835fb0..1c7788d 100644
--- a/tools/aapt/ResourceIdCache.cpp
+++ b/tools/aapt/ResourceIdCache.cpp
@@ -37,7 +37,7 @@
static uint32_t hash(const android::String16& hashableString) {
uint32_t hash = 5381;
- const char16_t* str = hashableString.string();
+ const char16_t* str = hashableString.c_str();
while (int c = *str++) hash = hashround(hash, c);
return hash;
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 4e597fb..2344007 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -361,10 +361,10 @@
ssize_t typeIdx = block.indexOfAttribute(NULL, "format");
if (typeIdx >= 0) {
String16 typeStr = String16(block.getAttributeStringValue(typeIdx, &len));
- attr.type = parse_flags(typeStr.string(), typeStr.size(), gFormatFlags);
+ attr.type = parse_flags(typeStr.c_str(), typeStr.size(), gFormatFlags);
if (attr.type == 0) {
attr.sourcePos.error("Tag <attr> 'format' attribute value \"%s\" not valid\n",
- String8(typeStr).string());
+ String8(typeStr).c_str());
attr.hasErrors = true;
}
attr.createIfNeeded(outTable);
@@ -374,14 +374,14 @@
attr.createIfNeeded(outTable);
}
- //printf("Attribute %s: type=0x%08x\n", String8(attr.ident).string(), attr.type);
+ //printf("Attribute %s: type=0x%08x\n", String8(attr.ident).c_str(), attr.type);
ssize_t minIdx = block.indexOfAttribute(NULL, "min");
if (minIdx >= 0) {
String16 val = String16(block.getAttributeStringValue(minIdx, &len));
- if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
+ if (!ResTable::stringToInt(val.c_str(), val.size(), NULL)) {
attr.sourcePos.error("Tag <attr> 'min' attribute must be a number, not \"%s\"\n",
- String8(val).string());
+ String8(val).c_str());
attr.hasErrors = true;
}
attr.createIfNeeded(outTable);
@@ -397,9 +397,9 @@
ssize_t maxIdx = block.indexOfAttribute(NULL, "max");
if (maxIdx >= 0) {
String16 val = String16(block.getAttributeStringValue(maxIdx, &len));
- if (!ResTable::stringToInt(val.string(), val.size(), NULL)) {
+ if (!ResTable::stringToInt(val.c_str(), val.size(), NULL)) {
attr.sourcePos.error("Tag <attr> 'max' attribute must be a number, not \"%s\"\n",
- String8(val).string());
+ String8(val).c_str());
attr.hasErrors = true;
}
attr.createIfNeeded(outTable);
@@ -422,7 +422,7 @@
uint32_t l10n_required = parse_flags(str, len, l10nRequiredFlags, &error);
if (error) {
attr.sourcePos.error("Tag <attr> 'localization' attribute value \"%s\" not valid\n",
- String8(str).string());
+ String8(str).c_str());
attr.hasErrors = true;
}
attr.createIfNeeded(outTable);
@@ -442,14 +442,14 @@
while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::START_TAG) {
uint32_t localType = 0;
- if (strcmp16(block.getElementName(&len), enum16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), enum16.c_str()) == 0) {
localType = ResTable_map::TYPE_ENUM;
- } else if (strcmp16(block.getElementName(&len), flag16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), flag16.c_str()) == 0) {
localType = ResTable_map::TYPE_FLAGS;
} else {
SourcePos(in->getPrintableSource(), block.getLineNumber())
.error("Tag <%s> can not appear inside <attr>, only <enum> or <flag>\n",
- String8(block.getElementName(&len)).string());
+ String8(block.getElementName(&len)).c_str());
return UNKNOWN_ERROR;
}
@@ -505,11 +505,11 @@
.error("A 'value' attribute is required for <enum> or <flag>\n");
attr.hasErrors = true;
}
- if (!attr.hasErrors && !ResTable::stringToInt(value.string(), value.size(), NULL)) {
+ if (!attr.hasErrors && !ResTable::stringToInt(value.c_str(), value.size(), NULL)) {
SourcePos(in->getPrintableSource(), block.getLineNumber())
.error("Tag <enum> or <flag> 'value' attribute must be a number,"
" not \"%s\"\n",
- String8(value).string());
+ String8(value).c_str());
attr.hasErrors = true;
}
@@ -546,21 +546,21 @@
}
}
} else if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), attr16.c_str()) == 0) {
break;
}
if ((attr.type&ResTable_map::TYPE_ENUM) != 0) {
- if (strcmp16(block.getElementName(&len), enum16.string()) != 0) {
+ if (strcmp16(block.getElementName(&len), enum16.c_str()) != 0) {
SourcePos(in->getPrintableSource(), block.getLineNumber())
.error("Found tag </%s> where </enum> is expected\n",
- String8(block.getElementName(&len)).string());
+ String8(block.getElementName(&len)).c_str());
return UNKNOWN_ERROR;
}
} else {
- if (strcmp16(block.getElementName(&len), flag16.string()) != 0) {
+ if (strcmp16(block.getElementName(&len), flag16.c_str()) != 0) {
SourcePos(in->getPrintableSource(), block.getLineNumber())
.error("Found tag </%s> where </flag> is expected\n",
- String8(block.getElementName(&len)).string());
+ String8(block.getElementName(&len)).c_str());
return UNKNOWN_ERROR;
}
}
@@ -606,7 +606,7 @@
String16 str;
Vector<StringPool::entry_style_span> spans;
- err = parseStyledString(bundle, in->getPrintableSource().string(),
+ err = parseStyledString(bundle, in->getPrintableSource().c_str(),
block, item16, &str, &spans, isFormatted,
pseudolocalize);
if (err != NO_ERROR) {
@@ -619,10 +619,10 @@
config.language[0], config.language[1],
config.country[0], config.country[1],
config.orientation, config.density,
- String8(parentIdent).string(),
- String8(ident).string(),
- String8(itemIdent).string(),
- String8(str).string());
+ String8(parentIdent).c_str(),
+ String8(ident).c_str(),
+ String8(itemIdent).c_str(),
+ String8(str).c_str());
}
err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()),
@@ -636,8 +636,8 @@
* haystack, false otherwise.
*/
bool isInProductList(const String16& needle, const String16& haystack) {
- const char16_t *needle2 = needle.string();
- const char16_t *haystack2 = haystack.string();
+ const char16_t *needle2 = needle.c_str();
+ const char16_t *haystack2 = haystack.c_str();
size_t needlesize = needle.size();
while (*haystack2 != '\0') {
@@ -703,7 +703,7 @@
String16 str;
Vector<StringPool::entry_style_span> spans;
- err = parseStyledString(bundle, in->getPrintableSource().string(), block,
+ err = parseStyledString(bundle, in->getPrintableSource().c_str(), block,
curTag, &str, curIsStyled ? &spans : NULL,
isFormatted, pseudolocalize);
@@ -730,7 +730,7 @@
*/
if (bundleProduct[0] == '\0') {
- if (strcmp16(String16("default").string(), product.string()) != 0) {
+ if (strcmp16(String16("default").c_str(), product.c_str()) != 0) {
/*
* This string has a product other than 'default'. Do not add it,
* but record it so that if we do not see the same string with
@@ -750,7 +750,7 @@
if (isInProductList(product, String16(bundleProduct))) {
;
- } else if (strcmp16(String16("default").string(), product.string()) == 0 &&
+ } else if (strcmp16(String16("default").c_str(), product.c_str()) == 0 &&
!outTable->hasBagOrEntry(myPackage, curType, ident, config)) {
;
} else {
@@ -764,7 +764,7 @@
config.language[0], config.language[1],
config.country[0], config.country[1],
config.orientation, config.density,
- String8(ident).string(), String8(str).string());
+ String8(ident).c_str(), String8(str).c_str());
}
err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()),
@@ -847,7 +847,7 @@
bool hasErrors = false;
bool fileIsTranslatable = true;
- if (strstr(in->getPrintableSource().string(), "donottranslate") != NULL) {
+ if (strstr(in->getPrintableSource().c_str(), "donottranslate") != NULL) {
fileIsTranslatable = false;
}
@@ -869,9 +869,9 @@
"No start tag found\n");
return UNKNOWN_ERROR;
}
- if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
+ if (strcmp16(block.getElementName(&len), resources16.c_str()) != 0) {
SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
- "Invalid start tag %s\n", String8(block.getElementName(&len)).string());
+ "Invalid start tag %s\n", String8(block.getElementName(&len)).c_str());
return UNKNOWN_ERROR;
}
@@ -900,7 +900,7 @@
SourcePos(in->getPrintableSource(), 0).warning(
"Resource file %s is skipped as pseudolocalization"
" was done automatically.",
- in->getPrintableSource().string());
+ in->getPrintableSource().c_str());
return NO_ERROR;
}
@@ -917,29 +917,29 @@
bool curIsFormatted = fileIsTranslatable;
bool localHasErrors = false;
- if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), skip16.c_str()) == 0) {
while ((code=block.next()) != ResXMLTree::END_DOCUMENT
&& code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), skip16.c_str()) == 0) {
break;
}
}
}
continue;
- } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), eat_comment16.c_str()) == 0) {
while ((code=block.next()) != ResXMLTree::END_DOCUMENT
&& code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), eat_comment16.c_str()) == 0) {
break;
}
}
}
continue;
- } else if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), public16.c_str()) == 0) {
SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
String16 type;
@@ -965,7 +965,7 @@
Res_value identValue;
if (!ResTable::stringToInt(identStr, len, &identValue)) {
srcPos.error("Given 'id' attribute is not an integer: %s\n",
- String8(block.getAttributeStringValue(identIdx, &len)).string());
+ String8(block.getAttributeStringValue(identIdx, &len)).c_str());
hasErrors = localHasErrors = true;
} else {
ident = identValue.data;
@@ -1004,14 +1004,14 @@
while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), public16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), public16.c_str()) == 0) {
break;
}
}
}
continue;
- } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), public_padding16.c_str()) == 0) {
SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
String16 type;
@@ -1037,7 +1037,7 @@
Res_value startValue;
if (!ResTable::stringToInt(startStr, len, &startValue)) {
srcPos.error("Given 'start' attribute is not an integer: %s\n",
- String8(block.getAttributeStringValue(startIdx, &len)).string());
+ String8(block.getAttributeStringValue(startIdx, &len)).c_str());
hasErrors = localHasErrors = true;
} else {
start = startValue.data;
@@ -1057,7 +1057,7 @@
Res_value endValue;
if (!ResTable::stringToInt(endStr, len, &endValue)) {
srcPos.error("Given 'end' attribute is not an integer: %s\n",
- String8(block.getAttributeStringValue(endIdx, &len)).string());
+ String8(block.getAttributeStringValue(endIdx, &len)).c_str());
hasErrors = localHasErrors = true;
} else {
end = endValue.data;
@@ -1114,14 +1114,14 @@
while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), public_padding16.c_str()) == 0) {
break;
}
}
}
continue;
- } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), private_symbols16.c_str()) == 0) {
String16 pkg;
ssize_t pkgIdx = block.indexOfAttribute(NULL, "package");
if (pkgIdx < 0) {
@@ -1144,14 +1144,14 @@
while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), private_symbols16.c_str()) == 0) {
break;
}
}
}
continue;
- } else if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), java_symbol16.c_str()) == 0) {
SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
String16 type;
@@ -1186,7 +1186,7 @@
while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), java_symbol16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), java_symbol16.c_str()) == 0) {
break;
}
}
@@ -1194,7 +1194,7 @@
continue;
- } else if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), add_resource16.c_str()) == 0) {
SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
String16 typeName;
@@ -1217,14 +1217,14 @@
while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), add_resource16.c_str()) == 0) {
break;
}
}
}
continue;
- } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), declare_styleable16.c_str()) == 0) {
SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
String16 ident;
@@ -1258,30 +1258,30 @@
while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::START_TAG) {
- if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), skip16.c_str()) == 0) {
while ((code=block.next()) != ResXMLTree::END_DOCUMENT
&& code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), skip16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), skip16.c_str()) == 0) {
break;
}
}
}
continue;
- } else if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), eat_comment16.c_str()) == 0) {
while ((code=block.next()) != ResXMLTree::END_DOCUMENT
&& code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), eat_comment16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), eat_comment16.c_str()) == 0) {
break;
}
}
}
continue;
- } else if (strcmp16(block.getElementName(&len), attr16.string()) != 0) {
+ } else if (strcmp16(block.getElementName(&len), attr16.c_str()) != 0) {
SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
"Tag <%s> can not appear inside <declare-styleable>, only <attr>\n",
- String8(block.getElementName(&len)).string());
+ String8(block.getElementName(&len)).c_str());
return UNKNOWN_ERROR;
}
@@ -1297,30 +1297,30 @@
SourcePos srcPos(String8(in->getPrintableSource()), block.getLineNumber());
symbols->addSymbol(String8(itemIdent), 0, srcPos);
symbols->appendComment(String8(itemIdent), comment, srcPos);
- //printf("Attribute %s comment: %s\n", String8(itemIdent).string(),
- // String8(comment).string());
+ //printf("Attribute %s comment: %s\n", String8(itemIdent).c_str(),
+ // String8(comment).c_str());
}
} else if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
+ if (strcmp16(block.getElementName(&len), declare_styleable16.c_str()) == 0) {
break;
}
SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
"Found tag </%s> where </attr> is expected\n",
- String8(block.getElementName(&len)).string());
+ String8(block.getElementName(&len)).c_str());
return UNKNOWN_ERROR;
}
}
continue;
- } else if (strcmp16(block.getElementName(&len), attr16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), attr16.c_str()) == 0) {
err = compileAttribute(in, block, myPackage, outTable, NULL);
if (err != NO_ERROR) {
hasErrors = true;
}
continue;
- } else if (strcmp16(block.getElementName(&len), item16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), item16.c_str()) == 0) {
curTag = &item16;
ssize_t attri = block.indexOfAttribute(NULL, "type");
if (attri >= 0) {
@@ -1333,12 +1333,12 @@
if (formatIdx >= 0) {
String16 formatStr = String16(block.getAttributeStringValue(
formatIdx, &len));
- curFormat = parse_flags(formatStr.string(), formatStr.size(),
+ curFormat = parse_flags(formatStr.c_str(), formatStr.size(),
gFormatFlags);
if (curFormat == 0) {
SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
"Tag <item> 'format' attribute value \"%s\" not valid\n",
- String8(formatStr).string());
+ String8(formatStr).c_str());
hasErrors = localHasErrors = true;
}
}
@@ -1348,7 +1348,7 @@
hasErrors = localHasErrors = true;
}
curIsStyled = true;
- } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), string16.c_str()) == 0) {
// Note the existence and locale of every string we process
char rawLocale[RESTABLE_MAX_LOCALE_LEN];
curParams.getBcp47Locale(rawLocale);
@@ -1361,11 +1361,11 @@
for (size_t i = 0; i < n; i++) {
size_t length;
const char16_t* attr = block.getAttributeName(i, &length);
- if (strcmp16(attr, name16.string()) == 0) {
+ if (strcmp16(attr, name16.c_str()) == 0) {
name.setTo(block.getAttributeStringValue(i, &length));
- } else if (strcmp16(attr, translatable16.string()) == 0) {
+ } else if (strcmp16(attr, translatable16.c_str()) == 0) {
translatable.setTo(block.getAttributeStringValue(i, &length));
- } else if (strcmp16(attr, formatted16.string()) == 0) {
+ } else if (strcmp16(attr, formatted16.c_str()) == 0) {
formatted.setTo(block.getAttributeStringValue(i, &length));
}
}
@@ -1380,8 +1380,8 @@
if (locale.size() > 0) {
SourcePos(in->getPrintableSource(), block.getLineNumber()).warning(
"string '%s' marked untranslatable but exists in locale '%s'\n",
- String8(name).string(),
- locale.string());
+ String8(name).c_str(),
+ locale.c_str());
// hasErrors = localHasErrors = true;
} else {
// Intentionally empty block:
@@ -1407,31 +1407,31 @@
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
curIsStyled = true;
curIsPseudolocalizable = fileIsTranslatable && (translatable != false16);
- } else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), drawable16.c_str()) == 0) {
curTag = &drawable16;
curType = drawable16;
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
- } else if (strcmp16(block.getElementName(&len), color16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), color16.c_str()) == 0) {
curTag = &color16;
curType = color16;
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
- } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), bool16.c_str()) == 0) {
curTag = &bool16;
curType = bool16;
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN;
- } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), integer16.c_str()) == 0) {
curTag = &integer16;
curType = integer16;
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
- } else if (strcmp16(block.getElementName(&len), dimen16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), dimen16.c_str()) == 0) {
curTag = &dimen16;
curType = dimen16;
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION;
- } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), fraction16.c_str()) == 0) {
curTag = &fraction16;
curType = fraction16;
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION;
- } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), bag16.c_str()) == 0) {
curTag = &bag16;
curIsBag = true;
ssize_t attri = block.indexOfAttribute(NULL, "type");
@@ -1442,16 +1442,16 @@
"A 'type' attribute is required for <bag>\n");
hasErrors = localHasErrors = true;
}
- } else if (strcmp16(block.getElementName(&len), style16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), style16.c_str()) == 0) {
curTag = &style16;
curType = style16;
curIsBag = true;
- } else if (strcmp16(block.getElementName(&len), plurals16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), plurals16.c_str()) == 0) {
curTag = &plurals16;
curType = plurals16;
curIsBag = true;
curIsPseudolocalizable = fileIsTranslatable;
- } else if (strcmp16(block.getElementName(&len), array16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), array16.c_str()) == 0) {
curTag = &array16;
curType = array16;
curIsBag = true;
@@ -1460,16 +1460,16 @@
if (formatIdx >= 0) {
String16 formatStr = String16(block.getAttributeStringValue(
formatIdx, &len));
- curFormat = parse_flags(formatStr.string(), formatStr.size(),
+ curFormat = parse_flags(formatStr.c_str(), formatStr.size(),
gFormatFlags);
if (curFormat == 0) {
SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
"Tag <array> 'format' attribute value \"%s\" not valid\n",
- String8(formatStr).string());
+ String8(formatStr).c_str());
hasErrors = localHasErrors = true;
}
}
- } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), string_array16.c_str()) == 0) {
// Check whether these strings need valid formats.
// (simplified form of what string16 does above)
bool isTranslatable = false;
@@ -1480,14 +1480,14 @@
for (size_t i = 0; i < n; i++) {
size_t length;
const char16_t* attr = block.getAttributeName(i, &length);
- if (strcmp16(attr, formatted16.string()) == 0) {
+ if (strcmp16(attr, formatted16.c_str()) == 0) {
const char16_t* value = block.getAttributeStringValue(i, &length);
- if (strcmp16(value, false16.string()) == 0) {
+ if (strcmp16(value, false16.c_str()) == 0) {
curIsFormatted = false;
}
- } else if (strcmp16(attr, translatable16.string()) == 0) {
+ } else if (strcmp16(attr, translatable16.c_str()) == 0) {
const char16_t* value = block.getAttributeStringValue(i, &length);
- if (strcmp16(value, false16.string()) == 0) {
+ if (strcmp16(value, false16.c_str()) == 0) {
isTranslatable = false;
}
}
@@ -1499,7 +1499,7 @@
curIsBag = true;
curIsBagReplaceOnOverwrite = true;
curIsPseudolocalizable = isTranslatable && fileIsTranslatable;
- } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
+ } else if (strcmp16(block.getElementName(&len), integer_array16.c_str()) == 0) {
curTag = &integer_array16;
curType = array16;
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
@@ -1508,7 +1508,7 @@
} else {
SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
"Found tag %s where item is expected\n",
- String8(block.getElementName(&len)).string());
+ String8(block.getElementName(&len)).c_str());
return UNKNOWN_ERROR;
}
@@ -1519,7 +1519,7 @@
} else {
SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
"A 'name' attribute is required for <%s>\n",
- String8(*curTag).string());
+ String8(*curTag).c_str());
hasErrors = localHasErrors = true;
}
@@ -1560,11 +1560,11 @@
&& code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::START_TAG) {
- if (strcmp16(block.getElementName(&len), item16.string()) != 0) {
+ if (strcmp16(block.getElementName(&len), item16.c_str()) != 0) {
SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
"Tag <%s> can not appear inside <%s>, only <item>\n",
- String8(block.getElementName(&len)).string(),
- String8(*curTag).string());
+ String8(block.getElementName(&len)).c_str(),
+ String8(*curTag).c_str());
return UNKNOWN_ERROR;
}
@@ -1647,11 +1647,11 @@
hasErrors = localHasErrors = true;
}
} else if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), curTag->string()) != 0) {
+ if (strcmp16(block.getElementName(&len), curTag->c_str()) != 0) {
SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
"Found tag </%s> where </%s> is expected\n",
- String8(block.getElementName(&len)).string(),
- String8(*curTag).string());
+ String8(block.getElementName(&len)).c_str(),
+ String8(*curTag).c_str());
return UNKNOWN_ERROR;
}
break;
@@ -1700,9 +1700,9 @@
#if 0
if (comment.size() > 0) {
- printf("Comment for @%s:%s/%s: %s\n", String8(myPackage).string(),
- String8(curType).string(), String8(ident).string(),
- String8(comment).string());
+ printf("Comment for @%s:%s/%s: %s\n", String8(myPackage).c_str(),
+ String8(curType).c_str(), String8(ident).c_str(),
+ String8(comment).c_str());
}
#endif
if (!localHasErrors) {
@@ -1710,9 +1710,9 @@
}
}
else if (code == ResXMLTree::END_TAG) {
- if (strcmp16(block.getElementName(&len), resources16.string()) != 0) {
+ if (strcmp16(block.getElementName(&len), resources16.c_str()) != 0) {
SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
- "Unexpected end tag %s\n", String8(block.getElementName(&len)).string());
+ "Unexpected end tag %s\n", String8(block.getElementName(&len)).c_str());
return UNKNOWN_ERROR;
}
}
@@ -1724,7 +1724,7 @@
}
SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
"Found text \"%s\" where item tag is expected\n",
- String8(block.getText(&len)).string());
+ String8(block.getText(&len)).c_str());
return UNKNOWN_ERROR;
}
}
@@ -1740,13 +1740,13 @@
const char* bundleProduct =
(bundle->getProduct() == NULL) ? "" : bundle->getProduct();
fprintf(stderr, "In resource file %s: %s\n",
- in->getPrintableSource().string(),
- curParams.toString().string());
+ in->getPrintableSource().c_str(),
+ curParams.toString().c_str());
fprintf(stderr, "\t%s '%s' does not match product %s.\n"
"\tYou may have forgotten to include a 'default' product variant"
" of the resource.\n",
- String8(p.type).string(), String8(p.ident).string(),
+ String8(p.type).c_str(), String8(p.ident).c_str(),
bundleProduct[0] == 0 ? "default" : bundleProduct);
return UNKNOWN_ERROR;
}
@@ -1816,7 +1816,7 @@
AssetManager featureAssetManager;
if (!featureAssetManager.addAssetPath(featureAfter, NULL)) {
fprintf(stderr, "ERROR: Feature package '%s' not found.\n",
- featureAfter.string());
+ featureAfter.c_str());
return UNKNOWN_ERROR;
}
@@ -1835,13 +1835,13 @@
const uint32_t ident)
{
uint32_t rid = mAssets->getIncludedResources()
- .identifierForName(name.string(), name.size(),
- type.string(), type.size(),
- package.string(), package.size());
+ .identifierForName(name.c_str(), name.size(),
+ type.c_str(), type.size(),
+ package.c_str(), package.size());
if (rid != 0) {
sourcePos.error("Error declaring public resource %s/%s for included package %s\n",
- String8(type).string(), String8(name).string(),
- String8(package).string());
+ String8(type).c_str(), String8(name).c_str(),
+ String8(package).c_str());
return UNKNOWN_ERROR;
}
@@ -1864,12 +1864,12 @@
const bool overwrite)
{
uint32_t rid = mAssets->getIncludedResources()
- .identifierForName(name.string(), name.size(),
- type.string(), type.size(),
- package.string(), package.size());
+ .identifierForName(name.c_str(), name.size(),
+ type.c_str(), type.size(),
+ package.c_str(), package.size());
if (rid != 0) {
sourcePos.error("Resource entry %s/%s is already defined in package %s.",
- String8(type).string(), String8(name).string(), String8(package).string());
+ String8(type).c_str(), String8(name).c_str(), String8(package).c_str());
return UNKNOWN_ERROR;
}
@@ -1899,12 +1899,12 @@
// Check for adding entries in other packages... for now we do
// nothing. We need to do the right thing here to support skinning.
uint32_t rid = mAssets->getIncludedResources()
- .identifierForName(name.string(), name.size(),
- type.string(), type.size(),
- package.string(), package.size());
+ .identifierForName(name.c_str(), name.size(),
+ type.c_str(), type.size(),
+ package.c_str(), package.size());
if (rid != 0) {
sourcePos.error("Resource entry %s/%s is already defined in package %s.",
- String8(type).string(), String8(name).string(), String8(package).string());
+ String8(type).c_str(), String8(name).c_str(), String8(package).c_str());
return UNKNOWN_ERROR;
}
@@ -1921,7 +1921,7 @@
}
if (!canAdd) {
sourcePos.error("Resource does not already exist in overlay at '%s'; use <add-resource> to add.\n",
- String8(name).string());
+ String8(name).c_str());
return UNKNOWN_ERROR;
}
}
@@ -1959,9 +1959,9 @@
// Check for adding entries in other packages... for now we do
// nothing. We need to do the right thing here to support skinning.
uint32_t rid = mAssets->getIncludedResources()
- .identifierForName(name.string(), name.size(),
- type.string(), type.size(),
- package.string(), package.size());
+ .identifierForName(name.c_str(), name.size(),
+ type.c_str(), type.size(),
+ package.c_str(), package.size());
if (rid != 0) {
return NO_ERROR;
}
@@ -1969,7 +1969,7 @@
#if 0
if (name == String16("left")) {
printf("Adding bag left: file=%s, line=%d, type=%s\n",
- sourcePos.file.striing(), sourcePos.line, String8(type).string());
+ sourcePos.file.striing(), sourcePos.line, String8(type).c_str());
}
#endif
sp<Entry> e = getEntry(package, type, name, sourcePos, replace, params);
@@ -1996,9 +1996,9 @@
{
// First look for this in the included resources...
uint32_t rid = mAssets->getIncludedResources()
- .identifierForName(name.string(), name.size(),
- type.string(), type.size(),
- package.string(), package.size());
+ .identifierForName(name.c_str(), name.size(),
+ type.c_str(), type.size(),
+ package.c_str(), package.size());
if (rid != 0) {
return true;
}
@@ -2022,9 +2022,9 @@
{
// First look for this in the included resources...
uint32_t rid = mAssets->getIncludedResources()
- .identifierForName(name.string(), name.size(),
- type.string(), type.size(),
- package.string(), package.size());
+ .identifierForName(name.c_str(), name.size(),
+ type.c_str(), type.size(),
+ package.c_str(), package.size());
if (rid != 0) {
return true;
}
@@ -2051,7 +2051,7 @@
const String16* defPackage)
{
String16 package, type, name;
- if (!ResTable::expandResourceRef(ref.string(), ref.size(), &package, &type, &name,
+ if (!ResTable::expandResourceRef(ref.c_str(), ref.size(), &package, &type, &name,
defType, defPackage ? defPackage:&mAssetsPackage, NULL)) {
return false;
}
@@ -2115,17 +2115,17 @@
// First look for this in the included resources...
uint32_t rid = mAssets->getIncludedResources()
- .identifierForName(name.string(), name.size(),
- attr16.string(), attr16.size(),
- package.string(), package.size());
+ .identifierForName(name.c_str(), name.size(),
+ attr16.c_str(), attr16.size(),
+ package.c_str(), package.size());
if (rid != 0) {
- source.error("Attribute \"%s\" has already been defined", String8(name).string());
+ source.error("Attribute \"%s\" has already been defined", String8(name).c_str());
return false;
}
sp<ResourceTable::Entry> entry = getEntry(package, attr16, name, source, false);
if (entry == NULL) {
- source.error("Failed to create entry attr/%s", String8(name).string());
+ source.error("Failed to create entry attr/%s", String8(name).c_str());
return false;
}
@@ -2146,7 +2146,7 @@
formatItem.value != formatValue16) {
source.error("Attribute \"%s\" already defined with incompatible format.\n"
"%s:%d: Original attribute defined here.",
- String8(name).string(), formatItem.sourcePos.file.string(),
+ String8(name).c_str(), formatItem.sourcePos.file.c_str(),
formatItem.sourcePos.line);
return false;
}
@@ -2207,9 +2207,9 @@
// First look for this in the included resources...
uint32_t specFlags = 0;
uint32_t rid = mAssets->getIncludedResources()
- .identifierForName(name.string(), name.size(),
- type.string(), type.size(),
- package.string(), package.size(),
+ .identifierForName(name.c_str(), name.size(),
+ type.c_str(), type.size(),
+ package.c_str(), package.size(),
&specFlags);
if (rid != 0) {
if (onlyPublic && (specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
@@ -2253,27 +2253,27 @@
String16 package, type, name;
bool refOnlyPublic = true;
if (!ResTable::expandResourceRef(
- ref.string(), ref.size(), &package, &type, &name,
+ ref.c_str(), ref.size(), &package, &type, &name,
defType, defPackage ? defPackage:&mAssetsPackage,
outErrorMsg, &refOnlyPublic)) {
if (kIsDebug) {
- printf("Expanding resource: ref=%s\n", String8(ref).string());
+ printf("Expanding resource: ref=%s\n", String8(ref).c_str());
printf("Expanding resource: defType=%s\n",
- defType ? String8(*defType).string() : "NULL");
+ defType ? String8(*defType).c_str() : "NULL");
printf("Expanding resource: defPackage=%s\n",
- defPackage ? String8(*defPackage).string() : "NULL");
- printf("Expanding resource: ref=%s\n", String8(ref).string());
+ defPackage ? String8(*defPackage).c_str() : "NULL");
+ printf("Expanding resource: ref=%s\n", String8(ref).c_str());
printf("Expanded resource: p=%s, t=%s, n=%s, res=0\n",
- String8(package).string(), String8(type).string(),
- String8(name).string());
+ String8(package).c_str(), String8(type).c_str(),
+ String8(name).c_str());
}
return 0;
}
uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic);
if (kIsDebug) {
printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
- String8(package).string(), String8(type).string(),
- String8(name).string(), res);
+ String8(package).c_str(), String8(type).c_str(),
+ String8(name).c_str(), res);
}
if (res == 0) {
if (outErrorMsg)
@@ -2284,7 +2284,7 @@
bool ResourceTable::isValidResourceName(const String16& s)
{
- const char16_t* p = s.string();
+ const char16_t* p = s.c_str();
bool first = true;
while (*p) {
if ((*p >= 'a' && *p <= 'z')
@@ -2315,7 +2315,7 @@
if (style == NULL || style->size() == 0) {
// Text is not styled so it can be any type... let's figure it out.
res = mAssets->getIncludedResources()
- .stringToValue(outValue, &finalStr, str.string(), str.size(), preserveSpaces,
+ .stringToValue(outValue, &finalStr, str.c_str(), str.size(), preserveSpaces,
coerceType, attrID, NULL, &mAssetsPackage, this,
accessorCookie, attrType);
} else {
@@ -2344,7 +2344,7 @@
if (kIsDebug) {
printf("Adding to pool string style #%zu config %s: %s\n",
style != NULL ? style->size() : 0U,
- configStr.string(), String8(finalStr).string());
+ configStr.c_str(), String8(finalStr).c_str());
}
if (style != NULL && style->size() > 0) {
outValue->data = pool->add(finalStr, *style, configTypeName, config);
@@ -2368,8 +2368,8 @@
uint32_t ResourceTable::getCustomResource(
const String16& package, const String16& type, const String16& name) const
{
- //printf("getCustomResource: %s %s %s\n", String8(package).string(),
- // String8(type).string(), String8(name).string());
+ //printf("getCustomResource: %s %s %s\n", String8(package).c_str(),
+ // String8(type).c_str(), String8(name).c_str());
sp<Package> p = mPackages.valueFor(package);
if (p == NULL) return 0;
sp<Type> t = p->getTypes().valueFor(type);
@@ -2400,7 +2400,7 @@
if (mAssetsPackage != package) {
mCurrentXmlPos.error("creating resource for external package %s: %s/%s.",
- String8(package).string(), String8(type).string(), String8(name).string());
+ String8(package).c_str(), String8(type).c_str(), String8(name).c_str());
if (package == String16("android")) {
mCurrentXmlPos.printf("did you mean to use @+id instead of @+android:id?");
}
@@ -2427,7 +2427,7 @@
Res_value value;
if (getItemValue(attrID, ResTable_map::ATTR_TYPE, &value)) {
//printf("getAttributeType #%08x (%s): #%08x\n", attrID,
- // String8(getEntry(attrID)->getName()).string(), value.data);
+ // String8(getEntry(attrID)->getName()).c_str(), value.data);
*outType = value.data;
return true;
}
@@ -2481,7 +2481,7 @@
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
ac->sourcePos.error("Error: %s (at '%s' with value '%s').\n",
- buf, ac->attr.string(), ac->value.string());
+ buf, ac->attr.c_str(), ac->value.c_str());
}
}
@@ -2493,7 +2493,7 @@
const size_t N = e->getBag().size();
for (size_t i=0; i<N; i++) {
const String16& key = e->getBag().keyAt(i);
- if (key.size() > 0 && key.string()[0] != '^') {
+ if (key.size() > 0 && key.c_str()[0] != '^') {
outKeys->add(key);
}
}
@@ -2506,14 +2506,14 @@
uint32_t attrID, const char16_t* name, size_t nameLen,
Res_value* outValue)
{
- //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).string());
+ //printf("getAttributeEnum #%08x %s\n", attrID, String8(name, nameLen).c_str());
String16 nameStr(name, nameLen);
sp<const Entry> e = getEntry(attrID);
if (e != NULL) {
const size_t N = e->getBag().size();
for (size_t i=0; i<N; i++) {
- //printf("Comparing %s to %s\n", String8(name, nameLen).string(),
- // String8(e->getBag().keyAt(i)).string());
+ //printf("Comparing %s to %s\n", String8(name, nameLen).c_str(),
+ // String8(e->getBag().keyAt(i)).c_str());
if (e->getBag().keyAt(i) == nameStr) {
return getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, outValue);
}
@@ -2529,7 +2529,7 @@
outValue->dataType = Res_value::TYPE_INT_HEX;
outValue->data = 0;
- //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).string());
+ //printf("getAttributeFlags #%08x %s\n", attrID, String8(name, nameLen).c_str());
String16 nameStr(name, nameLen);
sp<const Entry> e = getEntry(attrID);
if (e != NULL) {
@@ -2546,8 +2546,8 @@
String16 nameStr(start, pos-start);
size_t i;
for (i=0; i<N; i++) {
- //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).string(),
- // String8(e->getBag().keyAt(i)).string());
+ //printf("Comparing \"%s\" to \"%s\"\n", String8(nameStr).c_str(),
+ // String8(e->getBag().keyAt(i)).c_str());
if (e->getBag().keyAt(i) == nameStr) {
Res_value val;
bool got = getItemValue(attrID, e->getBag().valueAt(i).bagKeyId, &val);
@@ -2753,7 +2753,7 @@
if (mHasDefaultLocalization.find(c->getName())
== mHasDefaultLocalization.end()) {
// printf("Skip symbol [%08x] %s\n", rid,
- // String8(c->getName()).string());
+ // String8(c->getName()).c_str());
continue;
}
}
@@ -2763,7 +2763,7 @@
String16 comment(c->getComment());
typeSymbols->appendComment(String8(c->getName()), comment, c->getPos());
//printf("Type symbol [%08x] %s comment: %s\n", rid,
- // String8(c->getName()).string(), String8(comment).string());
+ // String8(c->getName()).c_str(), String8(comment).c_str());
comment = c->getTypeComment();
typeSymbols->appendTypeComment(String8(c->getName()), comment);
}
@@ -2809,10 +2809,10 @@
// Look for strings with no default localization
if (configSrcMap.count(defaultLocale) == 0) {
SourcePos().warning("string '%s' has no default translation.",
- String8(nameIter.first).string());
+ String8(nameIter.first).c_str());
if (mBundle->getVerbose()) {
for (const auto& locale : configSrcMap) {
- locale.second.printf("locale %s found", locale.first.string());
+ locale.second.printf("locale %s found", locale.first.c_str());
}
}
// !!! TODO: throw an error here in some circumstances
@@ -2820,7 +2820,7 @@
// Check that all requested localizations are present for this string
if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) {
- const char* allConfigs = mBundle->getConfigurations().string();
+ const char* allConfigs = mBundle->getConfigurations().c_str();
const char* start = allConfigs;
const char* comma;
@@ -2847,7 +2847,7 @@
// requiring a specific regional localization [e.g. de_DE] but there is an
// available string in the generic language localization [e.g. de];
// consider that string to have fulfilled the localization requirement.
- String8 region(config.string(), 2);
+ String8 region(config.c_str(), 2);
if (configSrcMap.find(region) == configSrcMap.end() &&
configSrcMap.count(defaultLocale) == 0) {
missingConfigs.insert(config);
@@ -2859,12 +2859,12 @@
if (!missingConfigs.empty()) {
String8 configStr;
for (const auto& iter : missingConfigs) {
- configStr.appendFormat(" %s", iter.string());
+ configStr.appendFormat(" %s", iter.c_str());
}
SourcePos().warning("string '%s' is missing %u required localizations:%s",
- String8(nameIter.first).string(),
+ String8(nameIter.first).c_str(),
(unsigned int)missingConfigs.size(),
- configStr.string());
+ configStr.c_str());
}
}
}
@@ -3021,7 +3021,7 @@
header->header.type = htods(RES_TABLE_PACKAGE_TYPE);
header->header.headerSize = htods(sizeof(*header));
header->id = htodl(static_cast<uint32_t>(p->getAssignedId()));
- strcpy16_htod(header->name, p->getName().string());
+ strcpy16_htod(header->name, p->getName().c_str());
// Write the string blocks.
const size_t typeStringsStart = data->getSize();
@@ -3061,7 +3061,7 @@
sp<Type> t = p->getTypes().valueFor(typeName);
LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"),
"Type name %s not found",
- String8(typeName).string());
+ String8(typeName).c_str());
if (t == NULL) {
continue;
}
@@ -3260,7 +3260,7 @@
sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
if (c != NULL) {
fprintf(stderr, "%s: no entries written for %s/%s (0x%08zx)\n", log_prefix,
- String8(typeName).string(), String8(c->getName()).string(),
+ String8(typeName).c_str(), String8(c->getName()).c_str(),
Res_MAKEID(p->getAssignedId() - 1, ti, i));
}
missing_entry = true;
@@ -3359,7 +3359,7 @@
sp<Package> libPackage = libs[i];
if (kIsDebug) {
fprintf(stderr, " Entry %s -> 0x%02x\n",
- String8(libPackage->getName()).string(),
+ String8(libPackage->getName()).c_str(),
(uint8_t)libPackage->getAssignedId());
}
@@ -3367,7 +3367,7 @@
entryStart, sizeof(ResTable_lib_entry));
memset(entry, 0, sizeof(*entry));
entry->packageId = htodl(libPackage->getAssignedId());
- strcpy16_htod(entry->packageName, libPackage->getName().string());
+ strcpy16_htod(entry->packageName, libPackage->getName().c_str());
}
}
return NO_ERROR;
@@ -3435,13 +3435,13 @@
const SourcePos& pos = c->getEntries().valueAt(k)->getPos();
if (pos.file != "") {
fprintf(fp," <!-- Declared at %s:%d -->\n",
- pos.file.string(), pos.line);
+ pos.file.c_str(), pos.line);
}
}
}
fprintf(fp, " <public type=\"%s\" name=\"%s\" id=\"0x%08x\" />\n",
- String8(t->getName()).string(),
- String8(c->getName()).string(),
+ String8(t->getName()).c_str(),
+ String8(c->getName()).c_str(),
getResId(pkg, t, c->getEntryIndex()));
}
}
@@ -3501,8 +3501,8 @@
}
sourcePos.error("Resource entry %s is already defined as a single item.\n"
"%s:%d: Originally defined here.\n",
- String8(mName).string(),
- mItem.sourcePos.file.string(), mItem.sourcePos.line);
+ String8(mName).c_str(),
+ mItem.sourcePos.file.c_str(), mItem.sourcePos.line);
return UNKNOWN_ERROR;
}
@@ -3517,21 +3517,21 @@
if (mType == TYPE_BAG) {
if (mBag.size() == 0) {
sourcePos.error("Resource entry %s is already defined as a bag.",
- String8(mName).string());
+ String8(mName).c_str());
} else {
const Item& item(mBag.valueAt(0));
sourcePos.error("Resource entry %s is already defined as a bag.\n"
"%s:%d: Originally defined here.\n",
- String8(mName).string(),
- item.sourcePos.file.string(), item.sourcePos.line);
+ String8(mName).c_str(),
+ item.sourcePos.file.c_str(), item.sourcePos.line);
}
return UNKNOWN_ERROR;
}
if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
sourcePos.error("Resource entry %s is already defined.\n"
"%s:%d: Originally defined here.\n",
- String8(mName).string(),
- mItem.sourcePos.file.string(), mItem.sourcePos.line);
+ String8(mName).c_str(),
+ mItem.sourcePos.file.c_str(), mItem.sourcePos.line);
return UNKNOWN_ERROR;
}
@@ -3562,12 +3562,12 @@
const Item& item(mBag.valueAt(origKey));
sourcePos.error("Resource entry %s already has bag item %s.\n"
"%s:%d: Originally defined here.\n",
- String8(mName).string(), String8(key).string(),
- item.sourcePos.file.string(), item.sourcePos.line);
+ String8(mName).c_str(), String8(key).c_str(),
+ item.sourcePos.file.c_str(), item.sourcePos.line);
return UNKNOWN_ERROR;
}
//printf("Replacing %s with %s\n",
- // String8(mBag.valueFor(key).value).string(), String8(value).string());
+ // String8(mBag.valueFor(key).value).c_str(), String8(value).c_str());
mBag.replaceValueFor(key, item);
}
@@ -3611,8 +3611,8 @@
String16 value("false");
if (kIsDebug) {
fprintf(stderr, "Generating %s:id/%s\n",
- String8(package).string(),
- String8(key).string());
+ String8(package).c_str(),
+ String8(key).c_str());
}
status_t err = table->addEntry(SourcePos(String8("<generated>"), 0), package,
id16, key, value);
@@ -3624,10 +3624,10 @@
#if 1
// fprintf(stderr, "ERROR: Bag attribute '%s' has not been defined.\n",
-// String8(key).string());
+// String8(key).c_str());
// const Item& item(mBag.valueAt(i));
// fprintf(stderr, "Referenced from file %s line %d\n",
-// item.sourcePos.file.string(), item.sourcePos.line);
+// item.sourcePos.file.c_str(), item.sourcePos.line);
// return UNKNOWN_ERROR;
#else
char numberStr[16];
@@ -3660,7 +3660,7 @@
mParentId = table->getResId(mParent, &style16, NULL, &errorMsg);
if (mParentId == 0) {
mPos.error("Error retrieving parent for item: %s '%s'.\n",
- errorMsg, String8(mParent).string());
+ errorMsg, String8(mParent).c_str());
hasErrors = true;
}
}
@@ -3670,11 +3670,11 @@
Item& it = mBag.editValueAt(i);
it.bagKeyId = table->getResId(key,
it.isId ? &id16 : &attr16, NULL, &errorMsg);
- //printf("Bag key of %s: #%08x\n", String8(key).string(), it.bagKeyId);
+ //printf("Bag key of %s: #%08x\n", String8(key).c_str(), it.bagKeyId);
if (it.bagKeyId == 0) {
it.sourcePos.error("Error: %s: %s '%s'.\n", errorMsg,
- String8(it.isId ? id16 : attr16).string(),
- String8(key).string());
+ String8(it.isId ? id16 : attr16).c_str(),
+ String8(key).c_str());
hasErrors = true;
}
}
@@ -3709,7 +3709,7 @@
}
} else {
mPos.error("Error: entry %s is not a single item or a bag.\n",
- String8(mName).string());
+ String8(mName).c_str());
return UNKNOWN_ERROR;
}
return NO_ERROR;
@@ -3732,7 +3732,7 @@
}
} else {
mPos.error("Error: entry %s is not a single item or a bag.\n",
- String8(mName).string());
+ String8(mName).c_str());
return UNKNOWN_ERROR;
}
return NO_ERROR;
@@ -3768,7 +3768,7 @@
par.data = htodl(it.parsedValue.data);
#if 0
printf("Writing item (%s): type=%d, data=0x%x, res0=0x%x\n",
- String8(mName).string(), it.parsedValue.dataType,
+ String8(mName).c_str(), it.parsedValue.dataType,
it.parsedValue.data, par.res0);
#endif
err = data->writeData(&par, it.parsedValue.size);
@@ -3852,7 +3852,7 @@
int32_t entryIdx = Res_GETENTRY(ident);
if (entryIdx < 0) {
sourcePos.error("Public resource %s/%s has an invalid 0 identifier (0x%08x).\n",
- String8(mName).string(), String8(name).string(), ident);
+ String8(mName).c_str(), String8(name).c_str(), ident);
return UNKNOWN_ERROR;
}
#endif
@@ -3863,7 +3863,7 @@
if (mPublicIndex > 0 && mPublicIndex != typeIdx) {
sourcePos.error("Public resource %s/%s has conflicting type codes for its"
" public identifiers (0x%x vs 0x%x).\n",
- String8(mName).string(), String8(name).string(),
+ String8(mName).c_str(), String8(name).c_str(),
mPublicIndex, typeIdx);
return UNKNOWN_ERROR;
}
@@ -3882,8 +3882,8 @@
sourcePos.error("Public resource %s/%s has conflicting public identifiers"
" (0x%08x vs 0x%08x).\n"
"%s:%d: Originally defined here.\n",
- String8(mName).string(), String8(name).string(), p.ident, ident,
- p.sourcePos.file.string(), p.sourcePos.line);
+ String8(mName).c_str(), String8(name).c_str(), p.ident, ident,
+ p.sourcePos.file.c_str(), p.sourcePos.line);
return UNKNOWN_ERROR;
}
}
@@ -3909,7 +3909,7 @@
if (overlay && !autoAddOverlay && mCanAddEntries.indexOf(entry) < 0) {
sourcePos.error("Resource at %s appears in overlay but not"
" in the base package; use <add-resource> to add.\n",
- String8(entry).string());
+ String8(entry).c_str());
return NULL;
}
c = new ConfigList(entry, sourcePos);
@@ -3931,7 +3931,7 @@
printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
"orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
"sw%ddp w%ddp h%ddp layout:%d\n",
- sourcePos.file.string(), sourcePos.line,
+ sourcePos.file.c_str(), sourcePos.line,
config->mcc, config->mnc,
config->language[0] ? config->language[0] : '-',
config->language[1] ? config->language[1] : '-',
@@ -3951,7 +3951,7 @@
config->screenLayout);
} else {
printf("New entry at %s:%d: NULL config\n",
- sourcePos.file.string(), sourcePos.line);
+ sourcePos.file.c_str(), sourcePos.line);
}
}
e = new Entry(entry, sourcePos);
@@ -4032,11 +4032,11 @@
const Public& p = mPublic.valueAt(j);
int32_t idx = Res_GETENTRY(p.ident);
//printf("Looking for entry \"%s\"/\"%s\" (0x%08x) in %d...\n",
- // String8(mName).string(), String8(name).string(), p.ident, N);
+ // String8(mName).c_str(), String8(name).c_str(), p.ident, N);
bool found = false;
for (i=0; i<N; i++) {
sp<ConfigList> e = origOrder.itemAt(i);
- //printf("#%d: \"%s\"\n", i, String8(e->getName()).string());
+ //printf("#%d: \"%s\"\n", i, String8(e->getName()).c_str());
if (e->getName() == name) {
if (idx >= (int32_t)mOrderedConfigs.size()) {
mOrderedConfigs.resize(idx + 1);
@@ -4056,10 +4056,10 @@
p.sourcePos.error("Multiple entry names declared for public entry"
" identifier 0x%x in type %s (%s vs %s).\n"
"%s:%d: Originally defined here.",
- idx+1, String8(mName).string(),
- String8(oe->getName()).string(),
- String8(name).string(),
- oe->getPublicSourcePos().file.string(),
+ idx+1, String8(mName).c_str(),
+ String8(oe->getName()).c_str(),
+ String8(name).c_str(),
+ oe->getPublicSourcePos().file.c_str(),
oe->getPublicSourcePos().line);
hasError = true;
}
@@ -4068,7 +4068,7 @@
if (!found) {
p.sourcePos.error("Public symbol %s/%s declared here is not defined.",
- String8(mName).string(), String8(name).string());
+ String8(mName).c_str(), String8(name).c_str());
hasError = true;
}
}
@@ -4189,9 +4189,9 @@
t->getFirstPublicSourcePos().error("Multiple type names declared for public type"
" identifier 0x%x (%s vs %s).\n"
"%s:%d: Originally defined here.",
- idx, String8(ot->getName()).string(),
- String8(t->getName()).string(),
- ot->getFirstPublicSourcePos().file.string(),
+ idx, String8(ot->getName()).c_str(),
+ String8(t->getName()).c_str(),
+ ot->getFirstPublicSourcePos().file.c_str(),
ot->getFirstPublicSourcePos().line);
return UNKNOWN_ERROR;
}
@@ -4399,8 +4399,8 @@
const Item& it = e->getBag().valueAt(i);
if (it.bagKeyId == 0) {
fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
- String8(e->getName()).string(),
- String8(e->getBag().keyAt(i)).string());
+ String8(e->getName()).c_str(),
+ String8(e->getBag().keyAt(i)).c_str());
}
if (it.bagKeyId == attrID) {
return ⁢
@@ -4427,8 +4427,8 @@
}
}
fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
- String8(e->getName()).string(),
- String8(e->getBag().keyAt(i)).string());
+ String8(e->getName()).c_str(),
+ String8(e->getBag().keyAt(i)).c_str());
return false;
}
item->evaluating = true;
@@ -4436,7 +4436,7 @@
if (kIsDebug) {
if (res) {
printf("getItemValue of #%08x[#%08x] (%s): type=#%08x, data=#%08x\n",
- resID, attrID, String8(getEntry(resID)->getName()).string(),
+ resID, attrID, String8(getEntry(resID)->getName()).c_str(),
outValue->dataType, outValue->data);
} else {
printf("getItemValue of #%08x[#%08x]: failed\n",
@@ -4713,10 +4713,10 @@
entriesToAdd[i].value->getPos()
.printf("using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
entriesToAdd[i].key.sdkVersion,
- String8(p->getName()).string(),
- String8(t->getName()).string(),
- String8(entriesToAdd[i].value->getName()).string(),
- entriesToAdd[i].key.toString().string());
+ String8(p->getName()).c_str(),
+ String8(t->getName()).c_str(),
+ String8(entriesToAdd[i].value->getName()).c_str(),
+ entriesToAdd[i].key.toString().c_str());
}
sp<Entry> newEntry = t->getEntry(c->getName(),
@@ -4801,8 +4801,8 @@
sp<AaptFile> newFile = new AaptFile(target->getSourceFile(),
AaptGroupEntry(newConfig), target->getResourceType());
String8 resPath = String8::format("res/%s/%s.xml",
- newFile->getGroupEntry().toDirName(target->getResourceType()).string(),
- String8(resourceName).string());
+ newFile->getGroupEntry().toDirName(target->getResourceType()).c_str(),
+ String8(resourceName).c_str());
resPath.convertToResPath();
// Add a resource table entry.
@@ -4893,10 +4893,10 @@
if (bundle->getVerbose()) {
SourcePos(node->getFilename(), node->getStartLineNumber()).printf(
"removing attribute %s%s%s from <%s>",
- String8(attr.ns).string(),
+ String8(attr.ns).c_str(),
(attr.ns.size() == 0 ? "" : ":"),
- String8(attr.name).string(),
- String8(node->getElementName()).string());
+ String8(attr.name).c_str(),
+ String8(node->getElementName()).c_str());
}
node->removeAttribute(i);
i--;
@@ -4925,8 +4925,8 @@
sp<AaptFile> newFile = new AaptFile(target->getSourceFile(),
AaptGroupEntry(newConfig), target->getResourceType());
String8 resPath = String8::format("res/%s/%s.xml",
- newFile->getGroupEntry().toDirName(target->getResourceType()).string(),
- String8(resourceName).string());
+ newFile->getGroupEntry().toDirName(target->getResourceType()).c_str(),
+ String8(resourceName).c_str());
resPath.convertToResPath();
// Add a resource table entry.
@@ -4934,10 +4934,10 @@
SourcePos(target->getSourceFile(), -1).printf(
"using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
newConfig.sdkVersion,
- mAssets->getPackage().string(),
- newFile->getResourceType().string(),
- String8(resourceName).string(),
- newConfig.toString().string());
+ mAssets->getPackage().c_str(),
+ newFile->getResourceType().c_str(),
+ String8(resourceName).c_str(),
+ newConfig.toString().c_str());
}
addEntry(SourcePos(),
@@ -5114,8 +5114,8 @@
sp<XMLNode> nestedRoot = findOnlyChildElement(child);
if (nestedRoot == NULL) {
source.error("<%s:%s> must have exactly one child element",
- String8(child->getElementNamespace()).string(),
- String8(child->getElementName()).string());
+ String8(child->getElementNamespace()).c_str(),
+ String8(child->getElementName()).c_str());
return UNKNOWN_ERROR;
}
@@ -5130,7 +5130,7 @@
// Parse the attribute name.
const char* errorMsg = NULL;
String16 attrPackage, attrType, attrName;
- bool result = ResTable::expandResourceRef(attr->string.string(),
+ bool result = ResTable::expandResourceRef(attr->string.c_str(),
attr->string.size(),
&attrPackage, &attrType, &attrName,
&kAttr16, &kAssetPackage16,
@@ -5156,11 +5156,11 @@
// This child element will be extracted into its own resource file.
// Generate a name and path for it from its parent.
nestedResourceName = String8::format("%s_%d",
- String8(resourceName).string(), suffix++);
+ String8(resourceName).c_str(), suffix++);
nestedResourcePath = String8::format("res/%s/%s.xml",
target->getGroupEntry().toDirName(target->getResourceType())
- .string(),
- nestedResourceName.string());
+ .c_str(),
+ nestedResourceName.c_str());
// Lookup or create the entry for this name.
sp<Entry> entry = getEntry(kAssetPackage16,
@@ -5187,20 +5187,20 @@
if (bundle->getVerbose()) {
source.printf("generating nested resource %s:%s/%s",
- mAssets->getPackage().string(), target->getResourceType().string(),
- nestedResourceName.string());
+ mAssets->getPackage().c_str(), target->getResourceType().c_str(),
+ nestedResourceName.c_str());
}
// Build the attribute reference and assign it to the parent.
String16 nestedResourceRef = String16(String8::format("@%s:%s/%s",
- mAssets->getPackage().string(), target->getResourceType().string(),
- nestedResourceName.string()));
+ mAssets->getPackage().c_str(), target->getResourceType().c_str(),
+ nestedResourceName.c_str()));
String16 attrNs = buildNamespace(attrPackage);
if (parent->getAttribute(attrNs, attrName) != NULL) {
SourcePos(parent->getFilename(), parent->getStartLineNumber())
.error("parent of nested resource already defines attribute '%s:%s'",
- String8(attrPackage).string(), String8(attrName).string());
+ String8(attrPackage).c_str(), String8(attrName).c_str());
return UNKNOWN_ERROR;
}
diff --git a/tools/aapt/SourcePos.cpp b/tools/aapt/SourcePos.cpp
index 3864320..e130286 100644
--- a/tools/aapt/SourcePos.cpp
+++ b/tools/aapt/SourcePos.cpp
@@ -80,12 +80,12 @@
if (!this->file.isEmpty()) {
if (this->line >= 0) {
- fprintf(to, "%s:%d: %s%s\n", this->file.string(), this->line, type, this->error.string());
+ fprintf(to, "%s:%d: %s%s\n", this->file.c_str(), this->line, type, this->error.c_str());
} else {
- fprintf(to, "%s: %s%s\n", this->file.string(), type, this->error.string());
+ fprintf(to, "%s: %s%s\n", this->file.c_str(), type, this->error.c_str());
}
} else {
- fprintf(to, "%s%s\n", type, this->error.string());
+ fprintf(to, "%s%s\n", type, this->error.c_str());
}
}
diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp
index 6cacd32..8d02683 100644
--- a/tools/aapt/StringPool.cpp
+++ b/tools/aapt/StringPool.cpp
@@ -67,7 +67,7 @@
const size_t NS = pool->size();
for (size_t s=0; s<NS; s++) {
auto str = pool->string8ObjectAt(s);
- printf("String #" ZD ": %s\n", (ZD_TYPE) s, (str.has_value() ? str->string() : ""));
+ printf("String #" ZD ": %s\n", (ZD_TYPE) s, (str.has_value() ? str->c_str() : ""));
}
}
@@ -139,7 +139,7 @@
if (eidx < 0) {
eidx = mEntries.add(entry(value));
if (eidx < 0) {
- fprintf(stderr, "Failure adding string %s\n", String8(value).string());
+ fprintf(stderr, "Failure adding string %s\n", String8(value).c_str());
return eidx;
}
}
@@ -148,7 +148,7 @@
entry& ent = mEntries.editItemAt(eidx);
if (kIsDebug) {
printf("*** adding config type name %s, was %s\n",
- configTypeName->string(), ent.configTypeName.string());
+ configTypeName->c_str(), ent.configTypeName.c_str());
}
if (ent.configTypeName.size() <= 0) {
ent.configTypeName = *configTypeName;
@@ -166,7 +166,7 @@
if (cmp >= 0) {
if (cmp > 0) {
if (kIsDebug) {
- printf("*** inserting config: %s\n", config->toString().string());
+ printf("*** inserting config: %s\n", config->toString().c_str());
}
ent.configs.insertAt(*config, addPos);
}
@@ -175,7 +175,7 @@
}
if (addPos >= ent.configs.size()) {
if (kIsDebug) {
- printf("*** adding config: %s\n", config->toString().string());
+ printf("*** adding config: %s\n", config->toString().c_str());
}
ent.configs.add(*config);
}
@@ -195,7 +195,7 @@
if (kIsDebug) {
printf("Adding string %s to pool: pos=%zd eidx=%zd vidx=%zd\n",
- String8(value).string(), pos, eidx, vidx);
+ String8(value).c_str(), pos, eidx, vidx);
}
return pos;
@@ -286,13 +286,13 @@
for (size_t i=0; i<N; i++) {
printf("#%d was %d: %s\n", i, newPosToOriginalPos[i],
- mEntries[mEntryArray[newPosToOriginalPos[i]]].makeConfigsString().string());
+ mEntries[mEntryArray[newPosToOriginalPos[i]]].makeConfigsString().c_str());
entries.add(mEntries[mEntryArray[i]]);
}
for (size_t i=0; i<entries.size(); i++) {
printf("Sorted config #%d: %s\n", i,
- entries[i].makeConfigsString().string());
+ entries[i].makeConfigsString().c_str());
}
#endif
@@ -363,8 +363,8 @@
printf("FINAL SORTED STRING CONFIGS:\n");
for (size_t i=0; i<mEntries.size(); i++) {
const entry& ent = mEntries[i];
- printf("#" ZD " %s: %s\n", (ZD_TYPE)i, ent.makeConfigsString().string(),
- String8(ent.value).string());
+ printf("#" ZD " %s: %s\n", (ZD_TYPE)i, ent.makeConfigsString().c_str(),
+ String8(ent.value).c_str());
}
#endif
}
@@ -415,7 +415,7 @@
ssize_t idx = add(span.name, true);
if (idx < 0) {
fprintf(stderr, "Error adding span for style tag '%s'\n",
- String8(span.name).string());
+ String8(span.name).c_str());
return idx;
}
span.span.name.index = (uint32_t)idx;
@@ -571,7 +571,7 @@
if (kIsDebug) {
printf("Writing entry #%zu: \"%s\" ent=%zu off=%zu\n",
i,
- String8(ent.value).string(),
+ String8(ent.value).c_str(),
mEntryArray[i],
ent.offset);
}
@@ -591,8 +591,8 @@
const Vector<size_t>* indices = offsetsForString(val);
ssize_t res = indices != NULL && indices->size() > 0 ? indices->itemAt(0) : -1;
if (kIsDebug) {
- printf("Offset for string %s: %zd (%s)\n", String8(val).string(), res,
- res >= 0 ? String8(mEntries[mEntryArray[res]].value).string() : String8());
+ printf("Offset for string %s: %zd (%s)\n", String8(val).c_str(), res,
+ res >= 0 ? String8(mEntries[mEntryArray[res]].value).c_str() : String8());
}
return res;
}
diff --git a/tools/aapt/Symbol.h b/tools/aapt/Symbol.h
index e157541..de1d60c 100644
--- a/tools/aapt/Symbol.h
+++ b/tools/aapt/Symbol.h
@@ -68,9 +68,9 @@
android::String8 Symbol::toString() const {
return android::String8::format("%s:%s/%s (0x%08x)",
- android::String8(package).string(),
- android::String8(type).string(),
- android::String8(name).string(),
+ android::String8(package).c_str(),
+ android::String8(type).c_str(),
+ android::String8(name).c_str(),
(int) id);
}
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 69392d6..e270a73 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -66,14 +66,14 @@
String16 getNamespaceResourcePackage(const String16& appPackage, const String16& namespaceUri, bool* outIsPublic)
{
- //printf("%s starts with %s?\n", String8(namespaceUri).string(),
- // String8(RESOURCES_PREFIX).string());
+ //printf("%s starts with %s?\n", String8(namespaceUri).c_str(),
+ // String8(RESOURCES_PREFIX).c_str());
size_t prefixSize;
bool isPublic = true;
if(namespaceUri.startsWith(RESOURCES_PREFIX_AUTO_PACKAGE)) {
if (kIsDebug) {
- printf("Using default application package: %s -> %s\n", String8(namespaceUri).string(),
- String8(appPackage).string());
+ printf("Using default application package: %s -> %s\n", String8(namespaceUri).c_str(),
+ String8(appPackage).c_str());
}
isPublic = true;
return appPackage;
@@ -88,7 +88,7 @@
}
//printf("YES!\n");
- //printf("namespace: %s\n", String8(String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize)).string());
+ //printf("namespace: %s\n", String8(String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize)).c_str());
if (outIsPublic) *outIsPublic = isPublic;
return String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize);
}
@@ -97,7 +97,7 @@
ResXMLTree* inXml,
const String16& str16)
{
- const char16_t* str = str16.string();
+ const char16_t* str = str16.c_str();
const char16_t* p = str;
const char16_t* end = str + str16.size();
@@ -223,7 +223,7 @@
String16 text(inXml->getText(&len));
if (firstTime && text.size() > 0) {
firstTime = false;
- if (text.string()[0] == '@') {
+ if (text.c_str()[0] == '@') {
// If this is a resource reference, don't do the pseudoloc.
pseudolocalize = NO_PSEUDOLOCALIZATION;
pseudo.setMethod(pseudolocalize);
@@ -263,7 +263,7 @@
{
SourcePos(String8(fileName), inXml->getLineNumber()).error(
"Found unsupported XLIFF tag <%s>\n",
- element8.string());
+ element8.c_str());
return UNKNOWN_ERROR;
}
moveon:
@@ -272,14 +272,14 @@
if (outSpans == NULL) {
SourcePos(String8(fileName), inXml->getLineNumber()).error(
- "Found style tag <%s> where styles are not allowed\n", element8.string());
+ "Found style tag <%s> where styles are not allowed\n", element8.c_str());
return UNKNOWN_ERROR;
}
- if (!ResTable::collectString(outString, curString.string(),
+ if (!ResTable::collectString(outString, curString.c_str(),
curString.size(), false, &errorMsg, true)) {
SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n",
- errorMsg, String8(curString).string());
+ errorMsg, String8(curString).c_str());
return UNKNOWN_ERROR;
}
rawString.append(curString);
@@ -295,7 +295,7 @@
str = inXml->getAttributeStringValue(ai, &len);
span.name.append(str, len);
}
- //printf("Span: %s\n", String8(span.name).string());
+ //printf("Span: %s\n", String8(span.name).c_str());
span.span.firstChar = span.span.lastChar = outString->size();
spanStack.push(span);
@@ -311,21 +311,21 @@
xliffDepth--;
continue;
}
- if (!ResTable::collectString(outString, curString.string(),
+ if (!ResTable::collectString(outString, curString.c_str(),
curString.size(), false, &errorMsg, true)) {
SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n",
- errorMsg, String8(curString).string());
+ errorMsg, String8(curString).c_str());
return UNKNOWN_ERROR;
}
rawString.append(curString);
curString = String16();
if (spanStack.size() == 0) {
- if (strcmp16(inXml->getElementName(&len), endTag.string()) != 0) {
+ if (strcmp16(inXml->getElementName(&len), endTag.c_str()) != 0) {
SourcePos(String8(fileName), inXml->getLineNumber()).error(
"Found tag %s where <%s> close is expected\n",
- String8(inXml->getElementName(&len)).string(),
- String8(endTag).string());
+ String8(inXml->getElementName(&len)).c_str(),
+ String8(endTag).c_str());
return UNKNOWN_ERROR;
}
break;
@@ -334,15 +334,15 @@
String16 spanTag;
ssize_t semi = span.name.findFirst(';');
if (semi >= 0) {
- spanTag.setTo(span.name.string(), semi);
+ spanTag.setTo(span.name.c_str(), semi);
} else {
spanTag.setTo(span.name);
}
- if (strcmp16(inXml->getElementName(&len), spanTag.string()) != 0) {
+ if (strcmp16(inXml->getElementName(&len), spanTag.c_str()) != 0) {
SourcePos(String8(fileName), inXml->getLineNumber()).error(
"Found close tag %s where close tag %s is expected\n",
- String8(inXml->getElementName(&len)).string(),
- String8(spanTag).string());
+ String8(inXml->getElementName(&len)).c_str(),
+ String8(spanTag).c_str());
return UNKNOWN_ERROR;
}
bool empty = true;
@@ -363,7 +363,7 @@
if (0 && empty) {
fprintf(stderr, "%s:%d: warning: empty '%s' span found in text '%s'\n",
fileName, inXml->getLineNumber(),
- String8(spanTag).string(), String8(*outString).string());
+ String8(spanTag).c_str(), String8(*outString).c_str());
}
} else if (code == ResXMLTree::START_NAMESPACE) {
@@ -380,11 +380,11 @@
if (outSpans != NULL && outSpans->size() > 0) {
if (curString.size() > 0) {
- if (!ResTable::collectString(outString, curString.string(),
+ if (!ResTable::collectString(outString, curString.c_str(),
curString.size(), false, &errorMsg, true)) {
SourcePos(String8(fileName), inXml->getLineNumber()).error(
"%s (in %s)\n",
- errorMsg, String8(curString).string());
+ errorMsg, String8(curString).c_str());
return UNKNOWN_ERROR;
}
}
@@ -450,10 +450,10 @@
String8 elemNs = build_namespace(namespaces, ns16);
const char16_t* com16 = block->getComment(&len);
if (com16) {
- printf("%s <!-- %s -->\n", prefix.string(), String8(com16).string());
+ printf("%s <!-- %s -->\n", prefix.c_str(), String8(com16).c_str());
}
- printf("%sE: %s%s (line=%d)\n", prefix.string(), elemNs.string(),
- String8(block->getElementName(&len)).string(),
+ printf("%sE: %s%s (line=%d)\n", prefix.c_str(), elemNs.c_str(),
+ String8(block->getElementName(&len)).c_str(),
block->getLineNumber());
int N = block->getAttributeCount();
depth++;
@@ -463,11 +463,11 @@
ns16 = block->getAttributeNamespace(i, &len);
String8 ns = build_namespace(namespaces, ns16);
String8 name(block->getAttributeName(i, &len));
- printf("%sA: ", prefix.string());
+ printf("%sA: ", prefix.c_str());
if (res) {
- printf("%s%s(0x%08x)", ns.string(), name.string(), res);
+ printf("%s%s(0x%08x)", ns.c_str(), name.c_str(), res);
} else {
- printf("%s%s", ns.string(), name.string());
+ printf("%s%s", ns.c_str(), name.c_str());
}
Res_value value;
block->getAttributeValue(i, &value);
@@ -480,14 +480,14 @@
} else if (value.dataType == Res_value::TYPE_STRING) {
printf("=\"%s\"",
ResTable::normalizeForOutput(String8(block->getAttributeStringValue(i,
- &len)).string()).string());
+ &len)).c_str()).c_str());
} else {
printf("=(type 0x%x)0x%x", (int)value.dataType, (int)value.data);
}
const char16_t* val = block->getAttributeStringValue(i, &len);
if (val != NULL) {
- printf(" (Raw: \"%s\")", ResTable::normalizeForOutput(String8(val).string()).
- string());
+ printf(" (Raw: \"%s\")", ResTable::normalizeForOutput(String8(val).c_str()).
+ c_str());
}
printf("\n");
}
@@ -509,8 +509,8 @@
}
ns.uri = String8(block->getNamespaceUri(&len));
namespaces.push(ns);
- printf("%sN: %s=%s\n", prefix.string(), ns.prefix.string(),
- ns.uri.string());
+ printf("%sN: %s=%s\n", prefix.c_str(), ns.prefix.c_str(),
+ ns.uri.c_str());
depth++;
} else if (code == ResXMLTree::END_NAMESPACE) {
if (--depth < 0) {
@@ -529,19 +529,19 @@
if (ns.prefix != pr) {
prefix = make_prefix(depth);
printf("%s*** BAD END NS PREFIX: found=%s, expected=%s\n",
- prefix.string(), pr.string(), ns.prefix.string());
+ prefix.c_str(), pr.c_str(), ns.prefix.c_str());
}
String8 uri = String8(block->getNamespaceUri(&len));
if (ns.uri != uri) {
prefix = make_prefix(depth);
printf("%s *** BAD END NS URI: found=%s, expected=%s\n",
- prefix.string(), uri.string(), ns.uri.string());
+ prefix.c_str(), uri.c_str(), ns.uri.c_str());
}
namespaces.pop();
} else if (code == ResXMLTree::TEXT) {
size_t len;
- printf("%sC: \"%s\"\n", prefix.string(),
- ResTable::normalizeForOutput(String8(block->getText(&len)).string()).string());
+ printf("%sC: \"%s\"\n", prefix.c_str(),
+ ResTable::normalizeForOutput(String8(block->getText(&len)).c_str()).c_str());
}
}
@@ -583,7 +583,7 @@
sp<XMLNode> XMLNode::parse(const sp<AaptFile>& file)
{
char buf[16384];
- int fd = open(file->getSourceFile().string(), O_RDONLY | O_BINARY);
+ int fd = open(file->getSourceFile().c_str(), O_RDONLY | O_BINARY);
if (fd < 0) {
SourcePos(file->getSourceFile(), -1).error("Unable to open file for read: %s",
strerror(errno));
@@ -875,9 +875,9 @@
}
if (kIsDebug) {
printf("Elem %s %s=\"%s\": set res id = 0x%08x\n",
- String8(getElementName()).string(),
- String8(mAttributes.itemAt(attrIdx).name).string(),
- String8(mAttributes.itemAt(attrIdx).string).string(),
+ String8(getElementName()).c_str(),
+ String8(mAttributes.itemAt(attrIdx).name).c_str(),
+ String8(mAttributes.itemAt(attrIdx).string).c_str(),
resId);
}
mAttributes.editItemAt(attrIdx).nameResId = resId;
@@ -915,7 +915,7 @@
void XMLNode::removeWhitespace(bool stripAll, const char** cDataTags)
{
- //printf("Removing whitespace in %s\n", String8(mElementName).string());
+ //printf("Removing whitespace in %s\n", String8(mElementName).c_str());
size_t N = mChildren.size();
if (cDataTags) {
String8 tag(mElementName);
@@ -931,13 +931,13 @@
sp<XMLNode> node = mChildren.itemAt(i);
if (node->getType() == TYPE_CDATA) {
// This is a CDATA node...
- const char16_t* p = node->mChars.string();
+ const char16_t* p = node->mChars.c_str();
while (*p != 0 && *p < 128 && isspace(*p)) {
p++;
}
//printf("Space ends at %d in \"%s\"\n",
- // (int)(p-node->mChars.string()),
- // String8(node->mChars).string());
+ // (int)(p-node->mChars.c_str()),
+ // String8(node->mChars).c_str());
if (*p == 0) {
if (stripAll) {
// Remove this node!
@@ -949,18 +949,18 @@
}
} else {
// Compact leading/trailing whitespace.
- const char16_t* e = node->mChars.string()+node->mChars.size()-1;
+ const char16_t* e = node->mChars.c_str()+node->mChars.size()-1;
while (e > p && *e < 128 && isspace(*e)) {
e--;
}
- if (p > node->mChars.string()) {
+ if (p > node->mChars.c_str()) {
p--;
}
- if (e < (node->mChars.string()+node->mChars.size()-1)) {
+ if (e < (node->mChars.c_str()+node->mChars.size()-1)) {
e++;
}
- if (p > node->mChars.string() ||
- e < (node->mChars.string()+node->mChars.size()-1)) {
+ if (p > node->mChars.c_str() ||
+ e < (node->mChars.c_str()+node->mChars.size()-1)) {
String16 tmp(p, e-p+1);
node->mChars = tmp;
}
@@ -986,14 +986,14 @@
table->setCurrentXmlPos(SourcePos(mFilename, getStartLineNumber()));
if (!assets->getIncludedResources()
.stringToValue(&e.value, &e.string,
- e.string.string(), e.string.size(), true, true,
+ e.string.c_str(), e.string.size(), true, true,
e.nameResId, NULL, &defPackage, table, &ac)) {
hasErrors = true;
}
if (kIsDebug) {
printf("Attr %s: type=0x%x, str=%s\n",
- String8(e.name).string(), e.value.dataType,
- String8(e.string).string());
+ String8(e.name).c_str(), e.value.dataType,
+ String8(e.string).c_str());
}
}
}
@@ -1023,30 +1023,30 @@
String16 pkg(getNamespaceResourcePackage(String16(assets->getPackage()), e.ns, &nsIsPublic));
if (kIsDebug) {
printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n",
- String8(getElementName()).string(),
- String8(e.name).string(),
- String8(e.string).string(),
- String8(e.ns).string(),
+ String8(getElementName()).c_str(),
+ String8(e.name).c_str(),
+ String8(e.string).c_str(),
+ String8(e.ns).c_str(),
(nsIsPublic) ? "public" : "private",
- String8(pkg).string());
+ String8(pkg).c_str());
}
if (pkg.size() <= 0) continue;
uint32_t res = table != NULL
? table->getResId(e.name, &attr, &pkg, &errorMsg, nsIsPublic)
: assets->getIncludedResources().
- identifierForName(e.name.string(), e.name.size(),
- attr.string(), attr.size(),
- pkg.string(), pkg.size());
+ identifierForName(e.name.c_str(), e.name.size(),
+ attr.c_str(), attr.size(),
+ pkg.c_str(), pkg.size());
if (res != 0) {
if (kIsDebug) {
printf("XML attribute name %s: resid=0x%08x\n",
- String8(e.name).string(), res);
+ String8(e.name).c_str(), res);
}
setAttributeResID(i, res);
} else {
SourcePos(mFilename, getStartLineNumber()).error(
"No resource identifier found for attribute '%s' in package '%s'\n",
- String8(e.name).string(), String8(pkg).string());
+ String8(e.name).c_str(), String8(pkg).c_str());
hasErrors = true;
}
}
@@ -1137,7 +1137,7 @@
if (kPrintStringMetrics) {
fprintf(stderr, "**** total xml size: %zu / %zu%% strings (in %s)\n",
dest->getSize(), (stringPool->getSize()*100)/dest->getSize(),
- dest->getPath().string());
+ dest->getPath().c_str());
}
return NO_ERROR;
@@ -1155,8 +1155,8 @@
if (elemNs.size() > 0) {
elemNs.append(":");
}
- printf("%s E: %s%s", prefix.string(),
- elemNs.string(), String8(getElementName()).string());
+ printf("%s E: %s%s", prefix.c_str(),
+ elemNs.c_str(), String8(getElementName()).c_str());
int N = mAttributes.size();
for (i=0; i<N; i++) {
ssize_t idx = mAttributeOrder.valueAt(i);
@@ -1171,21 +1171,21 @@
attrNs.append(":");
}
if (attr.nameResId) {
- printf("%s%s(0x%08x)", attrNs.string(),
- String8(attr.name).string(), attr.nameResId);
+ printf("%s%s(0x%08x)", attrNs.c_str(),
+ String8(attr.name).c_str(), attr.nameResId);
} else {
- printf("%s%s", attrNs.string(), String8(attr.name).string());
+ printf("%s%s", attrNs.c_str(), String8(attr.name).c_str());
}
- printf("=%s", String8(attr.string).string());
+ printf("=%s", String8(attr.string).c_str());
}
printf("\n");
} else if (getType() == TYPE_NAMESPACE) {
- printf("%s N: %s=%s\n", prefix.string(),
+ printf("%s N: %s=%s\n", prefix.c_str(),
getNamespacePrefix().size() > 0
- ? String8(getNamespacePrefix()).string() : "<DEF>",
- String8(getNamespaceUri()).string());
+ ? String8(getNamespacePrefix()).c_str() : "<DEF>",
+ String8(getNamespaceUri()).c_str());
} else {
- printf("%s C: \"%s\"\n", prefix.string(), String8(getCData()).string());
+ printf("%s C: \"%s\"\n", prefix.c_str(), String8(getCData()).c_str());
}
int N = mChildren.size();
for (i=0; i<N; i++) {
@@ -1258,7 +1258,7 @@
XMLNode::characterData(void *userData, const XML_Char *s, int len)
{
if (kIsDebugParse) {
- printf("CDATA: \"%s\"\n", String8(s, len).string());
+ printf("CDATA: \"%s\"\n", String8(s, len).c_str());
}
ParseState* st = (ParseState*)userData;
sp<XMLNode> node = NULL;
@@ -1423,7 +1423,7 @@
idx = outPool->add(attr.name);
if (kIsDebug) {
printf("Adding attr %s (resid 0x%08x) to pool: idx=%zd\n",
- String8(attr.name).string(), id, idx);
+ String8(attr.name).c_str(), id, idx);
}
if (id != 0) {
while ((ssize_t)outResIds->size() <= idx) {
@@ -1434,7 +1434,7 @@
}
attr.namePoolIdx = idx;
if (kIsDebug) {
- printf("String %s offset=0x%08zd\n", String8(attr.name).string(), idx);
+ printf("String %s offset=0x%08zd\n", String8(attr.name).c_str(), idx);
}
}
}
@@ -1488,7 +1488,7 @@
node.comment.index = htodl(
mComment.size() > 0 ? strings.offsetForString(mComment) : -1);
//if (mComment.size() > 0) {
- // printf("Flattening comment: %s\n", String8(mComment).string());
+ // printf("Flattening comment: %s\n", String8(mComment).c_str());
//}
} else {
node.comment.index = htodl((uint32_t)-1);
diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp
index 4e8dcb1..fc2ed98 100644
--- a/tools/aapt/pseudolocalize.cpp
+++ b/tools/aapt/pseudolocalize.cpp
@@ -42,7 +42,7 @@
size_t depth = mLastDepth;
size_t lastpos, pos;
const size_t length= text.size();
- const char16_t* str = text.string();
+ const char16_t* str = text.c_str();
bool escaped = false;
for (lastpos = pos = 0; pos < length; pos++) {
char16_t c = str[pos];
@@ -181,7 +181,7 @@
static String16 pseudo_generate_expansion(const unsigned int length) {
String16 result = k_expansion_string;
- const char16_t* s = result.string();
+ const char16_t* s = result.c_str();
if (result.size() < length) {
result += String16(" ");
result += pseudo_generate_expansion(length - result.size());
@@ -237,7 +237,7 @@
*/
String16 PseudoMethodAccent::text(const String16& source)
{
- const char16_t* s = source.string();
+ const char16_t* s = source.c_str();
String16 result;
const size_t I = source.size();
bool lastspace = true;
@@ -357,7 +357,7 @@
String16 PseudoMethodBidi::text(const String16& source)
{
- const char16_t* s = source.string();
+ const char16_t* s = source.c_str();
String16 result;
bool lastspace = true;
bool space = true;
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index cac4edd..6a17ef8 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -457,7 +457,7 @@
const size_t NS = pool->size();
for (size_t s=0; s<NS; s++) {
auto str = pool->string8ObjectAt(s);
- printer->Print(StringPrintf("String #%zd : %s\n", s, str.has_value() ? str->string() : ""));
+ printer->Print(StringPrintf("String #%zd : %s\n", s, str.has_value() ? str->c_str() : ""));
}
}
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 1671e1e..a92f24b 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -215,7 +215,7 @@
}
std::vector<std::string> sanitized_config_names;
for (const auto &config : constraints.configs) {
- sanitized_config_names.push_back(MakePackageSafeName(config.toString().string()));
+ sanitized_config_names.push_back(MakePackageSafeName(config.toString().c_str()));
}
split_name << "config." << util::Joiner(sanitized_config_names, "_");
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 75dcba5..2e20e81 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -453,7 +453,7 @@
const size_t count = entries.size();
for (size_t i = 0; i < count; i++) {
table_->included_packages_[entries.valueAt(i)] =
- android::util::Utf16ToUtf8(StringPiece16(entries.keyAt(i).string()));
+ android::util::Utf16ToUtf8(StringPiece16(entries.keyAt(i).c_str()));
}
return true;
}
diff --git a/tools/aapt2/trace/TraceBuffer.cpp b/tools/aapt2/trace/TraceBuffer.cpp
index fab2df3..0988c31 100644
--- a/tools/aapt2/trace/TraceBuffer.cpp
+++ b/tools/aapt2/trace/TraceBuffer.cpp
@@ -44,14 +44,16 @@
std::vector<TracePoint> traces;
bool enabled = true;
+constinit std::chrono::steady_clock::time_point startTime = {};
int64_t GetTime() noexcept {
auto now = std::chrono::steady_clock::now();
- return std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
+ if (startTime == decltype(tracebuffer::startTime){}) {
+ startTime = now;
+ }
+ return std::chrono::duration_cast<std::chrono::microseconds>(now - startTime).count();
}
-} // namespace anonymous
-
void AddWithTime(std::string tag, char type, int64_t time) noexcept {
TracePoint t = {type, getpid(), time, std::move(tag)};
traces.emplace_back(std::move(t));
@@ -76,7 +78,7 @@
// Wrap the trace in a JSON array [] to make Chrome/Perfetto UI handle it.
char delimiter = '[';
- for(const TracePoint& trace : traces) {
+ for (const TracePoint& trace : traces) {
fprintf(f,
"%c{\"ts\" : \"%" PRIu64
"\", \"ph\" : \"%c\", \"tid\" : \"%d\" , \"pid\" : \"%d\", \"name\" : \"%s\" }\n",
@@ -90,6 +92,8 @@
traces.clear();
}
+} // namespace
+
} // namespace tracebuffer
void BeginTrace(std::string tag) {
@@ -166,7 +170,7 @@
FlushTrace::~FlushTrace() {
if (!tracebuffer::enabled) return;
- tracebuffer::Add(tag_, tracebuffer::kEnd);
+ tracebuffer::Add(std::move(tag_), tracebuffer::kEnd);
tracebuffer::Flush(basepath_);
}
diff --git a/tools/split-select/Grouper_test.cpp b/tools/split-select/Grouper_test.cpp
index 7294a86..a8b78cd 100644
--- a/tools/split-select/Grouper_test.cpp
+++ b/tools/split-select/Grouper_test.cpp
@@ -179,7 +179,7 @@
errorMessage.append("\n");
}
}
- ADD_FAILURE() << errorMessage.string();
+ ADD_FAILURE() << errorMessage.c_str();
}
void GrouperTest::addSplit(Vector<SplitDescription>& splits, const char* str) {
diff --git a/tools/split-select/Main.cpp b/tools/split-select/Main.cpp
index e6966db..1e75117 100644
--- a/tools/split-select/Main.cpp
+++ b/tools/split-select/Main.cpp
@@ -99,8 +99,7 @@
}
masterRule = Rule::simplify(masterRule);
fprintf(stdout, " {\n \"path\": \"%s\",\n \"rules\": %s\n }",
- splits.keyAt(i).string(),
- masterRule->toJson(2).string());
+ splits.keyAt(i).c_str(), masterRule->toJson(2).c_str());
}
fprintf(stdout, "\n]\n");
}
@@ -158,25 +157,23 @@
const char16_t* name = xml.getElementName(&len);
String16 name16(name, len);
if (name16 == kManifestTag) {
- ssize_t idx = xml.indexOfAttribute(
- kAndroidNamespace.string(), kAndroidNamespace.size(),
- kVersionCodeAttr.string(), kVersionCodeAttr.size());
+ ssize_t idx = xml.indexOfAttribute(kAndroidNamespace.c_str(), kAndroidNamespace.size(),
+ kVersionCodeAttr.c_str(), kVersionCodeAttr.size());
if (idx >= 0) {
outInfo.versionCode = xml.getAttributeData(idx);
}
} else if (name16 == kApplicationTag) {
- ssize_t idx = xml.indexOfAttribute(
- kAndroidNamespace.string(), kAndroidNamespace.size(),
- kMultiArchAttr.string(), kMultiArchAttr.size());
+ ssize_t idx = xml.indexOfAttribute(kAndroidNamespace.c_str(), kAndroidNamespace.size(),
+ kMultiArchAttr.c_str(), kMultiArchAttr.size());
if (idx >= 0) {
outInfo.multiArch = xml.getAttributeData(idx) != 0;
}
} else if (name16 == kUsesSdkTag) {
- ssize_t idx = xml.indexOfAttribute(
- kAndroidNamespace.string(), kAndroidNamespace.size(),
- kMinSdkVersionAttr.string(), kMinSdkVersionAttr.size());
+ ssize_t idx =
+ xml.indexOfAttribute(kAndroidNamespace.c_str(), kAndroidNamespace.size(),
+ kMinSdkVersionAttr.c_str(), kMinSdkVersionAttr.size());
if (idx >= 0) {
uint16_t type = xml.getAttributeDataType(idx);
if (type >= Res_value::TYPE_FIRST_INT && type <= Res_value::TYPE_LAST_INT) {
@@ -187,10 +184,10 @@
fprintf(stderr, "warning: failed to retrieve android:minSdkVersion.\n");
} else {
char *endPtr;
- int minSdk = strtol(minSdk8->string(), &endPtr, 10);
- if (endPtr != minSdk8->string() + minSdk8->size()) {
+ int minSdk = strtol(minSdk8->c_str(), &endPtr, 10);
+ if (endPtr != minSdk8->c_str() + minSdk8->size()) {
fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n",
- minSdk8->string());
+ minSdk8->c_str());
} else {
outInfo.minSdkVersion = minSdk;
}
@@ -232,7 +229,7 @@
splits.add();
Vector<String8> parts = AaptUtil::splitAndLowerCase(dir->getFileName(i), '-');
if (parseAbi(parts, 0, &splits.editTop()) < 0) {
- fprintf(stderr, "Malformed library %s\n", dir->getFileName(i).string());
+ fprintf(stderr, "Malformed library %s\n", dir->getFileName(i).c_str());
splits.pop();
}
}
@@ -291,7 +288,7 @@
help();
return 0;
} else {
- fprintf(stderr, "error: unknown argument '%s'.\n", arg.string());
+ fprintf(stderr, "error: unknown argument '%s'.\n", arg.c_str());
usage();
return 1;
}
@@ -313,15 +310,14 @@
// Find out some details about the base APK.
AppInfo baseAppInfo;
if (!getAppInfo(baseApkPath, baseAppInfo)) {
- fprintf(stderr, "error: unable to read base APK: '%s'.\n", baseApkPath.string());
+ fprintf(stderr, "error: unable to read base APK: '%s'.\n", baseApkPath.c_str());
return 1;
}
SplitDescription targetSplit;
if (!generateFlag) {
if (!SplitDescription::parse(targetConfigStr, &targetSplit)) {
- fprintf(stderr, "error: invalid --target config: '%s'.\n",
- targetConfigStr.string());
+ fprintf(stderr, "error: invalid --target config: '%s'.\n", targetConfigStr.c_str());
usage();
return 1;
}
@@ -341,7 +337,7 @@
Vector<SplitDescription> splits = extractSplitDescriptionsFromApk(splitApkPaths[i]);
if (splits.isEmpty()) {
fprintf(stderr, "error: invalid --split path: '%s'. No splits found.\n",
- splitApkPaths[i].string());
+ splitApkPaths[i].c_str());
usage();
return 1;
}
@@ -364,7 +360,7 @@
const size_t matchingSplitApkPathCount = matchingSplitPaths.size();
for (size_t i = 0; i < matchingSplitApkPathCount; i++) {
if (matchingSplitPaths[i] != baseApkPath) {
- fprintf(stdout, "%s\n", matchingSplitPaths[i].string());
+ fprintf(stdout, "%s\n", matchingSplitPaths[i].c_str());
}
}
} else {
diff --git a/tools/split-select/Rule_test.cpp b/tools/split-select/Rule_test.cpp
index c6cff0d..c78533f 100644
--- a/tools/split-select/Rule_test.cpp
+++ b/tools/split-select/Rule_test.cpp
@@ -68,7 +68,7 @@
expected.erase(std::remove_if(expected.begin(), expected.end(), ::isspace), expected.end());
// Result
- std::string result(rule.toJson().string());
+ std::string result(rule.toJson().c_str());
result.erase(std::remove_if(result.begin(), result.end(), ::isspace), result.end());
ASSERT_EQ(expected, result);
diff --git a/tools/split-select/SplitDescription.cpp b/tools/split-select/SplitDescription.cpp
index 99bc23d..4e2b48e 100644
--- a/tools/split-select/SplitDescription.cpp
+++ b/tools/split-select/SplitDescription.cpp
@@ -134,8 +134,8 @@
String8 configStr;
String8 extensionStr;
if (index >= 0) {
- configStr.setTo(str.string(), index);
- extensionStr.setTo(str.string() + index + 1);
+ configStr.setTo(str.c_str(), index);
+ extensionStr.setTo(str.c_str() + index + 1);
} else {
configStr.setTo(str);
}
diff --git a/tools/split-select/TestRules.cpp b/tools/split-select/TestRules.cpp
index 86ccd6a..ca3c56f 100644
--- a/tools/split-select/TestRules.cpp
+++ b/tools/split-select/TestRules.cpp
@@ -78,9 +78,8 @@
const String8 actualStr(actual != NULL ? actual->toJson() : String8());
if (expectedStr != actualStr) {
- return ::testing::AssertionFailure()
- << "Expected: " << expectedStr.string() << "\n"
- << " Actual: " << actualStr.string();
+ return ::testing::AssertionFailure() << "Expected: " << expectedStr.c_str() << "\n"
+ << " Actual: " << actualStr.c_str();
}
return ::testing::AssertionSuccess();
}