Merge "Ensure that TaskView callbacks are made on the ui thread for the view" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index a271d06..08a09e1 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -15,6 +15,7 @@
aconfig_srcjars = [
":android.app.usage.flags-aconfig-java{.generated_srcjars}",
":android.content.pm.flags-aconfig-java{.generated_srcjars}",
+ ":android.hardware.radio.flags-aconfig-java{.generated_srcjars}",
":android.nfc.flags-aconfig-java{.generated_srcjars}",
":android.os.flags-aconfig-java{.generated_srcjars}",
":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
@@ -36,6 +37,7 @@
":hwui_flags_java_lib{.generated_srcjars}",
":display_flags_lib{.generated_srcjars}",
":android.multiuser.flags-aconfig-java{.generated_srcjars}",
+ ":android.app.flags-aconfig-java{.generated_srcjars}",
]
filegroup {
@@ -327,3 +329,29 @@
aconfig_declarations: "android.multiuser.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Activity Manager
+aconfig_declarations {
+ name: "android.app.flags-aconfig",
+ package: "android.app",
+ srcs: ["core/java/android/app/activity_manager.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.app.flags-aconfig-java",
+ aconfig_declarations: "android.app.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Broadcast Radio
+aconfig_declarations {
+ name: "android.hardware.radio.flags-aconfig",
+ package: "android.hardware.radio",
+ srcs: ["core/java/android/hardware/radio/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.hardware.radio.flags-aconfig-java",
+ aconfig_declarations: "android.hardware.radio.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 8989b10..56a69b4 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -67,6 +67,7 @@
tag: ".removed-api.txt",
},
],
+ api_surface: "public",
}
priv_apps = " --show-annotation android.annotation.SystemApi\\(" +
@@ -120,6 +121,7 @@
tag: ".removed-api.txt",
},
],
+ api_surface: "system",
}
droidstubs {
@@ -166,6 +168,7 @@
tag: ".removed-api.txt",
},
],
+ api_surface: "test",
}
droidstubs {
@@ -205,6 +208,7 @@
tag: ".removed-api.txt",
},
],
+ api_surface: "module-lib",
}
/////////////////////////////////////////////////////////////////////
@@ -381,8 +385,8 @@
java_api_library {
name: "android-non-updatable.stubs.from-text",
api_surface: "public",
- api_files: [
- ":non-updatable-current.txt",
+ api_contributions: [
+ "api-stubs-docs-non-updatable.api.contribution",
],
defaults: ["android-non-updatable_from_text_defaults"],
full_api_surface_stub: "android_stubs_current.from-text",
@@ -391,9 +395,9 @@
java_api_library {
name: "android-non-updatable.stubs.system.from-text",
api_surface: "system",
- api_files: [
- ":non-updatable-current.txt",
- ":non-updatable-system-current.txt",
+ api_contributions: [
+ "api-stubs-docs-non-updatable.api.contribution",
+ "system-api-stubs-docs-non-updatable.api.contribution",
],
defaults: ["android-non-updatable_from_text_defaults"],
full_api_surface_stub: "android_system_stubs_current.from-text",
@@ -402,10 +406,10 @@
java_api_library {
name: "android-non-updatable.stubs.test.from-text",
api_surface: "test",
- api_files: [
- ":non-updatable-current.txt",
- ":non-updatable-system-current.txt",
- ":non-updatable-test-current.txt",
+ api_contributions: [
+ "api-stubs-docs-non-updatable.api.contribution",
+ "system-api-stubs-docs-non-updatable.api.contribution",
+ "test-api-stubs-docs-non-updatable.api.contribution",
],
defaults: ["android-non-updatable_from_text_defaults"],
full_api_surface_stub: "android_test_stubs_current.from-text",
@@ -414,10 +418,10 @@
java_api_library {
name: "android-non-updatable.stubs.module_lib.from-text",
api_surface: "module_lib",
- api_files: [
- ":non-updatable-current.txt",
- ":non-updatable-system-current.txt",
- ":non-updatable-module-lib-current.txt",
+ api_contributions: [
+ "api-stubs-docs-non-updatable.api.contribution",
+ "system-api-stubs-docs-non-updatable.api.contribution",
+ "module-lib-api-stubs-docs-non-updatable.api.contribution",
],
defaults: ["android-non-updatable_from_text_defaults"],
full_api_surface_stub: "android_module_lib_stubs_current_full.from-text",
@@ -615,7 +619,6 @@
name: "android_test_stubs_current_contributions",
api_surface: "test",
api_contributions: [
- "test-api-stubs-docs-non-updatable.api.contribution",
"framework-virtualization.stubs.source.test.api.contribution",
"framework-location.stubs.source.test.api.contribution",
],
@@ -690,6 +693,7 @@
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
+ "test-api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 22b1ef8..d8e3abf 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4616,9 +4616,9 @@
public class ActivityManager {
method public int addAppTask(@NonNull android.app.Activity, @NonNull android.content.Intent, @Nullable android.app.ActivityManager.TaskDescription, @NonNull android.graphics.Bitmap);
- method public void addStartInfoTimestamp(@IntRange(from=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START, to=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int, long);
+ method @FlaggedApi("android.app.app_start_info") public void addStartInfoTimestamp(@IntRange(from=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START, to=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int, long);
method public void appNotResponding(@NonNull String);
- method public void clearApplicationStartInfoCompletionListener();
+ method @FlaggedApi("android.app.app_start_info") public void clearApplicationStartInfoCompletionListener();
method public boolean clearApplicationUserData();
method public void clearWatchHeapLimit();
method @RequiresPermission(android.Manifest.permission.DUMP) public void dumpPackageState(java.io.FileDescriptor, String);
@@ -4626,7 +4626,7 @@
method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo();
method @NonNull public java.util.List<android.app.ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String, @IntRange(from=0) int, @IntRange(from=0) int);
- method @NonNull public java.util.List<android.app.ApplicationStartInfo> getHistoricalProcessStartReasons(@IntRange(from=0) int);
+ method @FlaggedApi("android.app.app_start_info") @NonNull public java.util.List<android.app.ApplicationStartInfo> getHistoricalProcessStartReasons(@IntRange(from=0) int);
method public int getLargeMemoryClass();
method public int getLauncherLargeIconDensity();
method public int getLauncherLargeIconSize();
@@ -4653,7 +4653,7 @@
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int);
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int, android.os.Bundle);
method @Deprecated public void restartPackage(String);
- method public void setApplicationStartInfoCompletionListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.ApplicationStartInfo>);
+ method @FlaggedApi("android.app.app_start_info") public void setApplicationStartInfoCompletionListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.ApplicationStartInfo>);
method public void setProcessStateSummary(@Nullable byte[]);
method public static void setVrThread(int);
method public void setWatchHeapLimit(long);
@@ -5234,7 +5234,7 @@
field public static final int REASON_USER_STOPPED = 11; // 0xb
}
- public final class ApplicationStartInfo implements android.os.Parcelable {
+ @FlaggedApi("android.app.app_start_info") public final class ApplicationStartInfo implements android.os.Parcelable {
method public int describeContents();
method public int getDefiningUid();
method @Nullable public android.content.Intent getIntent();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ad65806..220482d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -540,7 +540,7 @@
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String);
method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser();
- method @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int);
+ method @FlaggedApi(Flags.FLAG_APP_START_INFO) @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
@@ -4529,6 +4529,7 @@
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float);
+ field public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 128; // 0x80
field public static final int VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED = 65536; // 0x10000
field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
}
@@ -9842,7 +9843,6 @@
field public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; // 0x1
field public static final int BUGREPORT_MODE_FULL = 0; // 0x0
field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1
- field public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7
field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2
field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4
field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4f45691..3bf2cca 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -24,6 +24,7 @@
import android.Manifest;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -3982,6 +3983,7 @@
* the order from most recent to least recent.
*/
@NonNull
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public List<ApplicationStartInfo> getHistoricalProcessStartReasons(
@IntRange(from = 0) int maxNum) {
try {
@@ -4012,6 +4014,7 @@
*/
@NonNull
@SystemApi
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
@RequiresPermission(Manifest.permission.DUMP)
public List<ApplicationStartInfo> getExternalHistoricalProcessStartReasons(
@NonNull String packageName, @IntRange(from = 0) int maxNum) {
@@ -4044,6 +4047,7 @@
*
* @throws IllegalArgumentException if executor or listener are null.
*/
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public void setApplicationStartInfoCompletionListener(@NonNull final Executor executor,
@NonNull final Consumer<ApplicationStartInfo> listener) {
Preconditions.checkNotNull(executor, "executor cannot be null");
@@ -4065,6 +4069,7 @@
/**
* Removes the callback set by {@link #setApplicationStartInfoCompletionListener} if there is one.
*/
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public void clearApplicationStartInfoCompletionListener() {
try {
getService().clearApplicationStartInfoCompleteListener(mContext.getUserId());
@@ -4089,6 +4094,7 @@
* Will thow {@link java.lang.IllegalArgumentException} if not in range.
* @param timestampNs Clock monotonic time in nanoseconds of event to be recorded.
*/
+ @FlaggedApi(Flags.FLAG_APP_START_INFO)
public void addStartInfoTimestamp(@IntRange(
from = ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START,
to = ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int key,
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index f5fb6ed..a6a57cd 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,6 +38,7 @@
/**
* Provide information related to a processes startup.
*/
+@FlaggedApi(Flags.FLAG_APP_START_INFO)
public final class ApplicationStartInfo implements Parcelable {
/**
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 7f38b27..ec5effd 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -98,6 +98,7 @@
ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(String pkg, int uid, String groupId, boolean includeDeleted);
+ ParceledListSlice getRecentBlockedNotificationChannelGroupsForPackage(String pkg, int uid);
void updateNotificationChannelGroupForPackage(String pkg, int uid, in NotificationChannelGroup group);
void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
void unlockNotificationChannel(String pkg, int uid, String channelId);
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 6f4abfd..6a03c17 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -207,9 +207,10 @@
final long identity = Binder.clearCallingIdentity();
try {
if (rotation == UiAutomation.ROTATION_UNFREEZE) {
- mWindowManager.thawRotation();
+ mWindowManager.thawRotation(/* caller= */ "UiAutomationConnection#setRotation");
} else {
- mWindowManager.freezeRotation(rotation);
+ mWindowManager.freezeRotation(rotation,
+ /* caller= */ "UiAutomationConnection#setRotation");
}
return true;
} catch (RemoteException re) {
@@ -615,11 +616,13 @@
if (mInitialFrozenRotation != INITIAL_FROZEN_ROTATION_UNSPECIFIED) {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
- mWindowManager.freezeRotation(mInitialFrozenRotation);
+ mWindowManager.freezeRotation(mInitialFrozenRotation,
+ /* caller= */ "UiAutomationConnection#restoreRotationStateLocked");
} else {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
- mWindowManager.thawRotation();
+ mWindowManager.thawRotation(
+ /* caller= */ "UiAutomationConnection#restoreRotationStateLocked");
}
} catch (RemoteException re) {
/* ignore */
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
new file mode 100644
index 0000000..2076e85
--- /dev/null
+++ b/core/java/android/app/activity_manager.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+ namespace: "system_performance"
+ name: "app_start_info"
+ description: "Control collecting of ApplicationStartInfo records and APIs."
+ bug: "247814855"
+}
\ No newline at end of file
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index ed0f872..15bd1dc 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -22,8 +22,10 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.FillRequest;
+import android.text.InputType;
import android.text.Spanned;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.view.View;
@@ -2452,7 +2454,7 @@
+ node.getTextStyle());
Log.i(TAG, prefix + " Text color fg: #" + Integer.toHexString(node.getTextColor())
+ ", bg: #" + Integer.toHexString(node.getTextBackgroundColor()));
- Log.i(TAG, prefix + " Input type: " + node.getInputType());
+ Log.i(TAG, prefix + " Input type: " + getInputTypeString(node.getInputType()));
Log.i(TAG, prefix + " Resource id: " + node.getTextIdEntry());
}
String webDomain = node.getWebDomain();
@@ -2664,4 +2666,33 @@
return new AssistStructure[size];
}
};
+
+ private static final ArrayMap<Integer, String> INPUT_TYPE_VARIATIONS = new ArrayMap<>();
+ static {
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, "EmailSubject");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS, "PostalAddress");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PERSON_NAME, "PersonName");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PASSWORD, "Password");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, "VisiblePassword");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_URI, "URI");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS, "WebEmailAddress");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD, "WebPassword");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE, "LongMessage");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE, "ShortMessage");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_MULTI_LINE, "MultiLine");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE, "ImeMultiLine");
+ INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_FILTER, "Filter");
+ }
+
+ private static String getInputTypeString(int inputType) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(inputType);
+ sb.append("(class=").append(inputType & InputType.TYPE_MASK_CLASS).append(')');
+ for (int variation : INPUT_TYPE_VARIATIONS.keySet()) {
+ if ((variation & inputType) == variation) {
+ sb.append('|').append(INPUT_TYPE_VARIATIONS.get(variation));
+ }
+ }
+ return sb.toString();
+ }
}
diff --git a/core/java/android/database/sqlite/package.html b/core/java/android/database/sqlite/package.html
index 2c36646..f2df536 100644
--- a/core/java/android/database/sqlite/package.html
+++ b/core/java/android/database/sqlite/package.html
@@ -15,7 +15,7 @@
<a href="{@docRoot}studio/command-line/sqlite3.html">sqlite3</a> command-line
database tool. On your development machine, run the tool from the
<code>platform-tools/</code> folder of your SDK. On the emulator, run the tool
-with adb shell, for example, <code>adb -e shell sqlite3</code>.
+with adb shell, for example, <code>adb shell sqlite3</code>.
<p>The version of SQLite depends on the version of Android. See the following table:
<table style="width:auto;">
@@ -42,15 +42,19 @@
<ul>
<li>If available, use the sqlite3 tool, for example:
- <code>adb -e shell sqlite3 --version</code>.</li>
+ <code>adb shell sqlite3 --version</code>.</li>
<li>Create and query an in-memory database as shown in the following code sample:
<pre>
String query = "select sqlite_version() AS sqlite_version";
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(":memory:", null);
Cursor cursor = db.rawQuery(query, null);
String sqliteVersion = "";
- if (cursor.moveToNext()) {
- sqliteVersion = cursor.getString(0);
+ try {
+ if (cursor.moveToNext()) {
+ sqliteVersion = cursor.getString(0);
+ }
+ } finally {
+ cursor.close();
}</pre>
</li>
</ul>
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index f0c87a1..990ebc5 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -27,6 +27,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -364,11 +365,20 @@
/**
* Virtual display flag: Indicates that the orientation of this display device is coupled to
- * the rotation of its associated logical display.
+ * the orientation of its associated logical display.
+ * <p>
+ * The flag should not be set when the physical display is mounted in a fixed orientation
+ * such as on a desk. Without this flag, display manager will apply a coordinate transformation
+ * such as a scale and translation to letterbox or pillarbox format under the assumption that
+ * the physical orientation of the display is invariant. With this flag set, the content will
+ * rotate to fill in the space of the display, as it does on the internal device display.
+ * </p>
*
* @see #createVirtualDisplay
* @hide
*/
+ @SuppressLint("UnflaggedApi")
+ @SystemApi
public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7;
/**
diff --git a/core/java/android/hardware/radio/flags.aconfig b/core/java/android/hardware/radio/flags.aconfig
new file mode 100644
index 0000000..dbc1a4b
--- /dev/null
+++ b/core/java/android/hardware/radio/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.hardware.radio"
+
+flag {
+ name: "hd_radio_improved"
+ namespace: "car_framework"
+ description: "Feature flag for improved HD radio support with less vendor extensions"
+ bug: "280300929"
+}
diff --git a/core/java/android/net/network-policy-restrictions.md b/core/java/android/net/network-policy-restrictions.md
index 04c658c..20f3d74 100644
--- a/core/java/android/net/network-policy-restrictions.md
+++ b/core/java/android/net/network-policy-restrictions.md
@@ -29,8 +29,8 @@
| **DS** | *AL* | ok | blk | ok | ok |
| **ON** | *!AL* | blk | blk | blk | blk |
| | *DL* | blk | blk | blk | blk |
-| **DS** | *AL* | blk | blk | ok | ok |
-| **OFF** | *!AL* | blk | blk | ok | ok |
+| **DS** | *AL* | ok | blk | ok | ok |
+| **OFF** | *!AL* | ok | blk | ok | ok |
| | *DL* | blk | blk | blk | blk |
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index ada5532..f817fb8 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -32,7 +32,9 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -613,15 +615,35 @@
*/
public native boolean transactNative(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
+
+ /* This list is to hold strong reference to the death recipients that are waiting for the death
+ * of binder that this proxy references. Previously, the death recipients were strongy
+ * referenced from JNI, but that can cause memory leak (b/298374304) when the application has a
+ * strong reference from the death recipient to the proxy object. The JNI reference is now weak.
+ * And this strong reference is to keep death recipients at least until the proxy is GC'ed. */
+ private List<DeathRecipient> mDeathRecipients = Collections.synchronizedList(new ArrayList<>());
+
/**
* See {@link IBinder#linkToDeath(DeathRecipient, int)}
*/
- public native void linkToDeath(DeathRecipient recipient, int flags)
- throws RemoteException;
+ public void linkToDeath(DeathRecipient recipient, int flags)
+ throws RemoteException {
+ linkToDeathNative(recipient, flags);
+ mDeathRecipients.add(recipient);
+ }
+
/**
* See {@link IBinder#unlinkToDeath}
*/
- public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+ public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
+ mDeathRecipients.remove(recipient);
+ return unlinkToDeathNative(recipient, flags);
+ }
+
+ private native void linkToDeathNative(DeathRecipient recipient, int flags)
+ throws RemoteException;
+
+ private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags);
/**
* Perform a dump on the remote object
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index f10467f..47ad72f 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -124,6 +124,8 @@
/**
* Options for a lightweight bugreport intended to be taken for onboarding-related flows.
+ *
+ * @hide
*/
public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING;
diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java
index 2dbb5da..dae3202 100644
--- a/core/java/android/security/FileIntegrityManager.java
+++ b/core/java/android/security/FileIntegrityManager.java
@@ -76,28 +76,38 @@
* Enables fs-verity to the owned file under the calling app's private directory. It always uses
* the common configuration, i.e. SHA-256 digest algorithm, 4K block size, and without salt.
*
- * The operation can only succeed when the file is not opened as writable by any process.
+ * <p>For enabling fs-verity to succeed, the device must support fs-verity, the file must be
+ * writable by the app and not already have fs-verity enabled, and the file must not currently
+ * be open for writing by any process. To check whether the device supports fs-verity, use
+ * {@link #isApkVeritySupported()}.
*
- * It takes O(file size) time to build the underlying data structure for continuous
+ * <p>It takes O(file size) time to build the underlying data structure for continuous
* verification. The operation is atomic, i.e. it's either enabled or not, even in case of
* power failure during or after the call.
*
- * Note for the API users: When the file's authenticity is crucial, the app typical needs to
+ * <p>Note for the API users: When the file's authenticity is crucial, the app typical needs to
* perform a signature check by itself before using the file. The signature is often delivered
* as a separate file and stored next to the targeting file in the filesystem. The public key of
* the signer (normally the same app developer) can be put in the APK, and the app can use the
* public key to verify the signature to the file's actual fs-verity digest (from {@link
- * #getFsVerityDigest}) before using the file. The exact format is not prescribed by the
+ * #getFsVerityDigest(File)}) before using the file. The exact format is not prescribed by the
* framework. App developers may choose to use common practices like JCA for the signing and
* verification, or their own preferred approach.
*
- * @param file The file to enable fs-verity. It should be an absolute path.
+ * @param file The file to enable fs-verity. It must represent an absolute path.
+ * @throws IllegalArgumentException If the provided file is not an absolute path.
+ * @throws IOException If the operation failed.
*
* @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
*/
@FlaggedApi(Flags.FLAG_FSVERITY_API)
public void setupFsVerity(@NonNull File file) throws IOException {
if (!file.isAbsolute()) {
+ // fs-verity is to be enabled by installd, which enforces the validation to the
+ // (untrusted) file path passed from here. To make this less error prone, installd
+ // accepts only absolute path. When a relative path is provided, we fail with an
+ // explicit exception to help developers understand the requirement to use an absolute
+ // path.
throw new IllegalArgumentException("Expect an absolute path");
}
IFsveritySetupAuthToken authToken;
@@ -121,11 +131,12 @@
}
/**
- * Returns the fs-verity digest for the owned file under the calling app's
- * private directory, or null when the file does not have fs-verity enabled.
+ * Returns the fs-verity digest for the owned file under the calling app's private directory, or
+ * null when the file does not have fs-verity enabled (including when fs-verity is not supported
+ * on older devices).
*
* @param file The file to measure the fs-verity digest.
- * @return The fs-verity digeset in byte[], null if none.
+ * @return The fs-verity digest in byte[], null if none.
* @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
*/
@FlaggedApi(Flags.FLAG_FSVERITY_API)
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index a46136a..31d759e 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -264,6 +264,16 @@
}
/**
+ * Called when a display hotplug event with connection error is received.
+ *
+ * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
+ * timebase.
+ * @param connectionError the hotplug connection error code.
+ */
+ public void onHotplugConnectionError(long timestampNanos, int connectionError) {
+ }
+
+ /**
* Called when a display mode changed event is received.
*
* @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
@@ -345,6 +355,11 @@
onHotplug(timestampNanos, physicalDisplayId, connected);
}
+ @SuppressWarnings("unused")
+ private void dispatchHotplugConnectionError(long timestampNanos, int connectionError) {
+ onHotplugConnectionError(timestampNanos, connectionError);
+ }
+
// Called from native code.
@SuppressWarnings("unused")
private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 512f4f2..981911e 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.Display.Mode.INVALID_MODE_ID;
import static android.view.DisplayInfoProto.APP_HEIGHT;
import static android.view.DisplayInfoProto.APP_WIDTH;
import static android.view.DisplayInfoProto.CUTOUT;
@@ -200,6 +201,11 @@
public int defaultModeId;
/**
+ * The user preferred display mode.
+ */
+ public int userPreferredModeId = INVALID_MODE_ID;
+
+ /**
* The supported modes of this display.
*/
public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY;
@@ -420,6 +426,7 @@
&& modeId == other.modeId
&& renderFrameRate == other.renderFrameRate
&& defaultModeId == other.defaultModeId
+ && userPreferredModeId == other.userPreferredModeId
&& Arrays.equals(supportedModes, other.supportedModes)
&& colorMode == other.colorMode
&& Arrays.equals(supportedColorModes, other.supportedColorModes)
@@ -478,6 +485,7 @@
modeId = other.modeId;
renderFrameRate = other.renderFrameRate;
defaultModeId = other.defaultModeId;
+ userPreferredModeId = other.userPreferredModeId;
supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length);
colorMode = other.colorMode;
supportedColorModes = Arrays.copyOf(
@@ -530,6 +538,7 @@
modeId = source.readInt();
renderFrameRate = source.readFloat();
defaultModeId = source.readInt();
+ userPreferredModeId = source.readInt();
int nModes = source.readInt();
supportedModes = new Display.Mode[nModes];
for (int i = 0; i < nModes; i++) {
@@ -596,6 +605,7 @@
dest.writeInt(modeId);
dest.writeFloat(renderFrameRate);
dest.writeInt(defaultModeId);
+ dest.writeInt(userPreferredModeId);
dest.writeInt(supportedModes.length);
for (int i = 0; i < supportedModes.length; i++) {
supportedModes[i].writeToParcel(dest, flags);
@@ -832,9 +842,12 @@
sb.append(presentationDeadlineNanos);
sb.append(", mode ");
sb.append(modeId);
+ sb.append(", renderFrameRate ");
sb.append(renderFrameRate);
sb.append(", defaultMode ");
sb.append(defaultModeId);
+ sb.append(", userPreferredModeId ");
+ sb.append(userPreferredModeId);
sb.append(", modes ");
sb.append(Arrays.toString(supportedModes));
sb.append(", hdrCapabilities ");
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d3b7a5b..cccac95 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -316,14 +316,14 @@
* android.view.Display#DEFAULT_DISPLAY} and given rotation.
*/
@UnsupportedAppUsage
- void freezeRotation(int rotation);
+ void freezeRotation(int rotation, String caller);
/**
* Equivalent to calling {@link #thawDisplayRotation(int)} with {@link
* android.view.Display#DEFAULT_DISPLAY}.
*/
@UnsupportedAppUsage
- void thawRotation();
+ void thawRotation(String caller);
/**
* Equivelant to call {@link #isDisplayRotationFrozen(int)} with {@link
@@ -341,7 +341,7 @@
* {@link android.view.Surface#ROTATION_270} or -1 to freeze it to current rotation.
* @hide
*/
- void freezeDisplayRotation(int displayId, int rotation);
+ void freezeDisplayRotation(int displayId, int rotation, String caller);
/**
* Release the orientation lock imposed by freezeRotation() on the display.
@@ -349,7 +349,7 @@
* @param displayId the ID of display which rotation should be thawed.
* @hide
*/
- void thawDisplayRotation(int displayId);
+ void thawDisplayRotation(int displayId, String caller);
/**
* Gets whether the rotation is frozen on the display.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0ba5d06..f421351 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3858,9 +3858,7 @@
mPendingTransitions.clear();
}
- if (mActiveSurfaceSyncGroup != null) {
- mActiveSurfaceSyncGroup.markSyncReady();
- }
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
} else if (cancelAndRedraw) {
mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason()
@@ -3874,8 +3872,8 @@
}
mPendingTransitions.clear();
}
- if (!performDraw(mActiveSurfaceSyncGroup) && mActiveSurfaceSyncGroup != null) {
- mActiveSurfaceSyncGroup.markSyncReady();
+ if (!performDraw(mActiveSurfaceSyncGroup)) {
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
}
}
@@ -3890,6 +3888,7 @@
mReportNextDraw = false;
mLastReportNextDrawReason = null;
mActiveSurfaceSyncGroup = null;
+ mHasPendingTransactions = false;
mSyncBuffer = false;
if (isInWMSRequestedSync()) {
mWmsRequestSyncGroup.markSyncReady();
@@ -4688,7 +4687,8 @@
return false;
}
- final boolean fullRedrawNeeded = mFullRedrawNeeded || surfaceSyncGroup != null;
+ final boolean fullRedrawNeeded =
+ mFullRedrawNeeded || surfaceSyncGroup != null || mHasPendingTransactions;
mFullRedrawNeeded = false;
mIsDrawing = true;
@@ -4718,8 +4718,15 @@
mAttachInfo.mPendingAnimatingRenderNodes.clear();
}
- if (mReportNextDraw) {
+ final Transaction pendingTransaction;
+ if (!usingAsyncReport && mHasPendingTransactions) {
+ pendingTransaction = new Transaction();
+ pendingTransaction.merge(mPendingTransaction);
+ } else {
+ pendingTransaction = null;
+ }
+ if (mReportNextDraw) {
// if we're using multi-thread renderer, wait for the window frame draws
if (mWindowDrawCountDown != null) {
try {
@@ -4741,9 +4748,7 @@
if (mSurfaceHolder != null && mSurface.isValid()) {
usingAsyncReport = true;
SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> {
- if (surfaceSyncGroup != null) {
- surfaceSyncGroup.markSyncReady();
- }
+ handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction);
});
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
@@ -4756,15 +4761,27 @@
}
}
- if (surfaceSyncGroup != null && !usingAsyncReport) {
- surfaceSyncGroup.markSyncReady();
+ if (!usingAsyncReport) {
+ handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction);
}
+
if (mPerformContentCapture) {
performContentCaptureInitialReport();
}
return true;
}
+ private void handleSyncRequestWhenNoAsyncDraw(SurfaceSyncGroup surfaceSyncGroup,
+ @Nullable Transaction pendingTransaction) {
+ if (surfaceSyncGroup != null) {
+ if (pendingTransaction != null) {
+ surfaceSyncGroup.addTransaction(pendingTransaction);
+ }
+ surfaceSyncGroup.markSyncReady();
+ } else if (pendingTransaction != null) {
+ pendingTransaction.apply();
+ }
+ }
/**
* Checks (and caches) if content capture is enabled for this context.
*/
@@ -4850,8 +4867,8 @@
}
}
- private boolean draw(boolean fullRedrawNeeded,
- @Nullable SurfaceSyncGroup activeSyncGroup, boolean syncBuffer) {
+ private boolean draw(boolean fullRedrawNeeded, @Nullable SurfaceSyncGroup activeSyncGroup,
+ boolean syncBuffer) {
Surface surface = mSurface;
if (!surface.isValid()) {
return false;
@@ -4995,12 +5012,11 @@
mAttachInfo.mThreadedRenderer.forceDrawNextFrame();
}
} else if (mHasPendingTransactions) {
- // Register a calback if there's no sync involved but there were calls to
+ // Register a callback if there's no sync involved but there were calls to
// applyTransactionOnDraw. If there is a sync involved, the sync callback will
// handle merging the pending transaction.
registerCallbackForPendingTransactions();
}
- mHasPendingTransactions = false;
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
@@ -8977,13 +8993,7 @@
mAdded = false;
AnimationHandler.removeRequestor(this);
}
- if (mActiveSurfaceSyncGroup != null) {
- mActiveSurfaceSyncGroup.markSyncReady();
- mActiveSurfaceSyncGroup = null;
- }
- if (mHasPendingTransactions) {
- mPendingTransaction.apply();
- }
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
WindowManagerGlobal.getInstance().doRemoveView(this);
}
@@ -11502,9 +11512,7 @@
Log.d(mTag, "registerCallbacksForSync syncBuffer=" + syncBuffer);
}
- Transaction t = new Transaction();
- t.merge(mPendingTransaction);
-
+ surfaceSyncGroup.addTransaction(mPendingTransaction);
mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
@Override
public void onFrameDraw(long frame) {
@@ -11518,7 +11526,6 @@
+ frame + ".");
}
- mergeWithNextTransaction(t, frame);
// If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
// SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
// any blast sync or commit callback, and the code should directly call
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index 9b10a7f..932608a3 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -16,6 +16,8 @@
package android.window;
+import static android.view.WindowManager.transitTypeToString;
+
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
@@ -88,6 +90,11 @@
this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags);
}
+ /** @hide */
+ String typeToString() {
+ return transitTypeToString(mType);
+ }
+
/** Requested change to a display. */
@DataClass(genToString = true, genSetters = true, genBuilder = false, genConstructor = false)
public static class DisplayChange implements Parcelable {
@@ -263,7 +270,7 @@
};
@DataClass.Generated(
- time = 1693425051905L,
+ time = 1695667226050L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nprivate boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -298,11 +305,11 @@
* @param type
* The type of the transition being requested.
* @param triggerTask
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+ * If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
* @param pipTask
- * If non-null, If non-null, the task containing the activity whose lifecycle change (start or
- * finish) has caused this transition to occur.
+ * If non-null, the task containing the pip activity that participates in this
+ * transition.
* @param remoteTransition
* If non-null, a remote-transition associated with the source of this transition.
* @param displayChange
@@ -431,7 +438,7 @@
// String fieldNameToString() { ... }
return "TransitionRequestInfo { " +
- "type = " + mType + ", " +
+ "type = " + typeToString() + ", " +
"triggerTask = " + mTriggerTask + ", " +
"pipTask = " + mPipTask + ", " +
"remoteTransition = " + mRemoteTransition + ", " +
@@ -506,10 +513,10 @@
};
@DataClass.Generated(
- time = 1693425051928L,
+ time = 1695667226088L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
- inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\n java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 0d1871d..663067c 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -203,14 +203,22 @@
}
return false;
}
-
+
+ /**
+ * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED
+ * Intent.ACTION_PACKAGE_CHANGED} being received, this callback
+ * has extras passed in.
+ */
+ public void onPackageChangedWithExtras(String packageName, Bundle extras) {
+ }
+
public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
return false;
}
public void onHandleUserStop(Intent intent, int userHandle) {
}
-
+
public void onUidRemoved(int uid) {
}
@@ -238,21 +246,34 @@
}
/**
+ * Called when a package disappears with extras passed in.
+ */
+ public void onPackageDisappearedWithExtras(String packageName, Bundle extras) {
+ }
+
+ /**
* Called when a package appears for any reason.
*/
public void onPackageAppeared(String packageName, int reason) {
}
+
+ /**
+ * Called when a package appears with extras passed in.
+ */
+ public void onPackageAppearedWithExtras(String packageName, Bundle extras) {
+ }
+
/**
* Called when an existing package is updated or its disabled state changes.
*/
public void onPackageModified(@NonNull String packageName) {
}
-
+
public boolean didSomePackagesChange() {
return mSomePackagesChanged;
}
-
+
public int isPackageAppearing(String packageName) {
if (mAppearingPackages != null) {
for (int i=mAppearingPackages.length-1; i>=0; i--) {
@@ -381,6 +402,7 @@
mChangeType = PACKAGE_PERMANENT_CHANGE;
onPackageAdded(pkg, uid);
}
+ onPackageAppearedWithExtras(pkg, intent.getExtras());
onPackageAppeared(pkg, mChangeType);
}
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
@@ -403,6 +425,7 @@
onPackageRemovedAllUsers(pkg, uid);
}
}
+ onPackageDisappearedWithExtras(pkg, intent.getExtras());
onPackageDisappeared(pkg, mChangeType);
}
} else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
@@ -417,6 +440,7 @@
if (onPackageChanged(pkg, uid, mModifiedComponents)) {
mSomePackagesChanged = true;
}
+ onPackageChangedWithExtras(pkg, intent.getExtras());
onPackageModified(pkg);
}
} else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index 058c6ec..6e45796 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -105,23 +105,23 @@
/**
* Enables or disables rotation lock from the system UI toggle.
*/
- public static void setRotationLock(Context context, final boolean enabled) {
+ public static void setRotationLock(Context context, final boolean enabled, String caller) {
final int rotation = areAllRotationsAllowed(context)
|| useCurrentRotationOnRotationLockChange(context) ? CURRENT_ROTATION
: NATURAL_ROTATION;
- setRotationLockAtAngle(context, enabled, rotation);
+ setRotationLockAtAngle(context, enabled, rotation, caller);
}
/**
* Enables or disables rotation lock at a specific rotation from system UI.
*/
public static void setRotationLockAtAngle(Context context, final boolean enabled,
- final int rotation) {
+ final int rotation, String caller) {
Settings.System.putIntForUser(context.getContentResolver(),
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
UserHandle.USER_CURRENT);
- setRotationLock(enabled, rotation);
+ setRotationLock(enabled, rotation, caller);
}
/**
@@ -129,12 +129,13 @@
*
* If rotation is locked for accessibility, the system UI toggle is hidden to avoid confusion.
*/
- public static void setRotationLockForAccessibility(Context context, final boolean enabled) {
+ public static void setRotationLockForAccessibility(Context context, final boolean enabled,
+ String caller) {
Settings.System.putIntForUser(context.getContentResolver(),
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
- setRotationLock(enabled, NATURAL_ROTATION);
+ setRotationLock(enabled, NATURAL_ROTATION, caller);
}
private static boolean areAllRotationsAllowed(Context context) {
@@ -146,16 +147,17 @@
R.bool.config_useCurrentRotationOnRotationLockChange);
}
- private static void setRotationLock(final boolean enabled, final int rotation) {
+ private static void setRotationLock(final boolean enabled, final int rotation,
+ final String caller) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
if (enabled) {
- wm.freezeRotation(rotation);
+ wm.freezeRotation(rotation, caller);
} else {
- wm.thawRotation();
+ wm.thawRotation(caller);
}
} catch (RemoteException exc) {
Log.w(TAG, "Unable to save auto-rotate setting");
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index ad196c0..9ed4155 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -15,7 +15,19 @@
],
}
-cc_library_shared {
+soong_config_module_type {
+ name: "cc_library_shared_for_libandroid_runtime",
+ module_type: "cc_library_shared",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "release_binder_death_recipient_weak_from_jni",
+ ],
+ properties: [
+ "cflags",
+ ],
+}
+
+cc_library_shared_for_libandroid_runtime {
name: "libandroid_runtime",
host_supported: true,
cflags: [
@@ -46,6 +58,12 @@
},
},
+ soong_config_variables: {
+ release_binder_death_recipient_weak_from_jni: {
+ cflags: ["-DBINDER_DEATH_RECIPIENT_WEAK_FROM_JNI"],
+ },
+ },
+
cpp_std: "gnu++20",
srcs: [
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 041f9c7..bfd80a9e 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -17,19 +17,8 @@
#define LOG_TAG "JavaBinder"
//#define LOG_NDEBUG 0
-#include "android_os_Parcel.h"
#include "android_util_Binder.h"
-#include <atomic>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <mutex>
-#include <stdio.h>
-#include <string>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
#include <android-base/stringprintf.h>
#include <binder/BpBinder.h>
#include <binder/IInterface.h>
@@ -40,7 +29,16 @@
#include <binder/Stability.h>
#include <binderthreadstate/CallerUtils.h>
#include <cutils/atomic.h>
+#include <fcntl.h>
+#include <inttypes.h>
#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <utils/KeyedVector.h>
#include <utils/List.h>
#include <utils/Log.h>
@@ -48,10 +46,11 @@
#include <utils/SystemClock.h>
#include <utils/threads.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include <nativehelper/ScopedUtfChars.h>
+#include <atomic>
+#include <mutex>
+#include <string>
+#include "android_os_Parcel.h"
#include "core_jni_helpers.h"
//#undef ALOGV
@@ -553,14 +552,48 @@
};
// ----------------------------------------------------------------------------
+#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
+#if __BIONIC__
+#include <android/api-level.h>
+static bool target_sdk_is_at_least_vic() {
+ return android_get_application_target_sdk_version() >= __ANDROID_API_V__;
+}
+#else
+static constexpr bool target_sdk_is_at_least_vic() {
+ // If not built for Android (i.e. glibc host), follow the latest behavior as there's no compat
+ // requirement there.
+ return true;
+}
+#endif // __BIONIC__
+#endif // BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
class JavaDeathRecipient : public IBinder::DeathRecipient
{
public:
JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
- : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
- mObjectWeak(NULL), mList(list)
- {
+ : mVM(jnienv_to_javavm(env)), mObject(NULL), mObjectWeak(NULL), mList(list) {
+ // b/298374304: For apps targeting Android V or beyond, we no longer hold the global JNI ref
+ // to the death recipient objects. This is to prevent the memory leak which can happen when
+ // the death recipient object internally has a strong reference to the proxy object. Under
+ // the old behavior, you were unable to kill the binder service by dropping all references
+ // to the proxy object - because it is still strong referenced from JNI (here). The only way
+ // to cut the strong reference was to call unlinkDeath(), but it was easy to forget.
+ //
+ // Now, the strong reference to the death recipient is held in the Java-side proxy object.
+ // See BinderProxy.mDeathRecipients. From JNI, only the weak reference is kept. An
+ // implication of this is that you may not receive binderDied() if you drop all references
+ // to the proxy object before the service dies. This should be okay for most cases because
+ // you normally are not interested in the death of a binder service which you don't have any
+ // reference to. If however you want to get binderDied() regardless of the proxy object's
+ // lifecycle, keep a strong reference to the death recipient object by yourself.
+#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
+ if (target_sdk_is_at_least_vic()) {
+ mObjectWeak = env->NewWeakGlobalRef(object);
+ } else
+#endif
+ {
+ mObject = env->NewGlobalRef(object);
+ }
// These objects manage their own lifetimes so are responsible for final bookkeeping.
// The list holds a strong reference to this object.
LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
@@ -573,26 +606,49 @@
void binderDied(const wp<IBinder>& who)
{
LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
- if (mObject != NULL) {
- JNIEnv* env = javavm_to_jnienv(mVM);
- ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
- env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
- gBinderProxyOffsets.mSendDeathNotice, mObject,
- jBinderProxy.get());
- if (env->ExceptionCheck()) {
- jthrowable excep = env->ExceptionOccurred();
- binder_report_exception(env, excep,
- "*** Uncaught exception returned from death notification!");
- }
+ if (mObject == NULL && mObjectWeak == NULL) {
+ return;
+ }
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
- // Serialize with our containing DeathRecipientList so that we can't
- // delete the global ref on mObject while the list is being iterated.
+ // Hold a local reference to the recipient. This may fail if the recipient is weakly
+ // referenced, in which case we can't deliver the death notice.
+ ScopedLocalRef<jobject> jRecipient(env,
+ env->NewLocalRef(mObject != NULL ? mObject
+ : mObjectWeak));
+ if (jRecipient.get() == NULL) {
+ ALOGW("Binder died, but death recipient is already garbage collected. If your target "
+ "sdk level is at or above 35, this can happen when you dropped all references to "
+ "the binder service before it died. If you want to get a death notice for a "
+ "binder service which you have no reference to, keep a strong reference to the "
+ "death recipient by yourself.");
+ return;
+ }
+
+ if (mFired) {
+ ALOGW("Received multiple death notices for the same binder object. Binder driver bug?");
+ return;
+ }
+ mFired = true;
+
+ env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mSendDeathNotice,
+ jRecipient.get(), jBinderProxy.get());
+ if (env->ExceptionCheck()) {
+ jthrowable excep = env->ExceptionOccurred();
+ binder_report_exception(env, excep,
+ "*** Uncaught exception returned from death notification!");
+ }
+
+ // Demote from strong ref (if exists) to weak after binderDied() has been delivered, to
+ // allow the DeathRecipient and BinderProxy to be GC'd if no longer needed. Do this in sync
+ // with our containing DeathRecipientList so that we can't delete the global ref on mObject
+ // while the list is being iterated.
+ if (mObject != NULL) {
sp<DeathRecipientList> list = mList.promote();
if (list != NULL) {
AutoMutex _l(list->lock());
- // Demote from strong ref to weak after binderDied() has been delivered,
- // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed.
mObjectWeak = env->NewWeakGlobalRef(mObject);
env->DeleteGlobalRef(mObject);
mObject = NULL;
@@ -659,9 +715,19 @@
private:
JavaVM* const mVM;
- jobject mObject; // Initial strong ref to Java-side DeathRecipient. Cleared on binderDied().
- jweak mObjectWeak; // Weak ref to the same Java-side DeathRecipient after binderDied().
+
+ // If target sdk version < 35, the Java-side DeathRecipient is strongly referenced from mObject
+ // upon linkToDeath() and then after binderDied() is called, the strong reference is demoted to
+ // a weak reference (mObjectWeak).
+ // If target sdk version >= 35, the strong reference is never made here (i.e. mObject == NULL
+ // always). Instead, the strong reference to the Java-side DeathRecipient is made in
+ // BinderProxy.mDeathRecipients. In the native world, only the weak reference is kept.
+ jobject mObject;
+ jweak mObjectWeak;
wp<DeathRecipientList> mList;
+
+ // Whether binderDied was called or not.
+ bool mFired = false;
};
// ----------------------------------------------------------------------------
@@ -1435,17 +1501,19 @@
// ----------------------------------------------------------------------------
+// clang-format off
static const JNINativeMethod gBinderProxyMethods[] = {
/* name, signature, funcPtr */
{"pingBinder", "()Z", (void*)android_os_BinderProxy_pingBinder},
{"isBinderAlive", "()Z", (void*)android_os_BinderProxy_isBinderAlive},
{"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
{"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
- {"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
- {"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
+ {"linkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
+ {"unlinkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
{"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension},
};
+// clang-format on
const char* const kBinderProxyPathName = "android/os/BinderProxy";
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 69fc515..41c65ae 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -38,6 +38,7 @@
jmethodID dispatchVsync;
jmethodID dispatchHotplug;
+ jmethodID dispatchHotplugConnectionError;
jmethodID dispatchModeChanged;
jmethodID dispatchFrameRateOverrides;
@@ -89,6 +90,7 @@
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
VsyncEventData vsyncEventData) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+ void dispatchHotplugConnectionError(nsecs_t timestamp, int errorCode) override;
void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
nsecs_t renderPeriod) override;
void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
@@ -230,6 +232,22 @@
mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
}
+void NativeDisplayEventReceiver::dispatchHotplugConnectionError(nsecs_t timestamp,
+ int connectionError) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
+ if (receiverObj.get()) {
+ ALOGV("receiver %p ~ Invoking hotplug dispatchHotplugConnectionError handler.", this);
+ env->CallVoidMethod(receiverObj.get(),
+ gDisplayEventReceiverClassInfo.dispatchHotplugConnectionError,
+ timestamp, connectionError);
+ ALOGV("receiver %p ~ Returned from hotplug dispatchHotplugConnectionError handler.", this);
+ }
+
+ mMessageQueue->raiseAndClearException(env, "dispatchHotplugConnectionError");
+}
+
void NativeDisplayEventReceiver::dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
int32_t modeId, nsecs_t renderPeriod) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -354,8 +372,12 @@
gDisplayEventReceiverClassInfo.dispatchVsync =
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
- gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
- gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
+ gDisplayEventReceiverClassInfo.dispatchHotplug =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug",
+ "(JJZ)V");
+ gDisplayEventReceiverClassInfo.dispatchHotplugConnectionError =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
+ "dispatchHotplugConnectionError", "(JI)V");
gDisplayEventReceiverClassInfo.dispatchModeChanged =
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged",
"(JJIJ)V");
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3d0af3d..c00a776f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5227,6 +5227,28 @@
non-zero. -->
<integer name="config_defaultPeakRefreshRate">0</integer>
+ <!-- External display peak refresh rate for the given device. Change this value if you want to
+ prevent the framework from using higher refresh rates, even if display modes with higher
+ refresh rates are available from hardware composer. Only has an effect if this value and
+ config_externalDisplayPeakWidth and config_externalDisplayPeakHeight are non-zero. -->
+ <integer name="config_externalDisplayPeakRefreshRate">0</integer>
+
+ <!-- External display peak width for the given device. Change this value if you want
+ to prevent the framework from using higher resolution, even if display modes with higher
+ resolutions are available from hardware composer. Only has an effect if this value and
+ config_externalDisplayPeakRefreshRate and config_externalDisplayPeakHeight are non-zero.-->
+ <integer name="config_externalDisplayPeakWidth">0</integer>
+
+ <!-- External display peak height for the given device. Change this value if you want
+ to prevent the framework from using higher resolution, even if display modes with higher
+ resolutions are available from hardware composer. Only has an effect if this value and
+ config_externalDisplayPeakRefreshRate and config_externalDisplayPeakWidth are non-zero. -->
+ <integer name="config_externalDisplayPeakHeight">0</integer>
+
+ <!-- Enable synchronization of the displays refresh rates by applying the default low refresh
+ rate. -->
+ <bool name="config_refreshRateSynchronizationEnabled">false</bool>
+
<!-- The display uses different gamma curves for different refresh rates. It's hard for panel
vendors to tune the curves to have exact same brightness for different refresh rate. So
flicker could be observed at switch time. The issue is worse at the gamma lower end.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 83fb098..b0eee1c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4231,6 +4231,10 @@
<!-- For high refresh rate displays -->
<java-symbol type="integer" name="config_defaultRefreshRate" />
<java-symbol type="integer" name="config_defaultPeakRefreshRate" />
+ <java-symbol type="integer" name="config_externalDisplayPeakRefreshRate" />
+ <java-symbol type="integer" name="config_externalDisplayPeakWidth" />
+ <java-symbol type="integer" name="config_externalDisplayPeakHeight" />
+ <java-symbol type="bool" name="config_refreshRateSynchronizationEnabled" />
<java-symbol type="integer" name="config_defaultRefreshRateInZone" />
<java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
<java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
index e082c25..c7eddabe 100644
--- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
+++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
@@ -17,7 +17,6 @@
package com.android.internal.content;
import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -28,6 +27,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
@@ -36,6 +36,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -45,6 +46,7 @@
@RunWith(AndroidJUnit4.class)
public class PackageMonitorTest {
private static final String FAKE_PACKAGE_NAME = "com.android.internal.content.fakeapp";
+ private static final String FAKE_EXTRA_REASON = "android.intent.extra.fakereason";
private static final int FAKE_PACKAGE_UID = 123;
private static final int FAKE_USER_ID = 0;
private static final int WAIT_CALLBACK_CALLED_IN_MS = 300;
@@ -245,6 +247,7 @@
intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ intent.putExtra(Intent.EXTRA_REASON, FAKE_EXTRA_REASON);
String [] packageList = new String[]{FAKE_PACKAGE_NAME};
intent.putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, packageList);
spyPackageMonitor.doHandlePackageEvent(intent);
@@ -253,6 +256,20 @@
verify(spyPackageMonitor, times(1))
.onPackageChanged(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID), eq(packageList));
verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageChangedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REASON))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REASON));
+
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
}
@@ -272,6 +289,21 @@
verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
verify(spyPackageMonitor, times(1))
.onPackageUpdateStarted(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS));
+
verify(spyPackageMonitor, times(1))
.onPackageDisappeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
@@ -295,6 +327,21 @@
.onPackageRemoved(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
verify(spyPackageMonitor, times(1))
.onPackageRemovedAllUsers(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS));
+
verify(spyPackageMonitor, times(1)).onPackageDisappeared(eq(FAKE_PACKAGE_NAME),
eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE));
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
@@ -316,6 +363,19 @@
verify(spyPackageMonitor, times(1))
.onPackageUpdateFinished(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageAppearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+
verify(spyPackageMonitor, times(1))
.onPackageAppeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
@@ -336,6 +396,19 @@
verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
verify(spyPackageMonitor, times(1))
.onPackageAdded(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1)).onPackageAppearedWithExtras(eq(FAKE_PACKAGE_NAME),
+ argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+
verify(spyPackageMonitor, times(1)).onPackageAppeared(eq(FAKE_PACKAGE_NAME),
eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE));
verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ad0ead7..9c65287 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1123,12 +1123,6 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowSurfaceController.java"
},
- "-1076978367": {
- "message": "thawRotation: mRotation=%d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"-1075136930": {
"message": "startLockTaskMode: Can't lock due to auth",
"level": "WARN",
@@ -1231,6 +1225,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-962760979": {
+ "message": "thawRotation: mRotation=%d, caller=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"-961053385": {
"message": "attachWindowContextToDisplayArea: calling from non-existing process pid=%d uid=%d",
"level": "WARN",
@@ -2779,6 +2779,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "364992694": {
+ "message": "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"371173718": {
"message": "finishSync cancel=%b for %s",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 3e64579..78d257f 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -49,11 +49,11 @@
* possible antialiasing logic for border pixels).</li>
* <li>Logic for the {@link Shader}, {@link ColorFilter}, and {@link BlendMode} on the
* {@link Paint}.</li>
- * <li>Color space conversion code, as part of Android’s color management.</li>
+ * <li>Color space conversion code, as part of Android's color management.</li>
* </ul>
*
* <p>A {@link RuntimeShader}, like other {@link Shader} types, effectively contributes a function
- * to the GPU’s fragment shader.</p>
+ * to the GPU's fragment shader.</p>
*
* <h3>AGSL Shader Execution</h3>
* <p>Just like a GLSL shader, an AGSL shader begins execution in a main function. Unlike GLSL, the
@@ -78,10 +78,10 @@
* {@link ColorSpace} for an AGSL shader is defined to be the color space of the destination, which
* in most cases is determined by {@link Window#setColorMode(int)}.</p>
*
- * <p>When authoring an AGSL shader, you won’t know what the working color space is. For many
+ * <p>When authoring an AGSL shader, you won't know what the working color space is. For many
* effects, this is fine because by default color inputs are automatically converted into the
* working color space. For certain effects, it may be important to do some math in a fixed, known
- * color space. A common example is lighting – to get physically accurate lighting, math should be
+ * color space. A common example is lighting - to get physically accurate lighting, math should be
* done in a linear color space. To help with this, AGSL provides two intrinsic functions that
* convert colors between the working color space and the
* {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB} color space:
@@ -93,7 +93,7 @@
* <h3>AGSL and Premultiplied Alpha</h3>
* <p>When dealing with transparent colors, there are two (common) possible representations:
* straight (unassociated) alpha and premultiplied (associated) alpha. In ASGL the color returned
- * by the main function is expected to be premultiplied. AGSL’s use of premultiplied alpha
+ * by the main function is expected to be premultiplied. AGSL's use of premultiplied alpha
* implies:
* </p>
*
@@ -101,7 +101,7 @@
* <li>If your AGSL shader will return transparent colors, be sure to multiply the RGB by A. The
* resulting color should be [R*A, G*A, B*A, A], not [R, G, B, A].</li>
* <li>For more complex shaders, you must understand which of your colors are premultiplied vs.
- * straight. Many operations don’t make sense if you mix both kinds of color together.</li>
+ * straight. Many operations don't make sense if you mix both kinds of color together.</li>
* </ul>
*
* <h3>Uniforms</h3>
@@ -224,7 +224,7 @@
* shader uniform is undefined if it is declared in the AGSL shader but not initialized.</p>
*
* <p>Although most {@link BitmapShader}s contain colors that should be color managed, some contain
- * data that isn’t actually colors. This includes bitmaps storing normals, material properties
+ * data that isn't actually colors. This includes bitmaps storing normals, material properties
* (e.g. roughness), heightmaps, or any other purely mathematical data that happens to be stored in
* a bitmap. When using these kinds of shaders in AGSL, you probably want to initialize them with
* {@link #setInputBuffer(String, BitmapShader)}. Shaders initialized this way work much like
@@ -237,7 +237,7 @@
*
* <p>In addition, when sampling from a {@link BitmapShader} be aware that the shader does not use
* normalized coordinates (like a texture in GLSL). It uses (0, 0) in the upper-left corner, and
- * (width, height) in the bottom-right corner. Normally, this is exactly what you want. If you’re
+ * (width, height) in the bottom-right corner. Normally, this is exactly what you want. If you're
* evaluating the shader with coordinates based on the ones passed to your AGSL program, the scale
* is correct. However, if you want to adjust those coordinates (to do some kind of re-mapping of
* the bitmap), remember that the coordinates are local to the canvas.</p>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
index 5eeb3b6..b141beb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
@@ -18,12 +18,16 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.IdRes;
import android.annotation.NonNull;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -36,14 +40,29 @@
*/
public class UserAspectRatioSettingsLayout extends LinearLayout {
+ private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+
+ private static final Interpolator PATH_INTERPOLATOR =
+ new PathInterpolator(0.2f, 0f, 0f, 1f);
+
private static final float ALPHA_FULL_TRANSPARENT = 0f;
private static final float ALPHA_FULL_OPAQUE = 1f;
- private static final long VISIBILITY_ANIMATION_DURATION_MS = 50;
+ private static final float SCALE_START = 0.8f;
+
+ private static final float SCALE_END = 1f;
+
+ private static final long FADE_ANIMATION_DURATION_MS = 167;
+
+ private static final long SCALE_ANIMATION_DURATION_MS = 300;
private static final String ALPHA_PROPERTY_NAME = "alpha";
+ private static final String SCALE_X_PROPERTY_NAME = "scaleX";
+
+ private static final String SCALE_Y_PROPERTY_NAME = "scaleY";
+
private UserAspectRatioSettingsWindowManager mWindowManager;
public UserAspectRatioSettingsLayout(Context context) {
@@ -88,7 +107,7 @@
if (show) {
showItem(view);
} else {
- view.setVisibility(visibility);
+ hideItem(view);
}
}
@@ -121,16 +140,40 @@
}
private void showItem(@NonNull View view) {
- view.setVisibility(View.VISIBLE);
+ final AnimatorSet animatorSet = new AnimatorSet();
final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME,
ALPHA_FULL_TRANSPARENT, ALPHA_FULL_OPAQUE);
- fadeIn.setDuration(VISIBILITY_ANIMATION_DURATION_MS);
- fadeIn.addListener(new AnimatorListenerAdapter() {
+ fadeIn.setDuration(FADE_ANIMATION_DURATION_MS);
+ fadeIn.setInterpolator(LINEAR_INTERPOLATOR);
+ final ObjectAnimator scaleY =
+ ObjectAnimator.ofFloat(view, SCALE_Y_PROPERTY_NAME, SCALE_START, SCALE_END);
+ final ObjectAnimator scaleX =
+ ObjectAnimator.ofFloat(view, SCALE_X_PROPERTY_NAME, SCALE_START, SCALE_END);
+ scaleX.setDuration(SCALE_ANIMATION_DURATION_MS);
+ scaleX.setInterpolator(PATH_INTERPOLATOR);
+ scaleY.setDuration(SCALE_ANIMATION_DURATION_MS);
+ scaleY.setInterpolator(PATH_INTERPOLATOR);
+ animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationEnd(Animator animation) {
+ public void onAnimationStart(Animator animation) {
view.setVisibility(View.VISIBLE);
}
});
- fadeIn.start();
+ animatorSet.playTogether(fadeIn, scaleY, scaleX);
+ animatorSet.start();
+ }
+
+ private void hideItem(@NonNull View view) {
+ final ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME,
+ ALPHA_FULL_OPAQUE, ALPHA_FULL_TRANSPARENT);
+ fadeOut.setDuration(FADE_ANIMATION_DURATION_MS);
+ fadeOut.setInterpolator(LINEAR_INTERPOLATOR);
+ fadeOut.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setVisibility(View.GONE);
+ }
+ });
+ fadeOut.start();
}
}
diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h
index 764d1ef..69fda34 100644
--- a/libs/hwui/Mesh.h
+++ b/libs/hwui/Mesh.h
@@ -166,11 +166,12 @@
#endif
mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
ib, mIndexCount, mIndexOffset, mBuilder->fUniforms,
- mBounds)
+ SkSpan<SkRuntimeEffect::ChildPtr>(), mBounds)
.mesh;
} else {
mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
- mBuilder->fUniforms, mBounds)
+ mBuilder->fUniforms, SkSpan<SkRuntimeEffect::ChildPtr>(),
+ mBounds)
.mesh;
}
mIsDirty = false;
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 71f47e9..ff0d8d74 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -539,7 +539,7 @@
if (!cpuMesh.indexBuffer()) {
gpuMesh = SkMesh::Make(cpuMesh.refSpec(), cpuMesh.mode(), vb, cpuMesh.vertexCount(),
cpuMesh.vertexOffset(), cpuMesh.refUniforms(),
- cpuMesh.bounds())
+ SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds())
.mesh;
} else {
sk_sp<SkMesh::IndexBuffer> ib =
@@ -547,7 +547,8 @@
gpuMesh = SkMesh::MakeIndexed(cpuMesh.refSpec(), cpuMesh.mode(), vb,
cpuMesh.vertexCount(), cpuMesh.vertexOffset(), ib,
cpuMesh.indexCount(), cpuMesh.indexOffset(),
- cpuMesh.refUniforms(), cpuMesh.bounds())
+ cpuMesh.refUniforms(),
+ SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds())
.mesh;
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 9924fae..09f09b9 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -2409,13 +2409,16 @@
@RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER)
@Nullable
public Descrambler openDescrambler() {
+ acquireTRMSLock("openDescrambler()");
mDemuxLock.lock();
try {
- if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
+ // no need to unlock mDemuxLock (so pass null instead) as TRMS lock is already acquired
+ if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, null)) {
return null;
}
return requestDescrambler();
} finally {
+ releaseTRMSLock();
mDemuxLock.unlock();
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 943c2b4..ba88484 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -16,23 +16,183 @@
package com.android.credentialmanager.autofill
+import android.app.assist.AssistStructure
+import android.content.Context
+import android.credentials.GetCredentialRequest
+import android.credentials.CredentialManager
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.CredentialOption
+import android.credentials.GetCandidateCredentialsException
+import android.os.Bundle
import android.os.CancellationSignal
-import android.service.autofill.AutofillService
-import android.service.autofill.FillCallback
+import android.os.OutcomeReceiver
import android.service.autofill.FillRequest
+import android.service.autofill.AutofillService
+import android.service.autofill.FillResponse
+import android.service.autofill.FillCallback
import android.service.autofill.SaveRequest
import android.service.autofill.SaveCallback
+import android.util.Log
+import org.json.JSONObject
+import java.util.concurrent.Executors
class CredentialAutofillService : AutofillService() {
+
+ companion object {
+ private const val TAG = "CredAutofill"
+
+ private const val CRED_HINT_PREFIX = "credential="
+ private const val REQUEST_DATA_KEY = "requestData"
+ private const val CANDIDATE_DATA_KEY = "candidateQueryData"
+ private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired"
+ private const val CRED_OPTIONS_KEY = "credentialOptions"
+ private const val TYPE_KEY = "type"
+ }
+
+ private val credentialManager: CredentialManager =
+ getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager
+
override fun onFillRequest(
request: FillRequest,
cancellationSignal: CancellationSignal,
callback: FillCallback
) {
+ val context = request.fillContexts
+ val structure = context[context.size - 1].structure
+ val callingPackage = structure.activityComponent.packageName
+ Log.i(TAG, "onFillRequest called for $callingPackage")
+
+ val getCredRequest: GetCredentialRequest? = getCredManRequest(structure)
+ if (getCredRequest == null) {
+ callback.onFailure("No credential manager request found")
+ return
+ }
+
+ val outcome = object : OutcomeReceiver<GetCandidateCredentialsResponse,
+ GetCandidateCredentialsException> {
+ override fun onResult(result: GetCandidateCredentialsResponse) {
+ Log.i(TAG, "getCandidateCredentials onResponse")
+ val fillResponse: FillResponse? = convertToFillResponse(result, request)
+ callback.onSuccess(fillResponse)
+ }
+
+ override fun onError(error: GetCandidateCredentialsException) {
+ Log.i(TAG, "getCandidateCredentials onError")
+ callback.onFailure("error received from credential manager ${error.message}")
+ }
+ }
+
+ credentialManager.getCandidateCredentials(
+ getCredRequest,
+ callingPackage,
+ CancellationSignal(),
+ Executors.newSingleThreadExecutor(),
+ outcome
+ )
+ }
+
+ private fun convertToFillResponse(
+ getCredResponse: GetCandidateCredentialsResponse,
+ filLRequest: FillRequest
+ ): FillResponse? {
TODO("Not yet implemented")
}
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
TODO("Not yet implemented")
}
+
+ private fun getCredManRequest(structure: AssistStructure): GetCredentialRequest? {
+ val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+ traverseStructure(structure, credentialOptions)
+
+ if (credentialOptions.isNotEmpty()) {
+ return GetCredentialRequest.Builder(Bundle.EMPTY)
+ .setCredentialOptions(credentialOptions)
+ .build()
+ }
+ return null
+ }
+
+ private fun traverseStructure(
+ structure: AssistStructure,
+ cmRequests: MutableList<CredentialOption>
+ ) {
+ val windowNodes: List<AssistStructure.WindowNode> =
+ structure.run {
+ (0 until windowNodeCount).map { getWindowNodeAt(it) }
+ }
+
+ windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
+ traverseNode(windowNode.rootViewNode, cmRequests)
+ }
+ }
+
+ private fun traverseNode(
+ viewNode: AssistStructure.ViewNode?,
+ cmRequests: MutableList<CredentialOption>
+ ) {
+ val options = getCredentialOptionsFromViewNode(viewNode)
+ cmRequests.addAll(options)
+
+ val children: List<AssistStructure.ViewNode>? =
+ viewNode?.run {
+ (0 until childCount).map { getChildAt(it) }
+ }
+
+ children?.forEach { childNode: AssistStructure.ViewNode ->
+ traverseNode(childNode, cmRequests)
+ }
+ }
+
+ private fun getCredentialOptionsFromViewNode(viewNode: AssistStructure.ViewNode?):
+ List<CredentialOption> {
+ // TODO(b/293945193) Replace with isCredential check from viewNode
+ val credentialHints: MutableList<String> = mutableListOf()
+ if (viewNode != null && viewNode.autofillHints != null) {
+ for (hint in viewNode.autofillHints!!) {
+ if (hint.startsWith(CRED_HINT_PREFIX)) {
+ credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX))
+ }
+ }
+ }
+
+ val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+ for (credentialHint in credentialHints) {
+ convertJsonToCredentialOption(credentialHint).let { credentialOptions.addAll(it) }
+ }
+ return credentialOptions
+ }
+
+ private fun convertJsonToCredentialOption(jsonString: String): List<CredentialOption> {
+ // TODO(b/302000646) Move this logic to jetpack so that is consistent
+ // with building the json
+ val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+
+ val json = JSONObject(jsonString)
+ val options = json.getJSONArray(CRED_OPTIONS_KEY)
+ for (i in 0 until options.length()) {
+ val option = options.getJSONObject(i)
+
+ credentialOptions.add(CredentialOption(
+ option.getString(TYPE_KEY),
+ convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)),
+ convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)),
+ option.getBoolean(SYS_PROVIDER_REQ_KEY),
+ ))
+ }
+ return credentialOptions
+ }
+
+ private fun convertJsonToBundle(json: JSONObject): Bundle {
+ val result = Bundle()
+ json.keys().forEach {
+ val v = json.get(it)
+ when (v) {
+ is String -> result.putString(it, v)
+ is Boolean -> result.putBoolean(it, v)
+ }
+ }
+ return result
+ }
}
\ No newline at end of file
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 5dcb9d2..2d231f2 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -71,7 +71,6 @@
"SettingsLibMainSwitchPreference",
"SettingsLibProfileSelector",
"SettingsLibProgressBar",
- "SettingsLibRadioButtonPreference",
"SettingsLibRestrictedLockUtils",
"SettingsLibSelectorWithWidgetPreference",
"SettingsLibSettingsSpinner",
diff --git a/packages/SettingsLib/RadioButtonPreference/Android.bp b/packages/SettingsLib/RadioButtonPreference/Android.bp
deleted file mode 100644
index 505ba05..0000000
--- a/packages/SettingsLib/RadioButtonPreference/Android.bp
+++ /dev/null
@@ -1,29 +0,0 @@
-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"],
-}
-
-android_library {
- name: "SettingsLibRadioButtonPreference",
- use_resource_processor: true,
-
- srcs: ["src/**/*.java"],
- resource_dirs: ["res"],
-
- static_libs: [
- "androidx.preference_preference",
- "SettingsLibSelectorWithWidgetPreference",
- "SettingsLibSettingsTheme",
- ],
-
- sdk_version: "system_current",
- min_sdk_version: "21",
- apex_available: [
- "//apex_available:platform",
- "com.android.permission",
- ],
-}
diff --git a/packages/SettingsLib/RadioButtonPreference/AndroidManifest.xml b/packages/SettingsLib/RadioButtonPreference/AndroidManifest.xml
deleted file mode 100644
index 8b5c3b1..0000000
--- a/packages/SettingsLib/RadioButtonPreference/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 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="com.android.settingslib.widget.preference.radio">
-
- <uses-sdk android:minSdkVersion="21" />
-
-</manifest>
diff --git a/packages/SettingsLib/RadioButtonPreference/res/drawable/ic_settings_accent.xml b/packages/SettingsLib/RadioButtonPreference/res/drawable/ic_settings_accent.xml
deleted file mode 100644
index 6521bc9..0000000
--- a/packages/SettingsLib/RadioButtonPreference/res/drawable/ic_settings_accent.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<!--
- Copyright (C) 2021 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.
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.9 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.71 14.59,22.25 13.85,22.25zM13.32,20.72c0,0.01 0,0.01 0,0.02L13.32,20.72zM10.68,20.7l0,0.02C10.69,20.72 10.69,20.71 10.68,20.7zM10.62,20.25h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96l-0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7 8.84,6.95 8.38,7.3L7.93,7.63L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47 6.06,11.74 6.06,12c0,0.26 0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.25zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.71l0.01,0.02C5.78,17.72 5.77,17.71 5.77,17.71zM3.93,9.47L3.93,9.47C3.93,9.47 3.93,9.47 3.93,9.47zM18.22,6.27c0,0.01 0.01,0.02 0.01,0.02L18.22,6.27zM5.79,6.25L5.78,6.27C5.78,6.27 5.79,6.26 5.79,6.25zM13.31,3.28c0,0.01 0,0.01 0,0.02L13.31,3.28zM10.69,3.26l0,0.02C10.69,3.27 10.69,3.27 10.69,3.26z"/>
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
deleted file mode 100644
index bb8ac28..0000000
--- a/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
- -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:settings="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
-
- <LinearLayout
- android:id="@android:id/widget_frame"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingHorizontal="20dp"
- android:gravity="center"
- android:minWidth="56dp"
- android:orientation="vertical"/>
-
- <LinearLayout
- android:id="@+id/icon_frame"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:minWidth="32dp"
- android:orientation="horizontal"
- android:layout_marginEnd="16dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp">
- <androidx.preference.internal.PreferenceImageView
- android:id="@android:id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- settings:maxWidth="@dimen/secondary_app_icon_size"
- settings:maxHeight="@dimen/secondary_app_icon_size"/>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:paddingTop="16dp"
- android:paddingBottom="16dp"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="2"
- android:hyphenationFrequency="normalFast"
- android:lineBreakWordStyle="phrase"
- android:textAppearance="?android:attr/textAppearanceListItem"/>
-
- <LinearLayout
- android:id="@+id/summary_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone">
- <TextView
- android:id="@android:id/summary"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAlignment="viewStart"
- android:hyphenationFrequency="normalFast"
- android:lineBreakWordStyle="phrase"
- android:textColor="?android:attr/textColorSecondary"/>
-
- <TextView
- android:id="@+id/appendix"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAlignment="viewEnd"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="1"
- android:visibility="gone"
- android:ellipsize="end"/>
- </LinearLayout>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/radio_extra_widget_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:gravity="center_vertical">
- <View
- android:layout_width=".75dp"
- android:layout_height="32dp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="16dp"
- android:background="?android:attr/dividerVertical" />
- <ImageView
- android:id="@+id/radio_extra_widget"
- android:layout_width="match_parent"
- android:minWidth="@dimen/two_target_min_width"
- android:layout_height="fill_parent"
- android:src="@drawable/ic_settings_accent"
- android:contentDescription="@string/settings_label"
- android:paddingStart="24dp"
- android:paddingEnd="24dp"
- android:layout_gravity="center"
- android:background="?android:attr/selectableItemBackground" />
- </LinearLayout>
-</LinearLayout>
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
deleted file mode 100644
index 906ff2c..0000000
--- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
+++ /dev/null
@@ -1,123 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 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.
- -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:settings="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
-
- <LinearLayout
- android:id="@android:id/widget_frame"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingHorizontal="20dp"
- android:gravity="center"
- android:minWidth="56dp"
- android:orientation="vertical"/>
-
- <LinearLayout
- android:id="@+id/icon_frame"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:minWidth="32dp"
- android:orientation="horizontal"
- android:layout_marginEnd="16dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp">
- <androidx.preference.internal.PreferenceImageView
- android:id="@android:id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- settings:maxWidth="@dimen/secondary_app_icon_size"
- settings:maxHeight="@dimen/secondary_app_icon_size"/>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:paddingTop="16dp"
- android:paddingBottom="16dp"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="2"
- android:textAppearance="?android:attr/textAppearanceListItem"/>
-
- <LinearLayout
- android:id="@+id/summary_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone">
- <TextView
- android:id="@android:id/summary"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAlignment="viewStart"
- android:textColor="?android:attr/textColorSecondary"/>
-
- <TextView
- android:id="@+id/appendix"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAlignment="viewEnd"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="1"
- android:visibility="gone"
- android:ellipsize="end"/>
- </LinearLayout>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/radio_extra_widget_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:gravity="center_vertical">
- <View
- android:layout_width=".75dp"
- android:layout_height="32dp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="16dp"
- android:background="?android:attr/dividerVertical" />
- <ImageView
- android:id="@+id/radio_extra_widget"
- android:layout_width="match_parent"
- android:minWidth="@dimen/two_target_min_width"
- android:layout_height="fill_parent"
- android:src="@drawable/ic_settings_accent"
- android:contentDescription="@string/settings_label"
- android:paddingStart="24dp"
- android:paddingEnd="24dp"
- android:layout_gravity="center"
- android:background="?android:attr/selectableItemBackground" />
- </LinearLayout>
-</LinearLayout>
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_widget_radiobutton.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_widget_radiobutton.xml
deleted file mode 100644
index cb7b8eb..0000000
--- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_widget_radiobutton.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 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.
- -->
-
-<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/checkbox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:background="@null"
- android:focusable="false"
- android:clickable="false" />
diff --git a/packages/SettingsLib/RadioButtonPreference/res/values/strings.xml b/packages/SettingsLib/RadioButtonPreference/res/values/strings.xml
deleted file mode 100644
index ff3f90c..0000000
--- a/packages/SettingsLib/RadioButtonPreference/res/values/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 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.
- -->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Content description for RadioButton with extra gear icon [CHAR LIMIT=NONE] -->
- <string name="settings_label">Settings</string>
-
-</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java b/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java
deleted file mode 100644
index fc4b714..0000000
--- a/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2019 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.widget;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.preference.CheckBoxPreference;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settingslib.widget.preference.radio.R;
-
-/**
- * DEPRECATED. Please use SelectorWithWidgetPreference instead.
- *
- * This file has been moved there and will be removed once all callers are updated.
- *
- * Check box preference with check box replaced by radio button.
- *
- * Functionally speaking, it's actually a CheckBoxPreference. We only modified
- * the widget to RadioButton to make it "look like" a RadioButtonPreference.
- *
- * In other words, there's no "RadioButtonPreferenceGroup" in this
- * implementation. When you check one RadioButtonPreference, if you want to
- * uncheck all the other preferences, you should do that by code yourself.
- *
- * RadioButtonPreference can assign a extraWidgetListener to show a gear icon
- * on the right side that can open another page.
- *
- * @Deprecated
- */
-public class RadioButtonPreference extends CheckBoxPreference {
-
- /**
- * Interface definition for a callback to be invoked when the preference is clicked.
- */
- public interface OnClickListener {
- /**
- * Called when a preference has been clicked.
- *
- * @param emiter The clicked preference
- */
- void onRadioButtonClicked(RadioButtonPreference emiter);
- }
-
- private OnClickListener mListener = null;
- private View mAppendix;
- private int mAppendixVisibility = -1;
-
- private View mExtraWidgetContainer;
- private ImageView mExtraWidget;
-
- private View.OnClickListener mExtraWidgetOnClickListener;
-
- /**
- * Perform inflation from XML and apply a class-specific base style.
- *
- * @param context The {@link Context} this is associated with, through which it can
- * access the current theme, resources, {@link SharedPreferences}, etc.
- * @param attrs The attributes of the XML tag that is inflating the preference
- * @param defStyle An attribute in the current theme that contains a reference to a style
- * resource that supplies default values for the view. Can be 0 to not
- * look for defaults.
- */
- public RadioButtonPreference(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init();
- }
-
- /**
- * Perform inflation from XML and apply a class-specific base style.
- *
- * @param context The {@link Context} this is associated with, through which it can
- * access the current theme, resources, {@link SharedPreferences}, etc.
- * @param attrs The attributes of the XML tag that is inflating the preference
- */
- public RadioButtonPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
- }
-
- /**
- * Constructor to create a preference.
- *
- * @param context The Context this is associated with.
- */
- public RadioButtonPreference(Context context) {
- this(context, null);
- }
-
- /**
- * Sets the callback to be invoked when this preference is clicked by the user.
- *
- * @param listener The callback to be invoked
- */
- public void setOnClickListener(OnClickListener listener) {
- mListener = listener;
- }
-
- /**
- * Processes a click on the preference.
- */
- @Override
- public void onClick() {
- if (mListener != null) {
- mListener.onRadioButtonClicked(this);
- }
- }
-
- /**
- * Binds the created View to the data for this preference.
- *
- * <p>This is a good place to grab references to custom Views in the layout and set
- * properties on them.
- *
- * <p>Make sure to call through to the superclass's implementation.
- *
- * @param holder The ViewHolder that provides references to the views to fill in. These views
- * will be recycled, so you should not hold a reference to them after this method
- * returns.
- */
- @Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
- super.onBindViewHolder(holder);
-
- View summaryContainer = holder.findViewById(R.id.summary_container);
- if (summaryContainer != null) {
- summaryContainer.setVisibility(
- TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE);
- mAppendix = holder.findViewById(R.id.appendix);
- if (mAppendix != null && mAppendixVisibility != -1) {
- mAppendix.setVisibility(mAppendixVisibility);
- }
- }
-
- mExtraWidget = (ImageView) holder.findViewById(R.id.radio_extra_widget);
- mExtraWidgetContainer = holder.findViewById(R.id.radio_extra_widget_container);
-
- setExtraWidgetOnClickListener(mExtraWidgetOnClickListener);
- }
-
- /**
- * Set the visibility state of appendix view.
- *
- * @param visibility One of {@link View#VISIBLE}, {@link View#INVISIBLE}, or {@link View#GONE}.
- */
- public void setAppendixVisibility(int visibility) {
- if (mAppendix != null) {
- mAppendix.setVisibility(visibility);
- }
- mAppendixVisibility = visibility;
- }
-
- /**
- * Sets the callback to be invoked when extra widget is clicked by the user.
- *
- * @param listener The callback to be invoked
- */
- public void setExtraWidgetOnClickListener(View.OnClickListener listener) {
- mExtraWidgetOnClickListener = listener;
-
- if (mExtraWidget == null || mExtraWidgetContainer == null) {
- return;
- }
-
- mExtraWidget.setOnClickListener(mExtraWidgetOnClickListener);
-
- mExtraWidgetContainer.setVisibility((mExtraWidgetOnClickListener != null)
- ? View.VISIBLE : View.GONE);
- }
-
- private void init() {
- setWidgetLayoutResource(R.layout.preference_widget_radiobutton);
- setLayoutResource(R.layout.preference_radio);
- setIconSpaceReserved(false);
- }
-}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index 1a938d6..a4089c0 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -22,6 +22,7 @@
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
@@ -424,6 +425,13 @@
}
/**
+ * Returns if this is searchable.
+ */
+ public boolean isSearchable() {
+ return mMetaData == null || mMetaData.getBoolean(META_DATA_PREFERENCE_SEARCHABLE, true);
+ }
+
+ /**
* The type of the tile.
*/
public enum Type {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index 33907ec..d0929e1 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -250,6 +250,11 @@
public static final String META_DATA_NEW_TASK = "com.android.settings.new_task";
/**
+ * If the entry should be shown in settings search results. Defaults to true.
+ */
+ public static final String META_DATA_PREFERENCE_SEARCHABLE = "com.android.settings.searchable";
+
+ /**
* Build a list of DashboardCategory.
*/
public static List<DashboardCategory> getCategories(Context context,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
index 4d2b1ae..21cdc49 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java
@@ -20,6 +20,7 @@
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
@@ -256,4 +257,26 @@
assertThat(tile.getType()).isEqualTo(Tile.Type.SWITCH_WITH_ACTION);
}
+
+ @Test
+ public void isSearchable_noMetadata_isTrue() {
+ final Tile tile = new ActivityTile(null, "category");
+
+ assertThat(tile.isSearchable()).isTrue();
+ }
+
+ @Test
+ public void isSearchable_notSet_isTrue() {
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+
+ assertThat(tile.isSearchable()).isTrue();
+ }
+
+ @Test
+ public void isSearchable_isSet_false() {
+ mActivityInfo.metaData.putBoolean(META_DATA_PREFERENCE_SEARCHABLE, false);
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+
+ assertThat(tile.isSearchable()).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java
index 80f9efb..faccf2f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ProviderTileTest.java
@@ -20,6 +20,7 @@
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_GROUP_KEY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SEARCHABLE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
@@ -257,6 +258,28 @@
assertThat(tile.getType()).isEqualTo(Tile.Type.GROUP);
}
+ @Test
+ public void isSearchable_noMetadata_isTrue() {
+ final Tile tile = new ProviderTile(mProviderInfo, "category", null);
+
+ assertThat(tile.isSearchable()).isTrue();
+ }
+
+ @Test
+ public void isSearchable_notSet_isTrue() {
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+
+ assertThat(tile.isSearchable()).isTrue();
+ }
+
+ @Test
+ public void isSearchable_isSet_false() {
+ mMetaData.putBoolean(META_DATA_PREFERENCE_SEARCHABLE, false);
+ final Tile tile = new ProviderTile(mProviderInfo, "category", mMetaData);
+
+ assertThat(tile.isSearchable()).isFalse();
+ }
+
@Implements(TileUtils.class)
private static class ShadowTileUtils {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java
deleted file mode 100644
index 0f708cd..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2019 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.widget;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.app.Application;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settingslib.widget.preference.radio.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class RadioButtonPreferenceTest {
-
- private Application mContext;
- private RadioButtonPreference mPreference;
-
- private View mExtraWidgetContainer;
- private View mExtraWidget;
-
- private boolean mIsClickListenerCalled;
- private View.OnClickListener mClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mIsClickListenerCalled = true;
- }
- };
-
- @Before
- public void setUp() {
- mContext = RuntimeEnvironment.application;
- mPreference = new RadioButtonPreference(mContext);
-
- View view = LayoutInflater.from(mContext)
- .inflate(R.layout.preference_radio, null /* root */);
- PreferenceViewHolder preferenceViewHolder =
- PreferenceViewHolder.createInstanceForTests(view);
- mPreference.onBindViewHolder(preferenceViewHolder);
-
- mExtraWidgetContainer = view.findViewById(R.id.radio_extra_widget_container);
- mExtraWidget = view.findViewById(R.id.radio_extra_widget);
- }
-
- @Test
- public void shouldHaveRadioPreferenceLayout() {
- assertThat(mPreference.getLayoutResource()).isEqualTo(R.layout.preference_radio);
- }
-
- @Test
- public void iconSpaceReservedShouldBeFalse() {
- assertThat(mPreference.isIconSpaceReserved()).isFalse();
- }
-
- @Test
- public void onBindViewHolder_withSummary_containerShouldBeVisible() {
- mPreference.setSummary("some summary");
- View summaryContainer = new View(mContext);
- View view = mock(View.class);
- when(view.findViewById(R.id.summary_container)).thenReturn(summaryContainer);
- PreferenceViewHolder preferenceViewHolder =
- PreferenceViewHolder.createInstanceForTests(view);
-
- mPreference.onBindViewHolder(preferenceViewHolder);
-
- assertEquals(View.VISIBLE, summaryContainer.getVisibility());
- }
-
- @Test
- public void onBindViewHolder_emptySummary_containerShouldBeGone() {
- mPreference.setSummary("");
- View summaryContainer = new View(mContext);
- View view = mock(View.class);
- when(view.findViewById(R.id.summary_container)).thenReturn(summaryContainer);
- PreferenceViewHolder preferenceViewHolder =
- PreferenceViewHolder.createInstanceForTests(view);
-
- mPreference.onBindViewHolder(preferenceViewHolder);
-
- assertEquals(View.GONE, summaryContainer.getVisibility());
- }
-
- @Test
- public void nullSummary_containerShouldBeGone() {
- mPreference.setSummary(null);
- View summaryContainer = new View(mContext);
- View view = mock(View.class);
- when(view.findViewById(R.id.summary_container)).thenReturn(summaryContainer);
- PreferenceViewHolder preferenceViewHolder =
- PreferenceViewHolder.createInstanceForTests(view);
- mPreference.onBindViewHolder(preferenceViewHolder);
- assertEquals(View.GONE, summaryContainer.getVisibility());
- }
-
- @Test
- public void setAppendixVisibility_setGone_shouldBeGone() {
- mPreference.setAppendixVisibility(View.GONE);
-
- View view = LayoutInflater.from(mContext)
- .inflate(R.layout.preference_radio, null /* root */);
- PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
- mPreference.onBindViewHolder(holder);
- assertThat(holder.findViewById(R.id.appendix).getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
- public void setExtraWidgetListener_setNull_extraWidgetShouldInvisible() {
- mPreference.setExtraWidgetOnClickListener(null);
-
- assertEquals(View.GONE, mExtraWidgetContainer.getVisibility());
- }
-
- @Test
- public void setExtraWidgetListener_extraWidgetShouldVisible() {
- mPreference.setExtraWidgetOnClickListener(mClickListener);
-
- assertEquals(View.VISIBLE, mExtraWidgetContainer.getVisibility());
- }
-
- @Test
- public void onClickListener_setExtraWidgetOnClickListener_ShouldCalled() {
- mPreference.setExtraWidgetOnClickListener(mClickListener);
-
- assertThat(mIsClickListenerCalled).isFalse();
- mExtraWidget.callOnClick();
- assertThat(mIsClickListenerCalled).isTrue();
- }
-}
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index af6fa86..5c2f979 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -131,5 +131,22 @@
}
]
}
+ ],
+ // v2/sysui/suite/test-mapping-sysui-screenshot-test
+ "sysui-screenshot-test": [
+ {
+ "name": "SystemUIGoogleScreenshotTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ }
+ ]
+ }
]
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 46a9d42..d0f2ce8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -48,13 +48,13 @@
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.res.R
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -104,7 +104,8 @@
modifier: Modifier = Modifier,
) {
val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
- val authMethodViewModel: AuthMethodBouncerViewModel? by viewModel.authMethod.collectAsState()
+ val authMethodViewModel: AuthMethodBouncerViewModel? by
+ viewModel.authMethodViewModel.collectAsState()
val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
var dialog: Dialog? by remember { mutableStateOf(null) }
val backgroundColor = MaterialTheme.colorScheme.surface
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 8e34008..519c0a9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -39,7 +39,9 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
@@ -166,11 +168,18 @@
modifier =
Modifier.align(Alignment.CenterVertically)
// use graphicsLayer instead of Modifier.scale to anchor transform to
- // top left corner
+ // the (start, top) corner
.graphicsLayer(
scaleX = 2.57f,
scaleY = 2.57f,
- transformOrigin = TransformOrigin(0f, 0.5f)
+ transformOrigin =
+ TransformOrigin(
+ when (LocalLayoutDirection.current) {
+ LayoutDirection.Ltr -> 0f
+ LayoutDirection.Rtl -> 1f
+ },
+ 0.5f
+ )
),
)
Spacer(modifier = Modifier.weight(1f))
diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar.xml b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
index 7ce1ba3..d7d75d4 100644
--- a/packages/SystemUI/res/drawable/volume_row_seekbar.xml
+++ b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
@@ -20,17 +20,14 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:paddingMode="stack" >
+ <!-- The groove used for indicating max volume !-->
<item android:id="@android:id/background"
android:gravity="center_vertical|fill_horizontal">
- <inset
- android:insetLeft="@dimen/rounded_slider_track_inset"
- android:insetRight="@dimen/rounded_slider_track_inset" >
- <shape>
- <size android:height="@dimen/volume_dialog_track_width" />
- <corners android:radius="@dimen/volume_dialog_track_corner_radius" />
- <solid android:color="?androidprv:attr/colorAccentSecondaryVariant" />
- </shape>
- </inset>
+ <shape>
+ <size android:height="@dimen/volume_dialog_track_width" />
+ <corners android:radius="@dimen/volume_dialog_panel_width_half" />
+ <solid android:color="?androidprv:attr/materialColorOutlineVariant" />
+ </shape>
</item>
<item android:id="@android:id/progress"
android:gravity="center_vertical|fill_horizontal">
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 37b8ae0..c70f8e2 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -22,8 +22,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:paddingStart="4dp"
- android:paddingEnd="4dp"
->
+ android:paddingEnd="4dp">
<LinearLayout
android:id="@+id/half_shelf"
@@ -82,11 +81,21 @@
android:theme="@style/MainSwitch.Settingslib"/>
</com.android.systemui.statusbar.notification.row.AppControlView>
- <!-- ChannelRows get added dynamically -->
-
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_blocker_channel_list_height"
+ android:clipToPadding="false">
+ <LinearLayout
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <!-- ChannelRows get added dynamically -->
+ </LinearLayout>
+ </ScrollView>
</com.android.systemui.statusbar.notification.row.ChannelEditorListView>
- <RelativeLayout
+ <LinearLayout
android:id="@+id/bottom_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -98,25 +107,23 @@
android:text="@string/see_more_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:gravity="start|center_vertical"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
style="@style/Widget.Dialog.Button"/>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
<TextView
android:id="@+id/done_button"
android:text="@string/inline_ok_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:gravity="end|center_vertical"
android:maxWidth="125dp"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
- android:layout_alignParentEnd="true"
style="@style/Widget.Dialog.Button"/>
- </RelativeLayout>
+ </LinearLayout>
</LinearLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 259b9ad..cfb4017 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -89,6 +89,9 @@
<dimen name="global_actions_button_size">72dp</dimen>
<dimen name="global_actions_button_padding">26dp</dimen>
+ <!-- scroll view the size of 2 channel rows -->
+ <dimen name="notification_blocker_channel_list_height">128dp</dimen>
+
<dimen name="keyguard_indication_margin_bottom">8dp</dimen>
<dimen name="lock_icon_margin_bottom">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 88726af..0ee5da2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -313,6 +313,8 @@
<!-- The space around a notification menu item -->
<dimen name="notification_menu_icon_padding">20dp</dimen>
+ <!-- scroll view the size of 3 channel rows -->
+ <dimen name="notification_blocker_channel_list_height">192dp</dimen>
<!-- The vertical space around the buttons in the inline settings -->
<dimen name="notification_guts_button_spacing">12dp</dimen>
@@ -545,7 +547,7 @@
<!-- (volume_dialog_panel_width - rounded_slider_icon_size) / 2 -->
<dimen name="volume_slider_icon_inset">11dp</dimen>
- <dimen name="volume_dialog_track_width">4dp</dimen>
+ <dimen name="volume_dialog_track_width">40dp</dimen>
<dimen name="volume_dialog_track_corner_radius">2dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 9059230..c074988 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -282,9 +282,9 @@
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
}
- public void setRotationLockedAtAngle(int rotationSuggestion) {
+ public void setRotationLockedAtAngle(int rotationSuggestion, String caller) {
RotationPolicy.setRotationLockAtAngle(mContext, /* enabled= */ isRotationLocked(),
- /* rotation= */ rotationSuggestion);
+ /* rotation= */ rotationSuggestion, caller);
}
public boolean isRotationLocked() {
@@ -468,7 +468,8 @@
if (rotationLocked || mRotationButton.isVisible()) {
// Do not allow a change in rotation to set user rotation when docked.
if (shouldOverrideUserLockPrefs(rotation) && rotationLocked && !mDocked) {
- setRotationLockedAtAngle(rotation);
+ setRotationLockedAtAngle(rotation, /* caller= */
+ "RotationButtonController#onRotationWatcherChanged");
}
setRotateSuggestionButtonState(false /* visible */, true /* forced */);
}
@@ -572,7 +573,8 @@
private void onRotateSuggestionClick(View v) {
mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
incrementNumAcceptedRotationSuggestionsIfNeeded();
- setRotationLockedAtAngle(mLastRotationSuggestion);
+ setRotationLockedAtAngle(mLastRotationSuggestion,
+ /* caller= */ "RotationButtonController#onRotateSuggestionClick");
Log.i(TAG, "onRotateSuggestionClick() mLastRotationSuggestion=" + mLastRotationSuggestion);
v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 57a4224..4cfc6aa 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -185,9 +185,6 @@
/** Whether the pattern should be visible for the currently-selected user. */
val isPatternVisible: StateFlow<Boolean> = repository.isPatternVisible
- /** The minimal length of a pattern. */
- val minPatternLength: Int = repository.minPatternLength
-
private var throttlingCountdownJob: Job? = null
init {
@@ -243,39 +240,46 @@
* Attempts to authenticate the user and unlock the device.
*
* If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method
- * supports auto-confirming, and the input's length is at least the code's length. Otherwise,
- * `null` is returned.
+ * supports auto-confirming, and the input's length is at least the required length. Otherwise,
+ * `AuthenticationResult.SKIPPED` is returned.
*
* @param input The input from the user to try to authenticate with. This can be a list of
* different things, based on the current authentication method.
* @param tryAutoConfirm `true` if called while the user inputs the code, without an explicit
* request to validate.
- * @return `true` if the authentication succeeded and the device is now unlocked; `false` when
- * authentication failed, `null` if the check was not performed.
+ * @return The result of this authentication attempt.
*/
- suspend fun authenticate(input: List<Any>, tryAutoConfirm: Boolean = false): Boolean? {
+ suspend fun authenticate(
+ input: List<Any>,
+ tryAutoConfirm: Boolean = false
+ ): AuthenticationResult {
if (input.isEmpty()) {
throw IllegalArgumentException("Input was empty!")
}
+ val authMethod = getAuthenticationMethod()
val skipCheck =
when {
// We're being throttled, the UI layer should not have called this; skip the
// attempt.
isThrottled.value -> true
+ // The pattern is too short; skip the attempt.
+ authMethod == DomainLayerAuthenticationMethodModel.Pattern &&
+ input.size < repository.minPatternLength -> true
// Auto-confirm attempt when the feature is not enabled; skip the attempt.
tryAutoConfirm && !isAutoConfirmEnabled.value -> true
// Auto-confirm should skip the attempt if the pin entered is too short.
- tryAutoConfirm && input.size < repository.getPinLength() -> true
+ tryAutoConfirm &&
+ authMethod == DomainLayerAuthenticationMethodModel.Pin &&
+ input.size < repository.getPinLength() -> true
else -> false
}
if (skipCheck) {
- return null
+ return AuthenticationResult.SKIPPED
}
// Attempt to authenticate:
- val authMethod = getAuthenticationMethod()
- val credential = authMethod.createCredential(input) ?: return null
+ val credential = authMethod.createCredential(input) ?: return AuthenticationResult.SKIPPED
val authenticationResult = repository.checkCredential(credential)
credential.zeroize()
@@ -299,7 +303,11 @@
refreshThrottling()
}
- return authenticationResult.isSuccessful
+ return if (authenticationResult.isSuccessful) {
+ AuthenticationResult.SUCCEEDED
+ } else {
+ AuthenticationResult.FAILED
+ }
}
/** Starts refreshing the throttling state every second. */
@@ -383,3 +391,16 @@
}
}
}
+
+/** Result of a user authentication attempt. */
+enum class AuthenticationResult {
+ /** Authentication succeeded and the device is now unlocked. */
+ SUCCEEDED,
+ /** Authentication failed and the device remains unlocked. */
+ FAILED,
+ /**
+ * Authentication was not performed, e.g. due to insufficient input, and the device remains
+ * unlocked.
+ */
+ SKIPPED,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 46d3c8a..79d9c1b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -1096,8 +1096,19 @@
// cancel the fingerprint scan.
mCancelAodFingerUpAction = mFgExecutor.executeDelayed(this::tryAodSendFingerUp,
AOD_SEND_FINGER_UP_DELAY_MILLIS);
- // using a hard-coded value for major and minor until it is available from the sensor
- onFingerDown(requestId, screenX, screenY, minor, major);
+ // using a hard-coded value for orientation, time and gestureStart until they are
+ // available from the sensor.
+ onFingerDown(
+ requestId,
+ MotionEvent.INVALID_POINTER_ID /* pointerId */,
+ screenX,
+ screenY,
+ minor,
+ major,
+ 0f /* orientation */,
+ 0L /* time */,
+ 0L /* gestureStart */,
+ true /* isAod */);
};
if (mScreenOn) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 9b2f2ba..f3a463b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -17,8 +17,8 @@
package com.android.systemui.bouncer.domain.interactor
import android.content.Context
-import com.android.systemui.res.R
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.authentication.domain.interactor.AuthenticationResult
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
@@ -26,6 +26,7 @@
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
@@ -34,6 +35,7 @@
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.async
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
@@ -92,9 +94,6 @@
/** Whether the pattern should be visible for the currently-selected user. */
val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible
- /** The minimal length of a pattern. */
- val minPatternLength = authenticationInteractor.minPatternLength
-
init {
if (flags.isEnabled()) {
// Clear the message if moved from throttling to no-longer throttling.
@@ -184,33 +183,44 @@
* dismissed and hidden.
*
* If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method
- * supports auto-confirming, and the input's length is at least the code's length. Otherwise,
- * `null` is returned.
+ * supports auto-confirming, and the input's length is at least the required length. Otherwise,
+ * `AuthenticationResult.SKIPPED` is returned.
*
* @param input The input from the user to try to authenticate with. This can be a list of
* different things, based on the current authentication method.
* @param tryAutoConfirm `true` if called while the user inputs the code, without an explicit
* request to validate.
- * @return `true` if the authentication succeeded and the device is now unlocked; `false` when
- * authentication failed, `null` if the check was not performed.
+ * @return The result of this authentication attempt.
*/
suspend fun authenticate(
input: List<Any>,
tryAutoConfirm: Boolean = false,
- ): Boolean? {
- val isAuthenticated =
- authenticationInteractor.authenticate(input, tryAutoConfirm) ?: return null
-
- if (isAuthenticated) {
- sceneInteractor.changeScene(
- scene = SceneModel(SceneKey.Gone),
- loggingReason = "successful authentication",
- )
- } else {
- showErrorMessage()
+ ): AuthenticationResult {
+ if (input.isEmpty()) {
+ return AuthenticationResult.SKIPPED
}
-
- return isAuthenticated
+ // Switching to the application scope here since this method is often called from
+ // view-models, whose lifecycle (and thus scope) is shorter than this interactor.
+ // This allows the task to continue running properly even when the calling scope has been
+ // cancelled.
+ return applicationScope
+ .async {
+ val authResult = authenticationInteractor.authenticate(input, tryAutoConfirm)
+ when (authResult) {
+ // Authentication succeeded.
+ AuthenticationResult.SUCCEEDED ->
+ sceneInteractor.changeScene(
+ scene = SceneModel(SceneKey.Gone),
+ loggingReason = "successful authentication",
+ )
+ // Authentication failed.
+ AuthenticationResult.FAILED -> showErrorMessage()
+ // Authentication skipped.
+ AuthenticationResult.SKIPPED -> if (!tryAutoConfirm) showErrorMessage()
+ }
+ authResult
+ }
+ .await()
}
/**
@@ -221,21 +231,20 @@
* For example, if the user entered a pattern that's too short, the system can show the error
* message without having the attempt trigger throttling.
*/
- suspend fun showErrorMessage() {
+ private suspend fun showErrorMessage() {
repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
}
- /** If the bouncer is showing, hides the bouncer and return to the lockscreen scene. */
- fun hide(
- loggingReason: String,
- ) {
+ /** Notifies the interactor that the input method editor has been hidden. */
+ fun onImeHidden() {
+ // If the bouncer is showing, hide it and return to the lockscreen scene.
if (sceneInteractor.desiredScene.value.key != SceneKey.Bouncer) {
return
}
sceneInteractor.changeScene(
scene = SceneModel(SceneKey.Lockscreen),
- loggingReason = loggingReason,
+ loggingReason = "IME hidden",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 4546bea..66c6162 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -16,12 +16,21 @@
package com.android.systemui.bouncer.ui.viewmodel
+import android.annotation.StringRes
+import android.util.Log
+import com.android.systemui.authentication.domain.interactor.AuthenticationResult
+import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
sealed class AuthMethodBouncerViewModel(
+ protected val viewModelScope: CoroutineScope,
+ protected val interactor: BouncerInteractor,
+
/**
* Whether user input is enabled.
*
@@ -29,7 +38,6 @@
* being able to attempt to unlock the device.
*/
val isInputEnabled: StateFlow<Boolean>,
- private val interactor: BouncerInteractor,
) {
private val _animateFailure = MutableStateFlow(false)
@@ -42,12 +50,26 @@
/** Whether the input method editor (for example, the software keyboard) is visible. */
private var isImeVisible: Boolean = false
+ /** The authentication method that corresponds to this view model. */
+ abstract val authenticationMethod: AuthenticationMethodModel
+
/**
- * Notifies that the failure animation has been shown. This should be called to consume a `true`
- * value in [animateFailure].
+ * String resource ID of the failure message to be shown during throttling.
+ *
+ * The message must include 2 number parameters: the first one indicating how many unsuccessful
+ * attempts were made, and the second one indicating in how many seconds throttling will expire.
*/
- fun onFailureAnimationShown() {
- _animateFailure.value = false
+ @get:StringRes abstract val throttlingMessageId: Int
+
+ /** Notifies that the UI has been shown to the user. */
+ fun onShown() {
+ clearInput()
+ interactor.resetMessage()
+ }
+
+ /** Notifies that the user has placed down a pointer. */
+ fun onDown() {
+ interactor.onDown()
}
/**
@@ -56,17 +78,44 @@
*/
fun onImeVisibilityChanged(isVisible: Boolean) {
if (isImeVisible && !isVisible) {
- // The IME has gone from visible to invisible, dismiss the bouncer.
- interactor.hide(
- loggingReason = "IME hidden",
- )
+ interactor.onImeHidden()
}
isImeVisible = isVisible
}
- /** Ask the UI to show the failure animation. */
- protected fun showFailureAnimation() {
- _animateFailure.value = true
+ /**
+ * Notifies that the failure animation has been shown. This should be called to consume a `true`
+ * value in [animateFailure].
+ */
+ fun onFailureAnimationShown() {
+ _animateFailure.value = false
+ }
+
+ /** Clears any previously-entered input. */
+ protected abstract fun clearInput()
+
+ /** Returns the input entered so far. */
+ protected abstract fun getInput(): List<Any>
+
+ /**
+ * Attempts to authenticate the user using the current input value.
+ *
+ * @see BouncerInteractor.authenticate
+ */
+ protected fun tryAuthenticate(useAutoConfirm: Boolean = false) {
+ viewModelScope.launch {
+ Log.d("Danny", "tryAuthenticate(useAutoConfirm=$useAutoConfirm)")
+ val authenticationResult = interactor.authenticate(getInput(), useAutoConfirm)
+ Log.d("Danny", "result = $authenticationResult")
+ if (authenticationResult == AuthenticationResult.SKIPPED && useAutoConfirm) {
+ return@launch
+ }
+ _animateFailure.value = authenticationResult != AuthenticationResult.SUCCEEDED
+
+ // TODO(b/291528545): On success, this should only be cleared after the view is animated
+ // away).
+ clearInput()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 15d1dae..782ead3 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -17,16 +17,19 @@
package com.android.systemui.bouncer.ui.viewmodel
import android.content.Context
-import com.android.systemui.res.R
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import javax.inject.Inject
import kotlin.math.ceil
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -35,6 +38,7 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.job
import kotlinx.coroutines.launch
/** Holds UI state and handles user input on bouncer UIs. */
@@ -44,8 +48,9 @@
constructor(
@Application private val applicationContext: Context,
@Application private val applicationScope: CoroutineScope,
+ @Main private val mainDispatcher: CoroutineDispatcher,
private val bouncerInteractor: BouncerInteractor,
- private val authenticationInteractor: AuthenticationInteractor,
+ authenticationInteractor: AuthenticationInteractor,
flags: SceneContainerFlags,
) {
private val isInputEnabled: StateFlow<Boolean> =
@@ -57,91 +62,45 @@
initialValue = !bouncerInteractor.isThrottled.value,
)
- private val pin: PinBouncerViewModel by lazy {
- PinBouncerViewModel(
- applicationContext = applicationContext,
- applicationScope = applicationScope,
- interactor = bouncerInteractor,
- isInputEnabled = isInputEnabled,
- )
- }
-
- private val password: PasswordBouncerViewModel by lazy {
- PasswordBouncerViewModel(
- applicationScope = applicationScope,
- interactor = bouncerInteractor,
- isInputEnabled = isInputEnabled,
- )
- }
-
- private val pattern: PatternBouncerViewModel by lazy {
- PatternBouncerViewModel(
- applicationContext = applicationContext,
- applicationScope = applicationScope,
- interactor = bouncerInteractor,
- isInputEnabled = isInputEnabled,
- )
- }
-
/** View-model for the current UI, based on the current authentication method. */
- val authMethod: StateFlow<AuthMethodBouncerViewModel?> =
+ val authMethodViewModel: StateFlow<AuthMethodBouncerViewModel?> =
authenticationInteractor.authenticationMethod
- .map { authenticationMethod ->
- when (authenticationMethod) {
- is AuthenticationMethodModel.Pin -> pin
- is AuthenticationMethodModel.Password -> password
- is AuthenticationMethodModel.Pattern -> pattern
- else -> null
- }
- }
+ .map(::getChildViewModel)
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
initialValue = null,
)
+ // Handle to the scope of the child ViewModel (stored in [authMethod]).
+ private var childViewModelScope: CoroutineScope? = null
+
init {
if (flags.isEnabled()) {
applicationScope.launch {
- bouncerInteractor.isThrottled
- .map { isThrottled ->
- if (isThrottled) {
- when (authenticationInteractor.getAuthenticationMethod()) {
- is AuthenticationMethodModel.Pin ->
- R.string.kg_too_many_failed_pin_attempts_dialog_message
- is AuthenticationMethodModel.Password ->
- R.string.kg_too_many_failed_password_attempts_dialog_message
- is AuthenticationMethodModel.Pattern ->
- R.string.kg_too_many_failed_pattern_attempts_dialog_message
- else -> null
- }?.let { stringResourceId ->
- applicationContext.getString(
- stringResourceId,
- bouncerInteractor.throttling.value.failedAttemptCount,
- ceil(bouncerInteractor.throttling.value.remainingMs / 1000f)
- .toInt(),
- )
- }
+ combine(bouncerInteractor.isThrottled, authMethodViewModel) {
+ isThrottled,
+ authMethodViewModel ->
+ if (isThrottled && authMethodViewModel != null) {
+ applicationContext.getString(
+ authMethodViewModel.throttlingMessageId,
+ bouncerInteractor.throttling.value.failedAttemptCount,
+ ceil(bouncerInteractor.throttling.value.remainingMs / 1000f)
+ .toInt(),
+ )
} else {
null
}
}
.distinctUntilChanged()
- .collect { dialogMessageOrNull ->
- if (dialogMessageOrNull != null) {
- _throttlingDialogMessage.value = dialogMessageOrNull
- }
- }
+ .collect { dialogMessage -> _throttlingDialogMessage.value = dialogMessage }
}
}
}
/** The user-facing message to show in the bouncer. */
val message: StateFlow<MessageViewModel> =
- combine(
- bouncerInteractor.message,
- bouncerInteractor.isThrottled,
- ) { message, isThrottled ->
+ combine(bouncerInteractor.message, bouncerInteractor.isThrottled) { message, isThrottled ->
toMessageViewModel(message, isThrottled)
}
.stateIn(
@@ -186,6 +145,50 @@
)
}
+ private fun getChildViewModel(
+ authenticationMethod: AuthenticationMethodModel,
+ ): AuthMethodBouncerViewModel? {
+ // If the current child view-model matches the authentication method, reuse it instead of
+ // creating a new instance.
+ val childViewModel = authMethodViewModel.value
+ if (authenticationMethod == childViewModel?.authenticationMethod) {
+ return childViewModel
+ }
+
+ childViewModelScope?.cancel()
+ val newViewModelScope = createChildCoroutineScope(applicationScope)
+ childViewModelScope = newViewModelScope
+ return when (authenticationMethod) {
+ is AuthenticationMethodModel.Pin ->
+ PinBouncerViewModel(
+ applicationContext = applicationContext,
+ viewModelScope = newViewModelScope,
+ interactor = bouncerInteractor,
+ isInputEnabled = isInputEnabled,
+ )
+ is AuthenticationMethodModel.Password ->
+ PasswordBouncerViewModel(
+ viewModelScope = newViewModelScope,
+ interactor = bouncerInteractor,
+ isInputEnabled = isInputEnabled,
+ )
+ is AuthenticationMethodModel.Pattern ->
+ PatternBouncerViewModel(
+ applicationContext = applicationContext,
+ viewModelScope = newViewModelScope,
+ interactor = bouncerInteractor,
+ isInputEnabled = isInputEnabled,
+ )
+ else -> null
+ }
+ }
+
+ private fun createChildCoroutineScope(parentScope: CoroutineScope): CoroutineScope {
+ return CoroutineScope(
+ SupervisorJob(parent = parentScope.coroutineContext.job) + mainDispatcher
+ )
+ }
+
data class MessageViewModel(
val text: String,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index 9e10f29..fe77419 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -16,22 +16,24 @@
package com.android.systemui.bouncer.ui.viewmodel
+import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.res.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the password bouncer UI. */
class PasswordBouncerViewModel(
- private val applicationScope: CoroutineScope,
- private val interactor: BouncerInteractor,
+ viewModelScope: CoroutineScope,
+ interactor: BouncerInteractor,
isInputEnabled: StateFlow<Boolean>,
) :
AuthMethodBouncerViewModel(
- isInputEnabled = isInputEnabled,
+ viewModelScope = viewModelScope,
interactor = interactor,
+ isInputEnabled = isInputEnabled,
) {
private val _password = MutableStateFlow("")
@@ -39,10 +41,16 @@
/** The password entered so far. */
val password: StateFlow<String> = _password.asStateFlow()
- /** Notifies that the UI has been shown to the user. */
- fun onShown() {
+ override val authenticationMethod = AuthenticationMethodModel.Password
+
+ override val throttlingMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message
+
+ override fun clearInput() {
_password.value = ""
- interactor.resetMessage()
+ }
+
+ override fun getInput(): List<Any> {
+ return _password.value.toCharArray().toList()
}
/** Notifies that the user has changed the password input. */
@@ -60,17 +68,8 @@
/** Notifies that the user has pressed the key for attempting to authenticate the password. */
fun onAuthenticateKeyPressed() {
- val password = _password.value.toCharArray().toList()
- if (password.isEmpty()) {
- return
- }
-
- _password.value = ""
-
- applicationScope.launch {
- if (interactor.authenticate(password) != true) {
- showFailureAnimation()
- }
+ if (_password.value.isNotEmpty()) {
+ tryAuthenticate()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index 497276b..52adf54 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -18,8 +18,10 @@
import android.content.Context
import android.util.TypedValue
+import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.res.R
import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow
@@ -31,18 +33,18 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the pattern bouncer UI. */
class PatternBouncerViewModel(
private val applicationContext: Context,
- private val applicationScope: CoroutineScope,
- private val interactor: BouncerInteractor,
+ viewModelScope: CoroutineScope,
+ interactor: BouncerInteractor,
isInputEnabled: StateFlow<Boolean>,
) :
AuthMethodBouncerViewModel(
- isInputEnabled = isInputEnabled,
+ viewModelScope = viewModelScope,
interactor = interactor,
+ isInputEnabled = isInputEnabled,
) {
/** The number of columns in the dot grid. */
@@ -58,7 +60,7 @@
_selectedDots
.map { it.toList() }
.stateIn(
- scope = applicationScope,
+ scope = viewModelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = emptyList(),
)
@@ -76,15 +78,9 @@
/** Whether the pattern itself should be rendered visibly. */
val isPatternVisible: StateFlow<Boolean> = interactor.isPatternVisible
- /** Notifies that the UI has been shown to the user. */
- fun onShown() {
- interactor.resetMessage()
- }
+ override val authenticationMethod = AuthenticationMethodModel.Pattern
- /** Notifies that the user has placed down a pointer, not necessarily dragging just yet. */
- fun onDown() {
- interactor.onDown()
- }
+ override val throttlingMessageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message
/** Notifies that the user has started a drag gesture across the dot grid. */
fun onDragStart() {
@@ -164,24 +160,23 @@
/** Notifies that the user has ended the drag gesture across the dot grid. */
fun onDragEnd() {
- val pattern = _selectedDots.value.map { it.toCoordinate() }
-
+ val pattern = getInput()
if (pattern.size == 1) {
// Single dot patterns are treated as erroneous/false taps:
interactor.onFalseUserInput()
}
+ tryAuthenticate()
+ }
+
+ override fun clearInput() {
_dots.value = defaultDots()
_currentDot.value = null
_selectedDots.value = linkedSetOf()
+ }
- applicationScope.launch {
- if (pattern.size < interactor.minPatternLength) {
- interactor.showErrorMessage()
- } else if (interactor.authenticate(pattern) != true) {
- showFailureAnimation()
- }
- }
+ override fun getInput(): List<Any> {
+ return _selectedDots.value.map(PatternDotViewModel::toCoordinate)
}
private fun defaultDots(): List<PatternDotViewModel> {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 8e6421e..b90e255 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -18,7 +18,9 @@
import android.content.Context
import com.android.keyguard.PinShapeAdapter
+import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.res.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -26,18 +28,18 @@
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
/** Holds UI state and handles user input for the PIN code bouncer UI. */
class PinBouncerViewModel(
applicationContext: Context,
- private val applicationScope: CoroutineScope,
- private val interactor: BouncerInteractor,
+ viewModelScope: CoroutineScope,
+ interactor: BouncerInteractor,
isInputEnabled: StateFlow<Boolean>,
) :
AuthMethodBouncerViewModel(
- isInputEnabled = isInputEnabled,
+ viewModelScope = viewModelScope,
interactor = interactor,
+ isInputEnabled = isInputEnabled,
) {
val pinShapes = PinShapeAdapter(applicationContext)
@@ -61,7 +63,7 @@
)
}
.stateIn(
- scope = applicationScope,
+ scope = viewModelScope,
// Make sure this is kept as WhileSubscribed or we can run into a bug where the
// downstream continues to receive old/stale/cached values.
started = SharingStarted.WhileSubscribed(),
@@ -73,21 +75,14 @@
interactor.isAutoConfirmEnabled
.map { if (it) ActionButtonAppearance.Hidden else ActionButtonAppearance.Shown }
.stateIn(
- scope = applicationScope,
+ scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = ActionButtonAppearance.Hidden,
)
- /** Notifies that the UI has been shown to the user. */
- fun onShown() {
- clearPinInput()
- interactor.resetMessage()
- }
+ override val authenticationMethod = AuthenticationMethodModel.Pin
- /** Notifies that the user has placed down a pointer. */
- fun onDown() {
- interactor.onDown()
- }
+ override val throttlingMessageId = R.string.kg_too_many_failed_pin_attempts_dialog_message
/** Notifies that the user clicked on a PIN button with the given digit value. */
fun onPinButtonClicked(input: Int) {
@@ -109,7 +104,8 @@
/** Notifies that the user long-pressed the backspace button. */
fun onBackspaceButtonLongPressed() {
- clearPinInput()
+ clearInput()
+ interactor.clearMessage()
}
/** Notifies that the user clicked the "enter" button. */
@@ -117,24 +113,12 @@
tryAuthenticate(useAutoConfirm = false)
}
- private fun clearPinInput() {
+ override fun clearInput() {
mutablePinInput.value = mutablePinInput.value.clearAll()
}
- private fun tryAuthenticate(useAutoConfirm: Boolean) {
- val pinCode = mutablePinInput.value.getPin()
-
- applicationScope.launch {
- val isSuccess = interactor.authenticate(pinCode, useAutoConfirm) ?: return@launch
-
- if (!isSuccess) {
- showFailureAnimation()
- }
-
- // TODO(b/291528545): this should not be cleared on success (at least until the view
- // is animated away).
- clearPinInput()
- }
+ override fun getInput(): List<Any> {
+ return mutablePinInput.value.getPin()
}
private fun computeBackspaceButtonAppearance(
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index ecad9d7..3b70555 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -253,7 +253,8 @@
/** Provide new auth messages on the bouncer. */
// TODO(b/277961132): Tracking bug.
- @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages")
+ @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages",
+ teamfood = true)
/** Keyguard Migration */
@@ -297,6 +298,11 @@
@JvmField val MIGRATE_KEYGUARD_STATUS_BAR_VIEW =
unreleasedFlag("migrate_keyguard_status_bar_view")
+ /** Migrate clocks from keyguard status view to keyguard root view*/
+ // TODO(b/301502635): Tracking Bug.
+ @JvmField val MIGRATE_CLOCKS_TO_BLUEPRINT =
+ unreleasedFlag("migrate_clocks_to_blueprint")
+
/** Enables preview loading animation in the wallpaper picker. */
// TODO(b/274443705): Tracking Bug
@JvmField
@@ -762,6 +768,9 @@
// TODO(b/289573946): Tracking Bug
@JvmField val PRECOMPUTED_TEXT = unreleasedFlag("precomputed_text", teamfood = true)
+ // TODO(b/302087895): Tracking Bug
+ @JvmField val CALL_LAYOUT_ASYNC_SET_DATA = unreleasedFlag("call_layout_async_set_data")
+
// 2900 - CentralSurfaces-related flags
// TODO(b/285174336): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index e59fc15..a48e56a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -606,6 +606,9 @@
}
?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
}
+ } else if (isRtl && mediaContent.childCount > 0) {
+ // In RTL, Scroll to the first player as it is the rightmost player in media carousel.
+ mediaCarouselScrollHandler.scrollToPlayer(destIndex = 0)
}
// Check postcondition: mediaContent should have the same number of children as there
// are
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
index 1261152..02f0d12 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
@@ -282,13 +282,14 @@
// It's an up and the fling didn't take it above
val relativePos = scrollView.relativeScrollX % playerWidthPlusPadding
val scrollXAmount: Int =
- if (isRtl xor (relativePos > playerWidthPlusPadding / 2)) {
+ if (relativePos > playerWidthPlusPadding / 2) {
playerWidthPlusPadding - relativePos
} else {
-1 * relativePos
}
if (scrollXAmount != 0) {
- val newScrollX = scrollView.relativeScrollX + scrollXAmount
+ val dx = if (isRtl) -scrollXAmount else scrollXAmount
+ val newScrollX = scrollView.scrollX + dx
// Delay the scrolling since scrollView calls springback which cancels
// the animation again..
mainExecutor.execute { scrollView.smoothScrollTo(newScrollX, scrollView.scrollY) }
@@ -539,7 +540,8 @@
// If the removed media item is "left of" the active one (in an absolute sense), we need to
// scroll the view to keep that player in view. This is because scroll position is always
// calculated from left to right.
- val leftOfActive = if (isRtl) !beforeActive else beforeActive
+ // For RTL, we need to scroll if the visible media player is the last item.
+ val leftOfActive = if (isRtl && visibleMediaIndex != 0) !beforeActive else beforeActive
if (leftOfActive) {
scrollView.scrollX = Math.max(scrollView.scrollX - playerWidthPlusPadding, 0)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt
index 0e07465..10512f1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt
@@ -74,6 +74,14 @@
scrollX = transformScrollX(value)
}
+ override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+ if (!isLaidOut && isLayoutRtl) {
+ // Reset scroll because onLayout method overrides RTL scroll if view was not laid out.
+ mScrollX = relativeScrollX
+ }
+ super.onLayout(changed, l, t, r, b)
+ }
+
/** Allow all scrolls to go through, use base implementation */
override fun scrollTo(x: Int, y: Int) {
if (mScrollX != x || mScrollY != y) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index cb52a5f..ad1c77d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -795,7 +795,8 @@
// Reset user rotation pref to match that of the WindowManager if starting in locked
// mode. This will automatically happen when switching from auto-rotate to locked mode.
if (display != null && rotationButtonController.isRotationLocked()) {
- rotationButtonController.setRotationLockedAtAngle(display.getRotation());
+ rotationButtonController.setRotationLockedAtAngle(
+ display.getRotation(), /* caller= */ "NavigationBar#onViewAttached");
}
} else {
mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 826f75f..19012e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -78,6 +78,14 @@
private int mMinRows = 1;
private int mMaxColumns = TileLayout.NO_MAX_COLUMNS;
+ /**
+ * it's fine to read this value when class is initialized because SysUI is always restarted
+ * when running tests in test harness, see SysUiTestIsolationRule. This check is done quite
+ * often - with every shade open action - so we don't want to potentially make it less
+ * performant only for test use case
+ */
+ private boolean mRunningInTestHarness = ActivityManager.isRunningInTestHarness();
+
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context, SCROLL_CUBIC);
@@ -590,11 +598,11 @@
private boolean shouldNotRunAnimation(Set<String> tilesToReveal) {
boolean noAnimationNeeded = tilesToReveal.isEmpty() || mPages.size() < 2;
boolean scrollingInProgress = getScrollX() != 0 || !beginFakeDrag();
- // isRunningInTestHarness() to disable animation in functional testing as it caused
+ // checking mRunningInTestHarness to disable animation in functional testing as it caused
// flakiness and is not needed there. Alternative solutions were more complex and would
// still be either potentially flaky or modify internal data.
// For more info see b/253493927 and b/293234595
- return noAnimationNeeded || scrollingInProgress || ActivityManager.isRunningInTestHarness();
+ return noAnimationNeeded || scrollingInProgress || mRunningInTestHarness;
}
private int sanitizePageAction(int action) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index ea162fa..5a95004 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -133,7 +133,7 @@
@Override
protected void handleClick(@Nullable View view) {
final boolean newState = !mState.value;
- mController.setRotationLocked(!newState);
+ mController.setRotationLocked(!newState, /* caller= */ "RotationLockTile#handleClick");
refreshState(newState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index e632214..37a4ef1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -104,8 +104,9 @@
// Record the HISTORY_SIZE most recent states
private int mHistoryIndex = 0;
private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
- // This is used by InteractionJankMonitor to get callback from HWUI.
+ // These views are used by InteractionJankMonitor to get callback from HWUI.
private View mView;
+ private KeyguardClockSwitch mClockSwitchView;
/**
* If any of the system bars is hidden.
@@ -334,6 +335,7 @@
if ((mView == null || !mView.isAttachedToWindow())
&& (view != null && view.isAttachedToWindow())) {
mView = view;
+ mClockSwitchView = view.findViewById(R.id.keyguard_clock_container);
}
mDozeAmountTarget = dozeAmount;
if (animated) {
@@ -416,21 +418,12 @@
/** 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) {
+ if (mClockSwitchView == 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();
+ return mClockSwitchView.getClockId();
}
private void beginInteractionJankMonitor() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index eb31bd3..2d5afd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -50,7 +50,7 @@
* Set of summary keys whose groups are expanded.
* NOTE: This should not be modified without notifying listeners, so prefer using
* {@code setGroupExpanded} when making changes.
- */
+ */
private final Set<NotificationEntry> mExpandedGroups = new HashSet<>();
private final FeatureFlags mFeatureFlags;
@@ -104,7 +104,18 @@
@Override
public void setGroupExpanded(NotificationEntry entry, boolean expanded) {
- final NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry);
+ NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry);
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)
+ && entry.getParent() == null) {
+ if (expanded) {
+ throw new IllegalArgumentException("Cannot expand group that is not attached");
+ } else {
+ // The entry is no longer attached, but we still want to make sure we don't have
+ // a stale expansion state.
+ groupSummary = entry;
+ }
+ }
+
boolean changed;
if (expanded) {
changed = mExpandedGroups.add(groupSummary);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
index c33e8ab..3158782 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
@@ -25,18 +25,18 @@
import java.util.List;
/**
- * Helper that determines the group states (parent, summary, children) of a notification.
+ * Helper that determines the group states (parent, summary, children) of a notification. This
+ * generally assumes that the notification is attached (aka its parent is not null).
*/
public interface GroupMembershipManager {
/**
- * @return whether a given notification is a top level entry or is the summary in a group which
- * has children
+ * @return whether a given notification is the summary in a group which has children
*/
boolean isGroupSummary(@NonNull NotificationEntry entry);
/**
* Get the summary of a specified status bar notification. For an isolated notification this
- * returns itself.
+ * returns null, but if called directly on a summary it returns itself.
*/
@Nullable
NotificationEntry getGroupSummary(@NonNull NotificationEntry entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
index a6b855f..cb79353 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
@@ -22,7 +22,7 @@
import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -38,47 +38,50 @@
*/
@SysUISingleton
public class GroupMembershipManagerImpl implements GroupMembershipManager {
- FeatureFlags mFeatureFlags;
+ FeatureFlagsClassic mFeatureFlags;
@Inject
- public GroupMembershipManagerImpl(FeatureFlags featureFlags) {
+ public GroupMembershipManagerImpl(FeatureFlagsClassic featureFlags) {
mFeatureFlags = featureFlags;
}
@Override
public boolean isGroupSummary(@NonNull NotificationEntry entry) {
- return getGroupSummary(entry) == entry;
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
+ if (entry.getParent() == null) {
+ // The entry is not attached, so it doesn't count.
+ return false;
+ }
+ // If entry is a summary, its parent is a GroupEntry with summary = entry.
+ return entry.getParent().getSummary() == entry;
+ } else {
+ return getGroupSummary(entry) == entry;
+ }
}
@Nullable
@Override
public NotificationEntry getGroupSummary(@NonNull NotificationEntry entry) {
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
- if (!isChildInGroup(entry)) {
- return entry.getRepresentativeEntry();
- }
- } else {
- if (isEntryTopLevel(entry) || entry.getParent() == null) {
- return null;
- }
+ if (isTopLevelEntry(entry) || entry.getParent() == null) {
+ return null;
}
-
- return entry.getParent().getRepresentativeEntry();
+ return entry.getParent().getSummary();
}
@Override
public boolean isChildInGroup(@NonNull NotificationEntry entry) {
if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
- return !isEntryTopLevel(entry) && entry.getParent() != null;
+ // An entry is a child if it's not a summary or top level entry, but it is attached.
+ return !isGroupSummary(entry) && !isTopLevelEntry(entry) && entry.getParent() != null;
} else {
- return !isEntryTopLevel(entry);
+ return !isTopLevelEntry(entry);
}
}
@Override
public boolean isOnlyChildInGroup(@NonNull NotificationEntry entry) {
if (entry.getParent() == null) {
- return false;
+ return false; // The entry is not attached.
}
return !isGroupSummary(entry) && entry.getParent().getChildren().size() == 1;
@@ -103,7 +106,7 @@
return null;
}
- private boolean isEntryTopLevel(@NonNull NotificationEntry entry) {
+ private boolean isTopLevelEntry(@NonNull NotificationEntry entry) {
return entry.getParent() == ROOT_ENTRY;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 88994b9..6ec9dbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -100,7 +100,11 @@
/**
* The notification is coming from a suspended packages, so FSI is suppressed.
*/
- NO_FSI_SUSPENDED(false);
+ NO_FSI_SUSPENDED(false),
+ /**
+ * The device is not provisioned, launch FSI.
+ */
+ FSI_NOT_PROVISIONED(true);
public final boolean shouldLaunch;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 0c43da0..3819843 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -45,6 +45,7 @@
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -75,6 +76,7 @@
private final KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
private final UiEventLogger mUiEventLogger;
private final UserTracker mUserTracker;
+ private final DeviceProvisionedController mDeviceProvisionedController;
@VisibleForTesting
protected boolean mUseHeadsUp = false;
@@ -121,7 +123,8 @@
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DeviceProvisionedController deviceProvisionedController) {
mContentResolver = contentResolver;
mPowerManager = powerManager;
mBatteryController = batteryController;
@@ -163,6 +166,7 @@
headsUpObserver);
}
headsUpObserver.onChange(true); // set up
+ mDeviceProvisionedController = deviceProvisionedController;
}
@Override
@@ -334,6 +338,12 @@
}
}
+ // The device is not provisioned, launch FSI.
+ if (!mDeviceProvisionedController.isDeviceProvisioned()) {
+ return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_NOT_PROVISIONED,
+ suppressedByDND);
+ }
+
// Detect the case determined by b/231322873 to launch FSI while device is in use,
// as blocked by the correct implementation, and report the event.
return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 4114eb2..a8d59d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -71,15 +71,15 @@
private var appUid: Int? = null
private var packageName: String? = null
private var appName: String? = null
+ private var channel: NotificationChannel? = null
private var onSettingsClickListener: NotificationInfo.OnSettingsClickListener? = null
// Caller should set this if they care about when we dismiss
var onFinishListener: OnChannelEditorDialogFinishedListener? = null
- @VisibleForTesting
- internal val paddedChannels = mutableListOf<NotificationChannel>()
// Channels handed to us from NotificationInfo
- private val providedChannels = mutableListOf<NotificationChannel>()
+ @VisibleForTesting
+ internal val channelList = mutableListOf<NotificationChannel>()
// Map from NotificationChannel to importance
private val edits = mutableMapOf<NotificationChannel, Int>()
@@ -93,14 +93,14 @@
private val channelGroupList = mutableListOf<NotificationChannelGroup>()
/**
- * Give the controller all of the information it needs to present the dialog
+ * Give the controller all the information it needs to present the dialog
* for a given app. Does a bunch of querying of NoMan, but won't present anything yet
*/
fun prepareDialogForApp(
appName: String,
packageName: String,
uid: Int,
- channels: Set<NotificationChannel>,
+ channel: NotificationChannel,
appIcon: Drawable,
onSettingsClickListener: NotificationInfo.OnSettingsClickListener?
) {
@@ -110,6 +110,7 @@
this.appIcon = appIcon
this.appNotificationsEnabled = checkAreAppNotificationsOn()
this.onSettingsClickListener = onSettingsClickListener
+ this.channel = channel
// These will always start out the same
appNotificationsCurrentlyEnabled = appNotificationsEnabled
@@ -117,9 +118,7 @@
channelGroupList.clear()
channelGroupList.addAll(fetchNotificationChannelGroups())
buildGroupNameLookup()
- providedChannels.clear()
- providedChannels.addAll(channels)
- padToFourChannels(channels)
+ populateChannelList()
initDialog()
prepared = true
@@ -133,36 +132,26 @@
}
}
- private fun padToFourChannels(channels: Set<NotificationChannel>) {
- paddedChannels.clear()
- // First, add all of the given channels
- paddedChannels.addAll(channels.asSequence().take(4))
-
- // Then pad to 4 if we haven't been given that many
- paddedChannels.addAll(getDisplayableChannels(channelGroupList.asSequence())
- .filterNot { paddedChannels.contains(it) }
- .distinct()
- .take(4 - paddedChannels.size))
-
- // If we only got one channel and it has the default miscellaneous tag, then we actually
- // are looking at an app with a targetSdk <= O, and it doesn't make much sense to show the
- // channel
- if (paddedChannels.size == 1 && DEFAULT_CHANNEL_ID == paddedChannels[0].id) {
- paddedChannels.clear()
+ private fun populateChannelList() {
+ channelList.clear()
+ if (DEFAULT_CHANNEL_ID != channel!!.id) {
+ channelList.add(0, channel!!)
+ channelList.addAll(getDisplayableChannels(channelGroupList.asSequence())
+ .filterNot { it.id == channel!!.id }
+ .distinct())
}
}
private fun getDisplayableChannels(
groupList: Sequence<NotificationChannelGroup>
): Sequence<NotificationChannel> {
-
- // TODO (b/194833441): remove channel level settings when we move to a permission
val channels = groupList
.flatMap { group ->
- group.channels.asSequence().filterNot { channel ->
- channel.importance == IMPORTANCE_NONE ||
+ group.channels.asSequence()
+ .sortedWith(compareBy {group.name?.toString() ?: group.id})
+ .filterNot { channel ->
channel.isImportanceLockedByCriticalDeviceFunction
- }
+ }
}
// TODO: sort these by avgSentWeekly, but for now let's just do alphabetical (why not)
@@ -196,8 +185,7 @@
appNotificationsCurrentlyEnabled = null
edits.clear()
- paddedChannels.clear()
- providedChannels.clear()
+ channelList.clear()
groupNameLookup.clear()
}
@@ -231,7 +219,7 @@
@Suppress("unchecked_cast")
private fun fetchNotificationChannelGroups(): List<NotificationChannelGroup> {
return try {
- noMan.getNotificationChannelGroupsForPackage(packageName!!, appUid!!, false)
+ noMan.getRecentBlockedNotificationChannelGroupsForPackage(packageName!!, appUid!!)
.list as? List<NotificationChannelGroup> ?: listOf()
} catch (e: Exception) {
Log.e(TAG, "Error fetching channel groups", e)
@@ -280,7 +268,6 @@
@VisibleForTesting
fun launchSettings(sender: View) {
- val channel = if (providedChannels.size == 1) providedChannels[0] else null
onSettingsClickListener?.onClick(sender, channel, appUid!!)
}
@@ -301,14 +288,12 @@
controller = this@ChannelEditorDialogController
appIcon = this@ChannelEditorDialogController.appIcon
appName = this@ChannelEditorDialogController.appName
- channels = paddedChannels
+ channels = channelList
}
setOnShowListener {
- // play a highlight animation for the given channels
- for (channel in providedChannels) {
- listView?.highlightChannel(channel)
- }
+ // play a highlight animation for the given channel
+ listView?.highlightChannel(channel!!)
}
findViewById<TextView>(R.id.done_button)?.setOnClickListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
index 2cfd075..10e67a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
@@ -20,6 +20,7 @@
import android.animation.ValueAnimator
import android.app.NotificationChannel
import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_LOW
import android.app.NotificationManager.IMPORTANCE_NONE
import android.app.NotificationManager.IMPORTANCE_UNSPECIFIED
import android.content.Context
@@ -55,12 +56,14 @@
// The first row is for the entire app
private lateinit var appControlRow: AppControlView
+ private lateinit var channelListView: LinearLayout
private val channelRows = mutableListOf<ChannelRow>()
override fun onFinishInflate() {
super.onFinishInflate()
appControlRow = requireViewById(R.id.app_control)
+ channelListView = requireViewById(R.id.scrollView)
}
/**
@@ -102,7 +105,7 @@
// Remove any rows
for (row in channelRows) {
- removeView(row)
+ channelListView.removeView(row)
}
channelRows.clear()
@@ -122,7 +125,7 @@
row.channel = channel
channelRows.add(row)
- addView(row)
+ channelListView.addView(row)
}
private fun updateAppControlRow(enabled: Boolean) {
@@ -179,7 +182,9 @@
switch = requireViewById(R.id.toggle)
switch.setOnCheckedChangeListener { _, b ->
channel?.let {
- controller.proposeEditForChannel(it, if (b) it.importance else IMPORTANCE_NONE)
+ controller.proposeEditForChannel(it,
+ if (b) it.originalImportance.coerceAtLeast(IMPORTANCE_LOW)
+ else IMPORTANCE_NONE)
}
}
setOnClickListener { switch.toggle() }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index fb8024c..d18f991 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2658,42 +2658,6 @@
}
/**
- * Returns the number of channels covered by the notification row (including its children if
- * it's a summary notification).
- */
- public int getNumUniqueChannels() {
- return getUniqueChannels().size();
- }
-
- /**
- * Returns the channels covered by the notification row (including its children if
- * it's a summary notification).
- */
- public ArraySet<NotificationChannel> getUniqueChannels() {
- ArraySet<NotificationChannel> channels = new ArraySet<>();
-
- channels.add(mEntry.getChannel());
-
- // If this is a summary, then add in the children notification channels for the
- // same user and pkg.
- if (mIsSummaryWithChildren) {
- final List<ExpandableNotificationRow> childrenRows = getAttachedChildren();
- final int numChildren = childrenRows.size();
- for (int i = 0; i < numChildren; i++) {
- final ExpandableNotificationRow childRow = childrenRows.get(i);
- final NotificationChannel childChannel = childRow.getEntry().getChannel();
- final StatusBarNotification childSbn = childRow.getEntry().getSbn();
- if (childSbn.getUser().equals(mEntry.getSbn().getUser())
- && childSbn.getPackageName().equals(mEntry.getSbn().getPackageName())) {
- channels.add(childChannel);
- }
- }
- }
-
- return channels;
- }
-
- /**
* If this is a group, update the appearance of the children.
*/
public void updateChildrenAppearance() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 1dd3739..6d656605 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -403,7 +403,6 @@
mChannelEditorDialogController,
packageName,
row.getEntry().getChannel(),
- row.getUniqueChannels(),
row.getEntry(),
onSettingsClick,
onAppSettingsClick,
@@ -449,7 +448,6 @@
mChannelEditorDialogController,
packageName,
row.getEntry().getChannel(),
- row.getUniqueChannels(),
row.getEntry(),
onSettingsClick,
mDeviceProvisionedController.isDeviceProvisioned(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index d8f31d4..d8ebd42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -104,8 +104,6 @@
private String mAppName;
private int mAppUid;
private String mDelegatePkg;
- private int mNumUniqueChannelsInRow;
- private Set<NotificationChannel> mUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
private boolean mWasShownHighPriority;
@@ -196,7 +194,6 @@
ChannelEditorDialogController channelEditorDialogController,
String pkg,
NotificationChannel notificationChannel,
- Set<NotificationChannel> uniqueChannelsInRow,
NotificationEntry entry,
OnSettingsClickListener onSettingsClick,
OnAppSettingsClickListener onAppSettingsClick,
@@ -213,8 +210,6 @@
mChannelEditorDialogController = channelEditorDialogController;
mAssistantFeedbackController = assistantFeedbackController;
mPackageName = pkg;
- mUniqueChannelsInRow = uniqueChannelsInRow;
- mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
mEntry = entry;
mSbn = entry.getSbn();
mPm = pm;
@@ -236,15 +231,8 @@
int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
pkg, mAppUid, false /* includeDeleted */);
- if (mNumUniqueChannelsInRow == 0) {
- throw new IllegalArgumentException("bindNotification requires at least one channel");
- } else {
- // Special behavior for the Default channel if no other channels have been defined.
- mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
- && mSingleNotificationChannel.getId().equals(
- NotificationChannel.DEFAULT_CHANNEL_ID)
- && numTotalChannels == 1;
- }
+ mIsSingleDefaultChannel = mSingleNotificationChannel.getId().equals(
+ NotificationChannel.DEFAULT_CHANNEL_ID) && numTotalChannels == 1;
mIsAutomaticChosen = getAlertingBehavior() == BEHAVIOR_AUTOMATIC;
bindHeader();
@@ -271,11 +259,6 @@
findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
findViewById(R.id.turn_off_notifications).setVisibility(GONE);
- } else if (mNumUniqueChannelsInRow > 1) {
- findViewById(R.id.non_configurable_call_text).setVisibility(GONE);
- findViewById(R.id.non_configurable_text).setVisibility(GONE);
- findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
- findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE);
} else {
findViewById(R.id.non_configurable_call_text).setVisibility(GONE);
findViewById(R.id.non_configurable_text).setVisibility(GONE);
@@ -361,9 +344,7 @@
if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
final int appUidF = mAppUid;
return ((View view) -> {
- mOnSettingsClickListener.onClick(view,
- mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
- appUidF);
+ mOnSettingsClickListener.onClick(view, mSingleNotificationChannel, appUidF);
});
}
return null;
@@ -375,7 +356,7 @@
mPresentingChannelEditorDialog = true;
mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
- mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
+ mSingleNotificationChannel, mPkgIcon, mOnSettingsClickListener);
mChannelEditorDialogController.setOnFinishListener(() -> {
mPresentingChannelEditorDialog = false;
mGutsContainer.closeControls(this, false);
@@ -392,7 +373,7 @@
private void bindName() {
final TextView channelName = findViewById(R.id.channel_name);
- if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
+ if (mIsSingleDefaultChannel) {
channelName.setVisibility(View.GONE);
} else {
channelName.setText(mSingleNotificationChannel.getName());
@@ -459,7 +440,7 @@
Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
bgHandler.post(
new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
- mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
+ mSingleNotificationChannel,
mStartingChannelImportance, newImportance, mIsAutomaticChosen));
mOnUserInteractionCallback.onImportanceChanged(mEntry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
index 06c3b79..53f7d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
@@ -57,7 +57,6 @@
private StatusBarNotification mSbn;
private boolean mIsDeviceProvisioned;
private boolean mIsNonBlockable;
- private Set<NotificationChannel> mUniqueChannelsInRow;
private Drawable mPkgIcon;
private boolean mPresentingChannelEditorDialog = false;
@@ -83,7 +82,6 @@
ChannelEditorDialogController channelEditorDialogController,
String pkg,
NotificationChannel notificationChannel,
- Set<NotificationChannel> uniqueChannelsInRow,
NotificationEntry entry,
NotificationInfo.OnSettingsClickListener onSettingsClick,
boolean isDeviceProvisioned,
@@ -100,7 +98,6 @@
mIsDeviceProvisioned = isDeviceProvisioned;
mIsNonBlockable = isNonBlockable;
mChannelEditorDialogController = channelEditorDialogController;
- mUniqueChannelsInRow = uniqueChannelsInRow;
bindHeader();
bindActions();
@@ -149,7 +146,7 @@
mPresentingChannelEditorDialog = true;
mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
- mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
+ mNotificationChannel, mPkgIcon, mOnSettingsClickListener);
mChannelEditorDialogController.setOnFinishListener(() -> {
mPresentingChannelEditorDialog = false;
mGutsContainer.closeControls(this, false);
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 cd68621..dc50990 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
@@ -71,6 +71,7 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.mapNotNull
@@ -181,6 +182,7 @@
telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
}
+ .flowOn(bgDispatcher)
.scan(initial = initial) { state, event -> state.applyEvent(event) }
.stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initial)
}
@@ -358,6 +360,7 @@
awaitClose { context.unregisterReceiver(receiver) }
}
+ .flowOn(bgDispatcher)
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
override val dataEnabled = run {
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 74a849a..ec54f08 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
@@ -134,22 +134,24 @@
)
.stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
- private val mobileSubscriptionsChangeEvent: Flow<Unit> = conflatedCallbackFlow {
- val callback =
- object : SubscriptionManager.OnSubscriptionsChangedListener() {
- override fun onSubscriptionsChanged() {
- logger.logOnSubscriptionsChanged()
- trySend(Unit)
- }
+ private val mobileSubscriptionsChangeEvent: Flow<Unit> =
+ conflatedCallbackFlow {
+ val callback =
+ object : SubscriptionManager.OnSubscriptionsChangedListener() {
+ override fun onSubscriptionsChanged() {
+ logger.logOnSubscriptionsChanged()
+ trySend(Unit)
+ }
+ }
+
+ subscriptionManager.addOnSubscriptionsChangedListener(
+ bgDispatcher.asExecutor(),
+ callback,
+ )
+
+ awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
}
-
- subscriptionManager.addOnSubscriptionsChangedListener(
- bgDispatcher.asExecutor(),
- callback,
- )
-
- awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
- }
+ .flowOn(bgDispatcher)
/**
* State flow that emits the set of mobile data subscriptions, each represented by its own
@@ -184,6 +186,7 @@
telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
}
+ .flowOn(bgDispatcher)
.distinctUntilChanged()
.logDiffsForTable(
tableLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 01fabcc..3008c866d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -159,7 +159,8 @@
// Update the rotation policy, if needed, for this new device state
if (shouldBeLocked != isLocked) {
- mRotationPolicyWrapper.setRotationLock(shouldBeLocked);
+ mRotationPolicyWrapper.setRotationLock(shouldBeLocked,
+ /* caller= */"DeviceStateRotationLockSettingController#readPersistedSetting");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
index 1158324..607f1e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
@@ -24,8 +24,8 @@
boolean isRotationLockAffordanceVisible();
boolean isRotationLocked();
boolean isCameraRotationEnabled();
- void setRotationLocked(boolean locked);
- void setRotationLockedAtAngle(boolean locked, int rotation);
+ void setRotationLocked(boolean locked, String caller);
+ void setRotationLockedAtAngle(boolean locked, int rotation, String caller);
public interface RotationLockControllerCallback {
void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 1eeb0ac..797aa1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -93,12 +93,12 @@
return mRotationPolicy.isCameraRotationEnabled();
}
- public void setRotationLocked(boolean locked) {
- mRotationPolicy.setRotationLock(locked);
+ public void setRotationLocked(boolean locked, String caller) {
+ mRotationPolicy.setRotationLock(locked, caller);
}
- public void setRotationLockedAtAngle(boolean locked, int rotation) {
- mRotationPolicy.setRotationLockAtAngle(locked, rotation);
+ public void setRotationLockedAtAngle(boolean locked, int rotation, String caller) {
+ mRotationPolicy.setRotationLockAtAngle(locked, rotation, caller);
}
public boolean isRotationLockAffordanceVisible() {
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index e489499..ce9d6fd 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -37,7 +37,6 @@
import com.android.internal.util.UserIcons
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.res.R
import com.android.systemui.SystemUISecondaryUserService
import com.android.systemui.animation.Expandable
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -50,6 +49,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.res.R
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.CreateUserActivity
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
@@ -62,6 +62,7 @@
import com.android.systemui.user.utils.MultiUserActionsEvent
import com.android.systemui.user.utils.MultiUserActionsEventHelper
import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.utils.UserRestrictionChecker
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -103,6 +104,7 @@
private val refreshUsersScheduler: RefreshUsersScheduler,
private val guestUserInteractor: GuestUserInteractor,
private val uiEventLogger: UiEventLogger,
+ private val userRestrictionChecker: UserRestrictionChecker,
) {
/**
* Defines interface for classes that can be notified when the state of users on the device is
@@ -593,6 +595,7 @@
) &&
// If the user is auto-created is must not be currently resetting.
!(isGuestUserAutoCreated && isGuestUserResetting),
+ userRestrictionChecker = userRestrictionChecker,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
index 93573fa..80139bd 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
@@ -22,10 +22,10 @@
import android.graphics.Bitmap
import android.os.UserManager
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
-import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.systemui.res.R
import com.android.systemui.user.data.source.UserRecord
import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.utils.UserRestrictionChecker
/**
* Defines utility functions for helping with legacy data code for users.
@@ -68,6 +68,7 @@
actionType: UserActionModel,
isRestricted: Boolean,
isSwitchToEnabled: Boolean,
+ userRestrictionChecker: UserRestrictionChecker,
): UserRecord {
return UserRecord(
isGuest = actionType == UserActionModel.ENTER_GUEST_MODE,
@@ -79,6 +80,7 @@
getEnforcedAdmin(
context = context,
selectedUserId = selectedUserId,
+ userRestrictionChecker = userRestrictionChecker,
),
isManageUsers = actionType == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
)
@@ -103,9 +105,10 @@
private fun getEnforcedAdmin(
context: Context,
selectedUserId: Int,
+ userRestrictionChecker: UserRestrictionChecker
): EnforcedAdmin? {
val admin =
- RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ userRestrictionChecker.checkIfRestrictionEnforced(
context,
UserManager.DISALLOW_ADD_USER,
selectedUserId,
@@ -113,7 +116,7 @@
?: return null
return if (
- !RestrictedLockUtilsInternal.hasBaseUserRestriction(
+ !userRestrictionChecker.hasBaseUserRestriction(
context,
UserManager.DISALLOW_ADD_USER,
selectedUserId,
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
index d8de07d..374ebe0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
@@ -28,8 +28,8 @@
* Testable wrapper interface around RotationPolicy {link com.android.internal.view.RotationPolicy}
*/
interface RotationPolicyWrapper {
- fun setRotationLock(enabled: Boolean)
- fun setRotationLockAtAngle(enabled: Boolean, rotation: Int)
+ fun setRotationLock(enabled: Boolean, caller: String)
+ fun setRotationLockAtAngle(enabled: Boolean, rotation: Int, caller: String)
fun getRotationLockOrientation(): Int
fun isRotationLockToggleVisible(): Boolean
fun isRotationLocked(): Boolean
@@ -44,14 +44,14 @@
) :
RotationPolicyWrapper {
- override fun setRotationLock(enabled: Boolean) {
+ override fun setRotationLock(enabled: Boolean, caller: String) {
traceSection("RotationPolicyWrapperImpl#setRotationLock") {
- RotationPolicy.setRotationLock(context, enabled)
+ RotationPolicy.setRotationLock(context, enabled, caller)
}
}
- override fun setRotationLockAtAngle(enabled: Boolean, rotation: Int) {
- RotationPolicy.setRotationLockAtAngle(context, enabled, rotation)
+ override fun setRotationLockAtAngle(enabled: Boolean, rotation: Int, caller: String) {
+ RotationPolicy.setRotationLockAtAngle(context, enabled, rotation, caller)
}
override fun getRotationLockOrientation(): Int =
diff --git a/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt b/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt
new file mode 100644
index 0000000..3f8346b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt
@@ -0,0 +1,25 @@
+package com.android.systemui.utils
+
+import android.content.Context
+import com.android.settingslib.RestrictedLockUtils
+import com.android.settingslib.RestrictedLockUtilsInternal
+import javax.inject.Inject
+
+/** Proxy to call [RestrictedLockUtilsInternal] */
+class UserRestrictionChecker @Inject constructor() {
+ fun checkIfRestrictionEnforced(
+ context: Context,
+ userRestriction: String,
+ userId: Int
+ ): RestrictedLockUtils.EnforcedAdmin? {
+ return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+ context,
+ userRestriction,
+ userId
+ )
+ }
+
+ fun hasBaseUserRestriction(context: Context, userRestriction: String, userId: Int): Boolean {
+ return RestrictedLockUtilsInternal.hasBaseUserRestriction(context, userRestriction, userId)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index fc7d20a..874053a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -236,7 +236,8 @@
utils.authenticationRepository.setAuthenticationMethod(
DataLayerAuthenticationMethodModel.Pin
)
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue()
+ assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
assertThat(isThrottled).isFalse()
}
@@ -246,7 +247,8 @@
utils.authenticationRepository.setAuthenticationMethod(
DataLayerAuthenticationMethodModel.Pin
)
- assertThat(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4))).isFalse()
+ assertThat(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4)))
+ .isEqualTo(AuthenticationResult.FAILED)
}
@Test(expected = IllegalArgumentException::class)
@@ -267,7 +269,7 @@
overrideCredential(pin)
}
- assertThat(underTest.authenticate(pin)).isTrue()
+ assertThat(underTest.authenticate(pin)).isEqualTo(AuthenticationResult.SUCCEEDED)
}
@Test
@@ -282,7 +284,8 @@
utils.authenticationRepository.setAuthenticationMethod(
DataLayerAuthenticationMethodModel.Pin
)
- assertThat(underTest.authenticate(List(17) { 9 })).isFalse()
+ assertThat(underTest.authenticate(List(17) { 9 }))
+ .isEqualTo(AuthenticationResult.FAILED)
}
@Test
@@ -293,7 +296,8 @@
DataLayerAuthenticationMethodModel.Password
)
- assertThat(underTest.authenticate("password".toList())).isTrue()
+ assertThat(underTest.authenticate("password".toList()))
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
assertThat(isThrottled).isFalse()
}
@@ -304,7 +308,8 @@
DataLayerAuthenticationMethodModel.Password
)
- assertThat(underTest.authenticate("alohomora".toList())).isFalse()
+ assertThat(underTest.authenticate("alohomora".toList()))
+ .isEqualTo(AuthenticationResult.FAILED)
}
@Test
@@ -314,7 +319,8 @@
DataLayerAuthenticationMethodModel.Pattern
)
- assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN)).isTrue()
+ assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN))
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
}
@Test
@@ -327,22 +333,14 @@
assertThat(
underTest.authenticate(
listOf(
- AuthenticationPatternCoordinate(
- x = 2,
- y = 0,
- ),
- AuthenticationPatternCoordinate(
- x = 2,
- y = 1,
- ),
- AuthenticationPatternCoordinate(
- x = 2,
- y = 2,
- ),
+ AuthenticationPatternCoordinate(x = 2, y = 0),
+ AuthenticationPatternCoordinate(x = 2, y = 1),
+ AuthenticationPatternCoordinate(x = 2, y = 2),
+ AuthenticationPatternCoordinate(x = 1, y = 2),
)
)
)
- .isFalse()
+ .isEqualTo(AuthenticationResult.FAILED)
}
@Test
@@ -361,7 +359,7 @@
tryAutoConfirm = true
)
)
- .isNull()
+ .isEqualTo(AuthenticationResult.SKIPPED)
assertThat(isThrottled).isFalse()
}
@@ -379,7 +377,7 @@
tryAutoConfirm = true
)
)
- .isFalse()
+ .isEqualTo(AuthenticationResult.FAILED)
assertThat(isUnlocked).isFalse()
}
@@ -397,7 +395,7 @@
tryAutoConfirm = true
)
)
- .isFalse()
+ .isEqualTo(AuthenticationResult.FAILED)
assertThat(isUnlocked).isFalse()
}
@@ -415,7 +413,7 @@
tryAutoConfirm = true
)
)
- .isTrue()
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
assertThat(isUnlocked).isTrue()
}
@@ -433,7 +431,7 @@
tryAutoConfirm = true
)
)
- .isNull()
+ .isEqualTo(AuthenticationResult.SKIPPED)
assertThat(isUnlocked).isFalse()
}
@@ -445,7 +443,8 @@
DataLayerAuthenticationMethodModel.Password
)
- assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)).isNull()
+ assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true))
+ .isEqualTo(AuthenticationResult.SKIPPED)
assertThat(isUnlocked).isFalse()
}
@@ -490,7 +489,8 @@
)
// Correct PIN, but throttled, so doesn't attempt it:
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isNull()
+ assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
+ .isEqualTo(AuthenticationResult.SKIPPED)
assertThat(isUnlocked).isFalse()
assertThat(isThrottled).isTrue()
assertThat(throttling)
@@ -536,7 +536,8 @@
)
// Correct PIN and no longer throttled so unlocks successfully:
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue()
+ assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
assertThat(isUnlocked).isTrue()
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 8fc63b2..7365460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -44,6 +44,7 @@
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -126,6 +127,7 @@
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
)
shadeInteractor =
ShadeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 77d8102..92c8a39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -17,13 +17,14 @@
package com.android.systemui.bouncer.domain.interactor
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.AuthenticationResult
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -87,7 +88,8 @@
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
// Wrong input.
- assertThat(underTest.authenticate(listOf(9, 8, 7))).isFalse()
+ assertThat(underTest.authenticate(listOf(9, 8, 7)))
+ .isEqualTo(AuthenticationResult.FAILED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -95,7 +97,8 @@
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
// Correct input.
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue()
+ assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@@ -115,13 +118,14 @@
underTest.clearMessage()
// Incomplete input.
- assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)).isNull()
+ assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true))
+ .isEqualTo(AuthenticationResult.SKIPPED)
assertThat(message).isEmpty()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Wrong 6-digit pin
assertThat(underTest.authenticate(listOf(1, 2, 3, 5, 5, 6), tryAutoConfirm = true))
- .isFalse()
+ .isEqualTo(AuthenticationResult.FAILED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -132,7 +136,7 @@
tryAutoConfirm = true
)
)
- .isTrue()
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@@ -150,7 +154,8 @@
underTest.clearMessage()
// Incomplete input.
- assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)).isNull()
+ assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true))
+ .isEqualTo(AuthenticationResult.SKIPPED)
assertThat(message).isEmpty()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -161,7 +166,7 @@
tryAutoConfirm = true
)
)
- .isNull()
+ .isEqualTo(AuthenticationResult.SKIPPED)
assertThat(message).isEmpty()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@@ -187,7 +192,8 @@
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
// Wrong input.
- assertThat(underTest.authenticate("alohamora".toList())).isFalse()
+ assertThat(underTest.authenticate("alohamora".toList()))
+ .isEqualTo(AuthenticationResult.FAILED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PASSWORD)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -195,7 +201,8 @@
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
// Correct input.
- assertThat(underTest.authenticate("password".toList())).isTrue()
+ assertThat(underTest.authenticate("password".toList()))
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@@ -220,8 +227,30 @@
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
// Wrong input.
- assertThat(underTest.authenticate(listOf(AuthenticationPatternCoordinate(1, 2))))
- .isFalse()
+ val wrongPattern =
+ listOf(
+ AuthenticationPatternCoordinate(1, 2),
+ AuthenticationPatternCoordinate(1, 1),
+ AuthenticationPatternCoordinate(0, 0),
+ AuthenticationPatternCoordinate(0, 1),
+ )
+ assertThat(wrongPattern).isNotEqualTo(FakeAuthenticationRepository.PATTERN)
+ assertThat(wrongPattern.size).isAtLeast(utils.authenticationRepository.minPatternLength)
+ assertThat(underTest.authenticate(wrongPattern)).isEqualTo(AuthenticationResult.FAILED)
+ assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN)
+ assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+
+ underTest.resetMessage()
+ assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
+
+ // Too short input.
+ val tooShortPattern =
+ FakeAuthenticationRepository.PATTERN.subList(
+ 0,
+ utils.authenticationRepository.minPatternLength - 1
+ )
+ assertThat(underTest.authenticate(tooShortPattern))
+ .isEqualTo(AuthenticationResult.SKIPPED)
assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -229,7 +258,8 @@
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
// Correct input.
- assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN)).isTrue()
+ assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN))
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@@ -294,7 +324,8 @@
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { times ->
// Wrong PIN.
- assertThat(underTest.authenticate(listOf(6, 7, 8, 9))).isFalse()
+ assertThat(underTest.authenticate(listOf(6, 7, 8, 9)))
+ .isEqualTo(AuthenticationResult.FAILED)
if (
times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1
) {
@@ -317,7 +348,8 @@
)
// Correct PIN, but throttled, so doesn't change away from the bouncer scene:
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isNull()
+ assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
+ .isEqualTo(AuthenticationResult.SKIPPED)
assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer)
assertTryAgainMessage(
message,
@@ -347,7 +379,8 @@
assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer)
// Correct PIN and no longer throttled so changes to the Gone scene:
- assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue()
+ assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
+ .isEqualTo(AuthenticationResult.SUCCEEDED)
assertThat(currentScene?.key).isEqualTo(SceneKey.Gone)
assertThat(isThrottled).isFalse()
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
@@ -362,7 +395,7 @@
val bouncerSceneKey = currentScene?.key
assertThat(bouncerSceneKey).isEqualTo(SceneKey.Bouncer)
- underTest.hide("")
+ underTest.onImeHidden()
assertThat(currentScene?.key).isEqualTo(SceneKey.Lockscreen)
}
@@ -376,7 +409,7 @@
val notBouncerSceneKey = currentScene?.key
assertThat(notBouncerSceneKey).isNotEqualTo(SceneKey.Bouncer)
- underTest.hide("")
+ underTest.onImeHidden()
assertThat(currentScene?.key).isEqualTo(notBouncerSceneKey)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 9011c2f..2f7dde0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -45,7 +45,7 @@
private val underTest =
PinBouncerViewModel(
applicationContext = context,
- applicationScope = testScope.backgroundScope,
+ viewModelScope = testScope.backgroundScope,
interactor =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 2c96bcc..da2534d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -46,7 +46,7 @@
private val testScope = utils.testScope
private val authenticationInteractor =
utils.authenticationInteractor(
- repository = utils.authenticationRepository(),
+ repository = utils.authenticationRepository,
)
private val bouncerInteractor =
utils.bouncerInteractor(
@@ -66,7 +66,8 @@
authMethodsToTest().forEach { authMethod ->
utils.authenticationRepository.setAuthenticationMethod(authMethod)
- val job = underTest.authMethod.onEach { authMethodViewModel = it }.launchIn(this)
+ val job =
+ underTest.authMethodViewModel.onEach { authMethodViewModel = it }.launchIn(this)
runCurrent()
if (authMethod.isSecure) {
@@ -86,22 +87,43 @@
}
@Test
- fun authMethod_reusesInstances() =
+ fun authMethodChanged_doesNotReuseInstances() =
testScope.runTest {
val seen =
mutableMapOf<DomainLayerAuthenticationMethodModel, AuthMethodBouncerViewModel>()
val authMethodViewModel: AuthMethodBouncerViewModel? by
- collectLastValue(underTest.authMethod)
+ collectLastValue(underTest.authMethodViewModel)
+
// First pass, populate our "seen" map:
authMethodsToTest().forEach { authMethod ->
utils.authenticationRepository.setAuthenticationMethod(authMethod)
authMethodViewModel?.let { seen[authMethod] = it }
}
- // Second pass, assert same instances are reused:
+ // Second pass, assert same instances are not reused:
authMethodsToTest().forEach { authMethod ->
utils.authenticationRepository.setAuthenticationMethod(authMethod)
- authMethodViewModel?.let { assertThat(it).isSameInstanceAs(seen[authMethod]) }
+ authMethodViewModel?.let {
+ assertThat(it.authenticationMethod).isEqualTo(authMethod)
+ assertThat(it).isNotSameInstanceAs(seen[authMethod])
+ }
+ }
+ }
+
+ @Test
+ fun authMethodUnchanged_reusesInstances() =
+ testScope.runTest {
+ authMethodsToTest().forEach { authMethod ->
+ utils.authenticationRepository.setAuthenticationMethod(authMethod)
+ val firstInstance: AuthMethodBouncerViewModel? =
+ collectLastValue(underTest.authMethodViewModel).invoke()
+
+ utils.authenticationRepository.setAuthenticationMethod(authMethod)
+ val secondInstance: AuthMethodBouncerViewModel? =
+ collectLastValue(underTest.authMethodViewModel).invoke()
+
+ firstInstance?.let { assertThat(it.authenticationMethod).isEqualTo(authMethod) }
+ assertThat(secondInstance).isSameInstanceAs(firstInstance)
}
}
@@ -136,7 +158,7 @@
testScope.runTest {
val isInputEnabled by
collectLastValue(
- underTest.authMethod.flatMapLatest { authViewModel ->
+ underTest.authMethodViewModel.flatMapLatest { authViewModel ->
authViewModel?.isInputEnabled ?: emptyFlow()
}
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 3375184..c1b3354 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -17,10 +17,11 @@
package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -28,6 +29,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -59,7 +61,7 @@
)
private val underTest =
PasswordBouncerViewModel(
- applicationScope = testScope.backgroundScope,
+ viewModelScope = testScope.backgroundScope,
interactor = bouncerInteractor,
isInputEnabled = MutableStateFlow(true).asStateFlow(),
)
@@ -76,19 +78,13 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
-
- underTest.onShown()
+ lockDeviceAndOpenPasswordBouncer()
assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
assertThat(password).isEqualTo("")
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ assertThat(underTest.authenticationMethod)
+ .isEqualTo(DomainAuthenticationMethodModel.Password)
}
@Test
@@ -97,15 +93,7 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- runCurrent()
+ lockDeviceAndOpenPasswordBouncer()
underTest.onPasswordInputChanged("password")
@@ -118,16 +106,9 @@
fun onAuthenticateKeyPressed_whenCorrect() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- underTest.onPasswordInputChanged("password")
+ lockDeviceAndOpenPasswordBouncer()
+ underTest.onPasswordInputChanged("password")
underTest.onAuthenticateKeyPressed()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
@@ -139,16 +120,9 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- underTest.onPasswordInputChanged("wrong")
+ lockDeviceAndOpenPasswordBouncer()
+ underTest.onPasswordInputChanged("wrong")
underTest.onAuthenticateKeyPressed()
assertThat(password).isEqualTo("")
@@ -185,14 +159,9 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val password by collectLastValue(underTest.password)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
+ lockDeviceAndOpenPasswordBouncer()
+
+ // Enter the wrong password:
underTest.onPasswordInputChanged("wrong")
underTest.onAuthenticateKeyPressed()
assertThat(password).isEqualTo("")
@@ -213,14 +182,7 @@
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val password by collectLastValue(underTest.password)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Password
- )
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
+ lockDeviceAndOpenPasswordBouncer()
// The user types a password.
underTest.onPasswordInputChanged("password")
@@ -243,6 +205,18 @@
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
+ private fun TestScope.lockDeviceAndOpenPasswordBouncer() {
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Password)
+ utils.authenticationRepository.setUnlocked(false)
+ sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
+
+ assertThat(collectLastValue(sceneInteractor.desiredScene).invoke())
+ .isEqualTo(SceneModel(SceneKey.Bouncer))
+ underTest.onShown()
+ runCurrent()
+ }
+
companion object {
private const val ENTER_YOUR_PASSWORD = "Enter your password"
private const val WRONG_PASSWORD = "Wrong password"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 102cfe2..bf109d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -17,12 +17,13 @@
package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate as Point
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -64,7 +65,7 @@
private val underTest =
PatternBouncerViewModel(
applicationContext = context,
- applicationScope = testScope.backgroundScope,
+ viewModelScope = testScope.backgroundScope,
interactor = bouncerInteractor,
isInputEnabled = MutableStateFlow(true).asStateFlow(),
)
@@ -85,14 +86,14 @@
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
- transitionToPatternBouncer()
-
- underTest.onShown()
+ lockDeviceAndOpenPatternBouncer()
assertThat(message?.text).isEqualTo(ENTER_YOUR_PATTERN)
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ assertThat(underTest.authenticationMethod)
+ .isEqualTo(DomainAuthenticationMethodModel.Pattern)
}
@Test
@@ -102,9 +103,7 @@
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
- transitionToPatternBouncer()
- underTest.onShown()
- runCurrent()
+ lockDeviceAndOpenPatternBouncer()
underTest.onDragStart()
@@ -120,8 +119,7 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
- transitionToPatternBouncer()
- underTest.onShown()
+ lockDeviceAndOpenPatternBouncer()
underTest.onDragStart()
assertThat(currentDot).isNull()
CORRECT_PATTERN.forEachIndexed { index, coordinate ->
@@ -158,8 +156,7 @@
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
- transitionToPatternBouncer()
- underTest.onShown()
+ lockDeviceAndOpenPatternBouncer()
underTest.onDragStart()
CORRECT_PATTERN.subList(0, 3).forEach { coordinate -> dragToCoordinate(coordinate) }
@@ -175,8 +172,7 @@
fun onDrag_shouldIncludeDotsThatWereSkippedOverAlongTheSameRow() =
testScope.runTest {
val selectedDots by collectLastValue(underTest.selectedDots)
- transitionToPatternBouncer()
- underTest.onShown()
+ lockDeviceAndOpenPatternBouncer()
/*
* Pattern setup, coordinates are (column, row)
@@ -202,8 +198,7 @@
fun onDrag_shouldIncludeDotsThatWereSkippedOverAlongTheSameColumn() =
testScope.runTest {
val selectedDots by collectLastValue(underTest.selectedDots)
- transitionToPatternBouncer()
- underTest.onShown()
+ lockDeviceAndOpenPatternBouncer()
/*
* Pattern setup, coordinates are (column, row)
@@ -229,8 +224,7 @@
fun onDrag_shouldIncludeDotsThatWereSkippedOverAlongTheDiagonal() =
testScope.runTest {
val selectedDots by collectLastValue(underTest.selectedDots)
- transitionToPatternBouncer()
- underTest.onShown()
+ lockDeviceAndOpenPatternBouncer()
/*
* Pattern setup
@@ -258,8 +252,7 @@
fun onDrag_shouldNotIncludeDotIfItIsNotOnTheLine() =
testScope.runTest {
val selectedDots by collectLastValue(underTest.selectedDots)
- transitionToPatternBouncer()
- underTest.onShown()
+ lockDeviceAndOpenPatternBouncer()
/*
* Pattern setup
@@ -287,8 +280,7 @@
fun onDrag_shouldNotIncludeSkippedOverDotsIfTheyAreAlreadySelected() =
testScope.runTest {
val selectedDots by collectLastValue(underTest.selectedDots)
- transitionToPatternBouncer()
- underTest.onShown()
+ lockDeviceAndOpenPatternBouncer()
/*
* Pattern setup
@@ -315,20 +307,10 @@
@Test
fun onDragEnd_whenPatternTooShort() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
- val selectedDots by collectLastValue(underTest.selectedDots)
- val currentDot by collectLastValue(underTest.currentDot)
val throttlingDialogMessage by
collectLastValue(bouncerViewModel.throttlingDialogMessage)
- utils.authenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.Pattern
- )
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
+ lockDeviceAndOpenPatternBouncer()
// Enter a pattern that's too short more than enough times that would normally trigger
// throttling if the pattern were not too short and wrong:
@@ -337,7 +319,7 @@
underTest.onDragStart()
CORRECT_PATTERN.subList(
0,
- authenticationInteractor.minPatternLength - 1,
+ utils.authenticationRepository.minPatternLength - 1,
)
.forEach { coordinate ->
underTest.onDrag(
@@ -362,10 +344,10 @@
val message by collectLastValue(bouncerViewModel.message)
val selectedDots by collectLastValue(underTest.selectedDots)
val currentDot by collectLastValue(underTest.currentDot)
- transitionToPatternBouncer()
- underTest.onShown()
+ lockDeviceAndOpenPatternBouncer()
+
underTest.onDragStart()
- CORRECT_PATTERN.subList(2, 7).forEach { coordinate -> dragToCoordinate(coordinate) }
+ CORRECT_PATTERN.subList(2, 7).forEach(::dragToCoordinate)
underTest.onDragEnd()
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
@@ -373,7 +355,7 @@
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Enter the correct pattern:
- CORRECT_PATTERN.forEach { coordinate -> dragToCoordinate(coordinate) }
+ CORRECT_PATTERN.forEach(::dragToCoordinate)
underTest.onDragEnd()
@@ -382,7 +364,7 @@
private fun dragOverCoordinates(vararg coordinatesDragged: Point) {
underTest.onDragStart()
- coordinatesDragged.forEach { dragToCoordinate(it) }
+ coordinatesDragged.forEach(::dragToCoordinate)
}
private fun dragToCoordinate(coordinate: Point) {
@@ -394,13 +376,15 @@
)
}
- private fun TestScope.transitionToPatternBouncer() {
+ private fun TestScope.lockDeviceAndOpenPatternBouncer() {
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pattern)
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
assertThat(collectLastValue(sceneInteractor.desiredScene).invoke())
.isEqualTo(SceneModel(SceneKey.Bouncer))
+ underTest.onShown()
+ runCurrent()
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 35238ce..2576204 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -17,11 +17,12 @@
package com.android.systemui.bouncer.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainAuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -30,6 +31,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -62,7 +64,7 @@
private val underTest =
PinBouncerViewModel(
applicationContext = context,
- applicationScope = testScope.backgroundScope,
+ viewModelScope = testScope.backgroundScope,
interactor = bouncerInteractor,
isInputEnabled = MutableStateFlow(true).asStateFlow(),
)
@@ -90,6 +92,8 @@
assertThat(message?.text).isEqualTo(ENTER_YOUR_PIN)
assertThat(pin).isEmpty()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
+ assertThat(underTest.authenticationMethod)
+ .isEqualTo(DomainAuthenticationMethodModel.Pin)
}
@Test
@@ -120,14 +124,8 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
+ lockDeviceAndOpenPinBouncer()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- runCurrent()
underTest.onPinButtonClicked(1)
assertThat(pin).hasSize(1)
@@ -141,15 +139,8 @@
@Test
fun onPinEdit() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
+ lockDeviceAndOpenPinBouncer()
underTest.onPinButtonClicked(1)
underTest.onPinButtonClicked(2)
@@ -168,18 +159,13 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
+ lockDeviceAndOpenPinBouncer()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
- runCurrent()
underTest.onPinButtonClicked(1)
underTest.onPinButtonClicked(2)
underTest.onPinButtonClicked(3)
underTest.onPinButtonClicked(4)
+ runCurrent()
underTest.onBackspaceButtonLongPressed()
@@ -192,13 +178,8 @@
fun onAuthenticateButtonClicked_whenCorrect() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
+ lockDeviceAndOpenPinBouncer()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
underTest.onPinButtonClicked(digit)
}
@@ -214,13 +195,8 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
+ lockDeviceAndOpenPinBouncer()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
underTest.onPinButtonClicked(1)
underTest.onPinButtonClicked(2)
underTest.onPinButtonClicked(3)
@@ -240,13 +216,8 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
+ lockDeviceAndOpenPinBouncer()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
underTest.onPinButtonClicked(1)
underTest.onPinButtonClicked(2)
underTest.onPinButtonClicked(3)
@@ -272,14 +243,9 @@
fun onAutoConfirm_whenCorrect() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
utils.authenticationRepository.setAutoConfirmEnabled(true)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
+ lockDeviceAndOpenPinBouncer()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
underTest.onPinButtonClicked(digit)
}
@@ -293,14 +259,9 @@
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val message by collectLastValue(bouncerViewModel.message)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
utils.authenticationRepository.setAutoConfirmEnabled(true)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
+ lockDeviceAndOpenPinBouncer()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
FakeAuthenticationRepository.DEFAULT_PIN.dropLast(1).forEach { digit ->
underTest.onPinButtonClicked(digit)
}
@@ -318,13 +279,7 @@
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.desiredScene)
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- underTest.onShown()
+ lockDeviceAndOpenPinBouncer()
// The user types a PIN.
FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
@@ -401,6 +356,18 @@
assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden)
}
+ private fun TestScope.lockDeviceAndOpenPinBouncer() {
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.authenticationRepository.setUnlocked(false)
+ sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
+ sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
+
+ assertThat(collectLastValue(sceneInteractor.desiredScene).invoke())
+ .isEqualTo(SceneModel(SceneKey.Bouncer))
+ underTest.onShown()
+ runCurrent()
+ }
+
companion object {
private const val ENTER_YOUR_PIN = "Enter your pin"
private const val WRONG_PIN = "Wrong pin"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt
index 49f536e..74b3fce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt
@@ -85,6 +85,7 @@
fun testCarouselScroll_shortScroll() {
whenever(mediaCarousel.isLayoutRtl).thenReturn(false)
whenever(mediaCarousel.relativeScrollX).thenReturn(300)
+ whenever(mediaCarousel.scrollX).thenReturn(300)
mediaCarousel.touchListener?.onTouchEvent(motionEventUp)
executor.runAllReady()
@@ -96,6 +97,7 @@
fun testCarouselScroll_shortScroll_isRTL() {
whenever(mediaCarousel.isLayoutRtl).thenReturn(true)
whenever(mediaCarousel.relativeScrollX).thenReturn(300)
+ whenever(mediaCarousel.scrollX).thenReturn(carouselWidth - 300)
mediaCarousel.touchListener?.onTouchEvent(motionEventUp)
executor.runAllReady()
@@ -107,6 +109,7 @@
fun testCarouselScroll_longScroll() {
whenever(mediaCarousel.isLayoutRtl).thenReturn(false)
whenever(mediaCarousel.relativeScrollX).thenReturn(600)
+ whenever(mediaCarousel.scrollX).thenReturn(600)
mediaCarousel.touchListener?.onTouchEvent(motionEventUp)
executor.runAllReady()
@@ -118,6 +121,7 @@
fun testCarouselScroll_longScroll_isRTL() {
whenever(mediaCarousel.isLayoutRtl).thenReturn(true)
whenever(mediaCarousel.relativeScrollX).thenReturn(600)
+ whenever(mediaCarousel.scrollX).thenReturn(carouselWidth - 600)
mediaCarousel.touchListener?.onTouchEvent(motionEventUp)
executor.runAllReady()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index bce4c06..3bf59ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -172,6 +172,9 @@
@Test
fun testNotAvailableControls() {
featureEnabled = false
+
+ // Destroy previous tile
+ tile.destroy()
tile = createTile()
assertThat(tile.isAvailable).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
index a0c1073..954d30ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
@@ -226,6 +226,10 @@
assertTrue(supportedTileOnlySystemUser.isAvailable());
when(mUserTracker.getUserInfo()).thenReturn(nonMainUserInfo);
assertFalse(supportedTileOnlySystemUser.isAvailable());
+
+ destroyTile(unsupportedTile);
+ destroyTile(supportedTileAllUsers);
+ destroyTile(supportedTileOnlySystemUser);
}
@Test
@@ -250,6 +254,8 @@
mTestableLooper.processAllMessages();
assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked),
dockedTile.getState().icon);
+
+ destroyTile(dockedTile);
}
private void setScreensaverEnabled(boolean enabled) {
@@ -257,6 +263,11 @@
DEFAULT_USER);
}
+ private void destroyTile(QSTileImpl<?> tile) {
+ tile.destroy();
+ mTestableLooper.processAllMessages();
+ }
+
private DreamTile constructTileForTest(boolean dreamSupported,
boolean dreamOnlyEnabledForSystemUser) {
return new DreamTile(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index df6993d..440270b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -216,7 +216,7 @@
public void testSecondaryString_rotationResolverDisabled_isEmpty() {
mTestableResources.addOverride(com.android.internal.R.bool.config_allowRotationResolver,
false);
- mLockTile = new RotationLockTile(
+ RotationLockTile otherTile = new RotationLockTile(
mHost,
mUiEventLogger,
mTestableLooper.getLooper(),
@@ -232,10 +232,12 @@
new FakeSettings()
);
- mLockTile.refreshState();
+ otherTile.refreshState();
mTestableLooper.processAllMessages();
- assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ assertEquals("", otherTile.getState().secondaryLabel.toString());
+
+ destroyTile(otherTile);
}
@Test
@@ -258,6 +260,12 @@
assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on));
}
+
+ private void destroyTile(QSTileImpl<?> tile) {
+ tile.destroy();
+ mTestableLooper.processAllMessages();
+ }
+
private void enableAutoRotation() {
when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 6b918c6..85bd92b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -76,6 +76,7 @@
* being used when the state is as required (e.g. cannot unlock an already unlocked device, cannot
* put to sleep a device that's already asleep, etc.).
*/
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class SceneFrameworkIntegrationTest : SysuiTestCase() {
@@ -481,7 +482,7 @@
bouncerSceneJob =
if (to.key == SceneKey.Bouncer) {
testScope.backgroundScope.launch {
- bouncerViewModel.authMethod.collect {
+ bouncerViewModel.authMethodViewModel.collect {
// Do nothing. Need this to turn this otherwise cold flow, hot.
}
}
@@ -556,7 +557,7 @@
assertWithMessage("Cannot enter PIN when not on the Bouncer scene!")
.that(getCurrentSceneInUi())
.isEqualTo(SceneKey.Bouncer)
- val authMethodViewModel by collectLastValue(bouncerViewModel.authMethod)
+ val authMethodViewModel by collectLastValue(bouncerViewModel.authMethodViewModel)
assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
.that(authMethodViewModel)
.isInstanceOf(PinBouncerViewModel::class.java)
@@ -613,11 +614,12 @@
private fun TestScope.dismissIme(
showImeBeforeDismissing: Boolean = true,
) {
- if (showImeBeforeDismissing) {
- bouncerViewModel.authMethod.value?.onImeVisibilityChanged(true)
+ bouncerViewModel.authMethodViewModel.value?.apply {
+ if (showImeBeforeDismissing) {
+ onImeVisibilityChanged(true)
+ }
+ onImeVisibilityChanged(false)
+ runCurrent()
}
-
- bouncerViewModel.authMethod.value?.onImeVisibilityChanged(false)
- runCurrent()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index d018cbb..2be1c09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -25,7 +25,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
@@ -35,6 +34,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -54,6 +54,7 @@
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -153,6 +154,7 @@
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
)
underTest =
ShadeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
index ac2aec6..0a10b2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.never
@@ -126,6 +127,22 @@
}
@Test
+ fun testExpandUnattachedEntry() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+
+ // First, expand the entry when it is attached.
+ gem.setGroupExpanded(summary1, true)
+ assertThat(gem.isGroupExpanded(summary1)).isTrue()
+
+ // Un-attach it, and un-expand it.
+ NotificationEntryBuilder.setNewParent(summary1, null)
+ gem.setGroupExpanded(summary1, false)
+
+ // Expanding again should throw.
+ assertThrows(IllegalArgumentException::class.java) { gem.setGroupExpanded(summary1, true) }
+ }
+
+ @Test
fun testSyncWithPipeline() {
featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
gem.attach(pipeline)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
index 37ec0e0..c1ffa64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
@@ -58,6 +58,35 @@
val noParentEntry = NotificationEntryBuilder().setParent(null).build()
assertThat(gmm.isChildInGroup(noParentEntry)).isFalse()
}
+ @Test
+ fun testIsChildInGroup_summary_old() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, false)
+
+ val groupKey = "group"
+ val summary =
+ NotificationEntryBuilder()
+ .setGroup(mContext, groupKey)
+ .setGroupSummary(mContext, true)
+ .build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
+
+ assertThat(gmm.isChildInGroup(summary)).isTrue()
+ }
+
+ @Test
+ fun testIsChildInGroup_summary_new() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+
+ val groupKey = "group"
+ val summary =
+ NotificationEntryBuilder()
+ .setGroup(mContext, groupKey)
+ .setGroupSummary(mContext, true)
+ .build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
+
+ assertThat(gmm.isChildInGroup(summary)).isFalse()
+ }
@Test
fun testIsChildInGroup_child() {
@@ -67,40 +96,78 @@
}
@Test
- fun testIsGroupSummary() {
+ fun testIsGroupSummary_topLevelEntry() {
featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
- val entry = NotificationEntryBuilder().setGroupSummary(mContext, true).build()
- assertThat(gmm.isGroupSummary(entry)).isTrue()
+ val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
+ assertThat(gmm.isGroupSummary(entry)).isFalse()
}
@Test
- fun testGetGroupSummary() {
+ fun testIsGroupSummary_summary() {
featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+ val groupKey = "group"
val summary =
NotificationEntryBuilder()
- .setGroup(mContext, "group")
+ .setGroup(mContext, groupKey)
.setGroupSummary(mContext, true)
.build()
- val groupEntry =
- GroupEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).setSummary(summary).build()
- val entry =
- NotificationEntryBuilder().setGroup(mContext, "group").setParent(groupEntry).build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
- assertThat(gmm.getGroupSummary(entry)).isEqualTo(summary)
+ assertThat(gmm.isGroupSummary(summary)).isTrue()
}
@Test
- fun testGetGroupSummary_isSummary_old() {
- featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, false)
- val entry = NotificationEntryBuilder().setGroupSummary(mContext, true).build()
+ fun testIsGroupSummary_child() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+
+ val groupKey = "group"
+ val summary =
+ NotificationEntryBuilder()
+ .setGroup(mContext, groupKey)
+ .setGroupSummary(mContext, true)
+ .build()
+ val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
+
+ assertThat(gmm.isGroupSummary(entry)).isFalse()
+ }
+
+ @Test
+ fun testGetGroupSummary_topLevelEntry() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+ val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
assertThat(gmm.getGroupSummary(entry)).isNull()
}
@Test
- fun testGetGroupSummary_isSummary_new() {
+ fun testGetGroupSummary_summary() {
featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
- val entry = NotificationEntryBuilder().setGroupSummary(mContext, true).build()
- assertThat(gmm.getGroupSummary(entry)).isEqualTo(entry)
+
+ val groupKey = "group"
+ val summary =
+ NotificationEntryBuilder()
+ .setGroup(mContext, groupKey)
+ .setGroupSummary(mContext, true)
+ .build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
+
+ assertThat(gmm.getGroupSummary(summary)).isEqualTo(summary)
+ }
+
+ @Test
+ fun testGetGroupSummary_child() {
+ featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
+
+ val groupKey = "group"
+ val summary =
+ NotificationEntryBuilder()
+ .setGroup(mContext, groupKey)
+ .setGroupSummary(mContext, true)
+ .build()
+ val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
+ GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
+
+ assertThat(gmm.getGroupSummary(entry)).isEqualTo(summary)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index f05436f..50ce265 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -71,6 +71,7 @@
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -117,6 +118,8 @@
PendingIntent mPendingIntent;
@Mock
UserTracker mUserTracker;
+ @Mock
+ DeviceProvisionedController mDeviceProvisionedController;
private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
@@ -141,7 +144,8 @@
mFlags,
mKeyguardNotificationVisibilityProvider,
mUiEventLoggerFake,
- mUserTracker);
+ mUserTracker,
+ mDeviceProvisionedController);
mNotifInterruptionStateProvider.mUseHeadsUp = true;
}
@@ -694,6 +698,25 @@
}
@Test
+ public void testShouldFullscreen_suppressedInterruptionsWhenNotProvisioned() {
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE);
+ when(mStatusBarStateController.isDreaming()).thenReturn(false);
+ when(mPowerManager.isScreenOn()).thenReturn(true);
+ when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(false);
+ mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions);
+
+ assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
+ .isEqualTo(FullScreenIntentDecision.FSI_NOT_PROVISIONED);
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isTrue();
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger).logFullscreen(entry, "FSI_NOT_PROVISIONED");
+ }
+
+ @Test
public void testShouldNotFullScreen_willHun() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
index 64d0256..7dcbd80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
@@ -73,14 +73,14 @@
controller = ChannelEditorDialogController(mContext, mockNoMan, dialogBuilder)
channel1 = NotificationChannel(TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT)
- channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_DEFAULT)
+ channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_NONE)
channelDefault = NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT)
group = NotificationChannelGroup(TEST_GROUP_ID, TEST_GROUP_NAME)
- `when`(mockNoMan.getNotificationChannelGroupsForPackage(
- eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean()))
+ `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
.thenReturn(ParceledListSlice(listOf(group)))
`when`(mockNoMan.areNotificationsEnabledForPackage(eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
@@ -89,11 +89,13 @@
@Test
fun testPrepareDialogForApp_noExtraChannels() {
+ channel1.group = group.id
+ channel2.group = group.id
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
- assertEquals(2, controller.paddedChannels.size)
+ assertEquals(2, controller.channelList.size)
}
@Test
@@ -101,39 +103,41 @@
group.addChannel(channelDefault)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channelDefault), appIcon, clickListener)
+ channelDefault, appIcon, clickListener)
assertEquals("No channels should be shown when there is only the miscellaneous channel",
- 0, controller.paddedChannels.size)
+ 0, controller.channelList.size)
}
@Test
- fun testPrepareDialogForApp_noProvidedChannels_noException() {
- group.channels = listOf()
-
- controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(), appIcon, clickListener)
- }
-
- @Test
- fun testPrepareDialogForApp_retrievesUpTo4Channels() {
+ fun testPrepareDialogForApp_AddsAllChannelsAllGroups() {
+ val group2 = NotificationChannelGroup("two", "group two")
val channel3 = NotificationChannel("test_channel_3", "Test channel 3", IMPORTANCE_DEFAULT)
+ channel3.group = group2.id
val channel4 = NotificationChannel("test_channel_4", "Test channel 4", IMPORTANCE_DEFAULT)
+ channel4.group = group.id
+ val channel5 = NotificationChannel("test_channel_5", "Test channel 5", IMPORTANCE_DEFAULT)
+ channel5.group = group.id
- group.channels = listOf(channel1, channel2, channel3, channel4)
+ `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage(
+ eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
+ .thenReturn(ParceledListSlice(listOf(group, group2)))
+
+ group.channels = listOf(channel1, channel2, channel4, channel5)
+ group2.channels = listOf(channel3)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1), appIcon, clickListener)
+ channel1, appIcon, clickListener)
- assertEquals("ChannelEditorDialog should fetch enough channels to show 4",
- 4, controller.paddedChannels.size)
+ assertEquals("ChannelEditorDialog should show all channels",
+ 5, controller.channelList.size)
}
@Test
fun testApply_demoteChannel() {
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
// propose an adjustment of channel1
controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -145,14 +149,33 @@
// Channel 2 shouldn't have changed
assertEquals("Proposed edits should take effect after apply",
+ IMPORTANCE_NONE, channel2.importance)
+ }
+
+ @Test
+ fun testApply_promoteChannel() {
+ group.channels = listOf(channel1, channel2)
+ controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
+ channel1, appIcon, clickListener)
+
+ // propose an adjustment of channel1
+ controller.proposeEditForChannel(channel2, IMPORTANCE_DEFAULT)
+
+ controller.apply()
+
+ assertEquals("Proposed edits should take effect after apply",
IMPORTANCE_DEFAULT, channel2.importance)
+
+ // Channel 1 shouldn't have changed
+ assertEquals("Proposed edits should take effect after apply",
+ IMPORTANCE_DEFAULT, channel1.importance)
}
@Test
fun testApply_demoteApp() {
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
controller.proposeSetAppNotificationsEnabled(false)
controller.apply()
@@ -168,7 +191,7 @@
.thenReturn(false)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, clickListener)
+ channel1, appIcon, clickListener)
controller.proposeSetAppNotificationsEnabled(true)
controller.apply()
@@ -181,7 +204,7 @@
// GIVEN editor dialog
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, null)
+ channel1, appIcon, null)
// WHEN user taps settings
// Pass in any old view, it should never actually be used
@@ -197,7 +220,7 @@
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, null)
+ channel1, appIcon, null)
// WHEN the user proposes a change
controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -214,7 +237,7 @@
group.channels = listOf(channel1, channel2)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
- setOf(channel1, channel2), appIcon, null)
+ channel1, appIcon, null)
// WHEN the user proposes a change
controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -236,7 +259,6 @@
const val TEST_PACKAGE_NAME = "test_package"
const val TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME
const val TEST_UID = 1
- const val MULTIPLE_CHANNEL_COUNT = 2
const val TEST_CHANNEL = "test_channel"
const val TEST_CHANNEL_NAME = "Test Channel Name"
const val TEST_CHANNEL2 = "test_channel2"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index ac8b42a..e373143 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -481,33 +481,6 @@
}
@Test
- public void testGetNumUniqueChildren_defaultChannel() throws Exception {
- ExpandableNotificationRow groupRow = mNotificationTestHelper.createGroup();
-
- assertEquals(1, groupRow.getNumUniqueChannels());
- }
-
- @Test
- public void testGetNumUniqueChildren_multiChannel() throws Exception {
- ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
-
- List<ExpandableNotificationRow> childRows =
- group.getChildrenContainer().getAttachedChildren();
- // Give each child a unique channel id/name.
- int i = 0;
- for (ExpandableNotificationRow childRow : childRows) {
- modifyRanking(childRow.getEntry())
- .setChannel(
- new NotificationChannel(
- "id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT))
- .build();
- i++;
- }
-
- assertEquals(3, group.getNumUniqueChannels());
- }
-
- @Test
public void testIconScrollXAfterTranslationAndReset() throws Exception {
ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 9e0f83c..0f1e63f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -462,7 +462,6 @@
eq(mChannelEditorDialogController),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anySet(),
eq(entry),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -496,7 +495,6 @@
eq(mChannelEditorDialogController),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anySet(),
eq(entry),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -528,7 +526,6 @@
eq(mChannelEditorDialogController),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
- anySet(),
eq(entry),
any(NotificationInfo.OnSettingsClickListener.class),
any(NotificationInfo.OnAppSettingsClickListener.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index f0b4dd4..b59385c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -92,7 +92,6 @@
private static final String TEST_PACKAGE_NAME = "test_package";
private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME;
private static final int TEST_UID = 1;
- private static final int MULTIPLE_CHANNEL_COUNT = 2;
private static final String TEST_CHANNEL = "test_channel";
private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
@@ -100,8 +99,6 @@
private NotificationInfo mNotificationInfo;
private NotificationChannel mNotificationChannel;
private NotificationChannel mDefaultNotificationChannel;
- private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>();
- private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
@@ -162,11 +159,9 @@
// Some test channels.
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
- mNotificationChannelSet.add(mNotificationChannel);
mDefaultNotificationChannel = new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
IMPORTANCE_LOW);
- mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
new Notification(), UserHandle.getUserHandleForUid(TEST_UID), null, 0);
mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
@@ -185,7 +180,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -212,7 +206,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -235,7 +228,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -267,7 +259,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
entry,
null,
null,
@@ -291,7 +282,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -320,7 +310,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -344,7 +333,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -367,7 +355,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mDefaultNotificationChannel,
- mDefaultNotificationChannelSet,
mEntry,
null,
null,
@@ -394,7 +381,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mDefaultNotificationChannel,
- mDefaultNotificationChannelSet,
mEntry,
null,
null,
@@ -417,7 +403,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -441,7 +426,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -470,7 +454,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -494,7 +477,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -519,7 +501,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -536,7 +517,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> { },
null,
@@ -551,86 +531,6 @@
}
@Test
- public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
- final CountDownLatch latch = new CountDownLatch(1);
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mOnUserInteractionCallback,
- mChannelEditorDialogController,
- TEST_PACKAGE_NAME, mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
- mEntry,
- (View v, NotificationChannel c, int appUid) -> {
- assertEquals(null, c);
- latch.countDown();
- },
- null,
- mUiEventLogger,
- true,
- true,
- true,
- mAssistantFeedbackController,
- mMetricsLogger);
-
- mNotificationInfo.findViewById(R.id.info).performClick();
- // Verify that listener was triggered.
- assertEquals(0, latch.getCount());
- }
-
- @Test
- @UiThreadTest
- public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels()
- throws Exception {
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mOnUserInteractionCallback,
- mChannelEditorDialogController,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
- mEntry,
- null,
- null,
- mUiEventLogger,
- true,
- false,
- true,
- mAssistantFeedbackController,
- mMetricsLogger);
- final TextView channelNameView =
- mNotificationInfo.findViewById(R.id.channel_name);
- assertEquals(GONE, channelNameView.getVisibility());
- }
-
- @Test
- @UiThreadTest
- public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mOnUserInteractionCallback,
- mChannelEditorDialogController,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
- mEntry,
- null,
- null,
- mUiEventLogger,
- true,
- false,
- true,
- mAssistantFeedbackController,
- mMetricsLogger);
- assertEquals(GONE, mNotificationInfo.findViewById(
- R.id.interruptiveness_settings).getVisibility());
- assertEquals(VISIBLE, mNotificationInfo.findViewById(
- R.id.non_configurable_multichannel_text).getVisibility());
- }
-
- @Test
public void testBindNotification_whenAppUnblockable() throws Exception {
mNotificationInfo.bindNotification(
mMockPackageManager,
@@ -639,7 +539,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -683,7 +582,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -727,7 +625,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -755,7 +652,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -778,7 +674,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -803,7 +698,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -825,7 +719,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -847,7 +740,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -869,7 +761,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -893,7 +784,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -918,7 +808,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -946,7 +835,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -974,7 +862,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1003,7 +890,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1031,7 +917,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1067,7 +952,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1096,7 +980,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1138,7 +1021,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1176,7 +1058,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1209,7 +1090,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1246,7 +1126,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1285,7 +1164,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1317,7 +1195,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1356,7 +1233,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1386,7 +1262,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1418,7 +1293,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1454,7 +1328,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1488,7 +1361,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1522,7 +1394,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1549,7 +1420,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
null,
@@ -1562,21 +1432,4 @@
assertFalse(mNotificationInfo.willBeRemoved());
}
-
- private Set<NotificationChannel> createMultipleChannelSet(int howMany) {
- Set<NotificationChannel> multiChannelSet = new HashSet<>();
- for (int i = 0; i < howMany; i++) {
- if (i == 0) {
- multiChannelSet.add(mNotificationChannel);
- continue;
- }
-
- NotificationChannel channel = new NotificationChannel(
- TEST_CHANNEL, TEST_CHANNEL_NAME + i, IMPORTANCE_LOW);
-
- multiChannelSet.add(channel);
- }
-
- return multiChannelSet;
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
index e42ce27..ccedd36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -83,8 +83,6 @@
private PartialConversationInfo mInfo;
private NotificationChannel mNotificationChannel;
private NotificationChannel mDefaultNotificationChannel;
- private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>();
- private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
@@ -144,11 +142,9 @@
// Some test channels.
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
- mNotificationChannelSet.add(mNotificationChannel);
mDefaultNotificationChannel = new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
IMPORTANCE_LOW);
- mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
.setContentTitle(new SpannableString("title"))
.build();
@@ -166,7 +162,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -185,7 +180,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -202,7 +196,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -228,7 +221,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
entry,
null,
true,
@@ -247,7 +239,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -271,7 +262,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -294,7 +284,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
@@ -311,7 +300,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
@@ -330,7 +318,6 @@
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
mNotificationChannel,
- mNotificationChannelSet,
mEntry,
null,
true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 5c3dde5..0b171b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -364,7 +364,8 @@
mock(NotifPipelineFlags.class),
mock(KeyguardNotificationVisibilityProvider.class),
mock(UiEventLogger.class),
- mUserTracker);
+ mUserTracker,
+ mDeviceProvisionedController);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -1169,7 +1170,8 @@
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DeviceProvisionedController deviceProvisionedController) {
super(
contentResolver,
powerManager,
@@ -1183,7 +1185,8 @@
flags,
keyguardNotificationVisibilityProvider,
uiEventLogger,
- userTracker
+ userTracker,
+ deviceProvisionedController
);
mUseHeadsUp = true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index c8f28bc..4ccbd1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -65,7 +65,7 @@
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
- private final RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
+ private final FakeRotationPolicy mFakeRotationPolicy = new FakeRotationPolicy();
private DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
private DeviceStateRotationLockSettingsManager mSettingsManager;
@@ -324,13 +324,21 @@
private boolean mRotationLock;
- @Override
public void setRotationLock(boolean enabled) {
- mRotationLock = enabled;
+ setRotationLock(enabled, /* caller= */ "FakeRotationPolicy");
}
@Override
+ public void setRotationLock(boolean enabled, String caller) {
+ mRotationLock = enabled;
+ }
+
public void setRotationLockAtAngle(boolean enabled, int rotation) {
+ setRotationLockAtAngle(enabled, rotation, /* caller= */ "FakeRotationPolicy");
+ }
+
+ @Override
+ public void setRotationLockAtAngle(boolean enabled, int rotation, String caller) {
mRotationLock = enabled;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index bbc49c8..af941d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -34,7 +34,6 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.GuestResetOrExitSessionReceiver
import com.android.systemui.GuestResumeSessionReceiver
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Text
@@ -45,6 +44,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -1120,6 +1120,7 @@
),
uiEventLogger = uiEventLogger,
featureFlags = featureFlags,
+ userRestrictionChecker = mock(),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 2433e12..a8db368 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -267,6 +267,7 @@
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 8c88f95..6932f5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -45,6 +45,7 @@
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import com.android.systemui.user.shared.model.UserActionModel
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -176,6 +177,7 @@
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor = guestUserInteractor,
uiEventLogger = uiEventLogger,
+ userRestrictionChecker = mock(),
),
guestUserInteractor = guestUserInteractor,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
index 94ed608..e59e475 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
@@ -15,104 +15,119 @@
*/
package com.android.systemui.wmshell
-import android.content.ContentResolver
import android.content.Context
+import android.content.Intent
import android.content.SharedPreferences
+import android.content.pm.ShortcutInfo
+import android.content.res.Resources
+import android.os.UserHandle
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.core.content.edit
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 com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
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 sharedPrefs: SharedPreferences
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)
+ sharedPrefs = mContext.getSharedPreferences(mContext.packageName, Context.MODE_PRIVATE)
+ sharedPrefs.edit {
+ remove(PREF_STACK_EDUCATION)
+ remove(PREF_MANAGED_EDUCATION)
+ }
+ sut = BubbleEducationController(mContext)
}
@Test
fun testSeenStackEducation_read() {
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ sharedPrefs.edit { putBoolean(PREF_STACK_EDUCATION, 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)
+ assertThat(sharedPrefs.getBoolean(PREF_STACK_EDUCATION, false)).isTrue()
}
@Test
fun testSeenManageEducation_read() {
- Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+ sharedPrefs.edit { putBoolean(PREF_MANAGED_EDUCATION, 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)
+ assertThat(sharedPrefs.getBoolean(PREF_MANAGED_EDUCATION, false)).isTrue()
}
@Test
fun testShouldShowStackEducation() {
- val bubble = Mockito.mock(Bubble::class.java)
// When bubble is null
assertEquals(sut.shouldShowStackEducation(null), false)
+ var bubble = createFakeBubble(isConversational = 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)
+ bubble = createFakeBubble(isConversational = true)
+ sharedPrefs.edit { putBoolean(PREF_STACK_EDUCATION, 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)
+ sharedPrefs.edit { remove(PREF_STACK_EDUCATION) }
assertEquals(sut.shouldShowStackEducation(bubble), true)
}
@Test
fun testShouldShowManageEducation() {
- val bubble = Mockito.mock(Bubble::class.java)
// When bubble is null
assertEquals(sut.shouldShowManageEducation(null), false)
+ var bubble = createFakeBubble(isConversational = 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)
+ bubble = createFakeBubble(isConversational = true)
+ sharedPrefs.edit { putBoolean(PREF_MANAGED_EDUCATION, 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)
+ sharedPrefs.edit { remove(PREF_MANAGED_EDUCATION) }
assertEquals(sut.shouldShowManageEducation(bubble), true)
}
+
+ private fun createFakeBubble(isConversational: Boolean): Bubble {
+ return if (isConversational) {
+ val shortcutInfo = ShortcutInfo.Builder(mContext, "fakeId").build()
+ Bubble(
+ "key",
+ shortcutInfo,
+ /* desiredHeight= */ 6,
+ Resources.ID_NULL,
+ "title",
+ /* taskId= */ 0,
+ "locus",
+ /* isDismissable= */ true,
+ directExecutor()
+ ) {}
+ } else {
+ val intent = Intent(Intent.ACTION_VIEW).setPackage(mContext.packageName)
+ Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index d8511e8..65b8b55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -126,6 +126,7 @@
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -391,7 +392,8 @@
mock(NotifPipelineFlags.class),
mock(KeyguardNotificationVisibilityProvider.class),
mock(UiEventLogger.class),
- mock(UserTracker.class)
+ mock(UserTracker.class),
+ mock(DeviceProvisionedController.class)
);
mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
index 4e14bbf6..0df235d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -48,7 +49,8 @@
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
UiEventLogger uiEventLogger,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DeviceProvisionedController deviceProvisionedController) {
super(contentResolver,
powerManager,
ambientDisplayConfiguration,
@@ -61,7 +63,8 @@
flags,
keyguardNotificationVisibilityProvider,
uiEventLogger,
- userTracker);
+ userTracker,
+ deviceProvisionedController);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index ceab8e9..945aaed 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -50,6 +50,7 @@
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.utils.UserRestrictionChecker
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.test.TestScope
import org.mockito.Mockito.mock
@@ -137,6 +138,7 @@
refreshUsersScheduler = mock(RefreshUsersScheduler::class.java),
guestUserInteractor = mock(GuestUserInteractor::class.java),
uiEventLogger = mock(UiEventLogger::class.java),
+ userRestrictionChecker = mock(UserRestrictionChecker::class.java),
)
return WithDependencies(
trustRepository = trustRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 1620dc27..69c89e8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -206,6 +206,7 @@
return BouncerViewModel(
applicationContext = context,
applicationScope = applicationScope(),
+ mainDispatcher = testDispatcher,
bouncerInteractor = bouncerInteractor,
authenticationInteractor = authenticationInteractor,
flags = sceneContainerFlags,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
index 4f9cb35..be57658 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -46,7 +46,7 @@
}
@Override
- public void setRotationLocked(boolean locked) {
+ public void setRotationLocked(boolean locked, String caller) {
}
@@ -56,7 +56,7 @@
}
@Override
- public void setRotationLockedAtAngle(boolean locked, int rotation) {
+ public void setRotationLockedAtAngle(boolean locked, int rotation, String caller) {
}
}
diff --git a/services/Android.bp b/services/Android.bp
index f237095..3ae9360 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -277,4 +277,5 @@
tag: ".removed-api.txt",
},
],
+ api_surface: "system-server",
}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 0480c22..11189cf 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -25,5 +25,12 @@
name: "send_a11y_events_based_on_state"
namespace: "accessibility"
description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness."
-bug: "295575684"
-}
\ No newline at end of file
+ bug: "295575684"
+}
+
+flag {
+ name: "add_window_token_without_lock"
+ namespace: "accessibility"
+ description: "Calls WMS.addWindowToken without holding A11yManagerService#mLock"
+ bug: "297972548"
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 05b6eb4..fa73cff 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1506,11 +1506,17 @@
}
}
- public void onAdded() {
+ /**
+ * Called when the connection is first created. Add a window token for all known displays.
+ * <p>
+ * <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService
+ * lock because this calls out to WindowManagerService.
+ */
+ void addWindowTokensForAllDisplays() {
final Display[] displays = mDisplayManager.getDisplays();
for (int i = 0; i < displays.length; i++) {
final int displayId = displays[i].getDisplayId();
- onDisplayAdded(displayId);
+ addWindowTokenForDisplay(displayId);
}
}
@@ -1518,9 +1524,13 @@
* Called whenever a logical display has been added to the system. Add a window token for adding
* an accessibility overlay.
*
+ * <p>
+ * <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService
+ * lock because this calls out to WindowManagerService.
+ *
* @param displayId The id of the logical display that was added.
*/
- public void onDisplayAdded(int displayId) {
+ void addWindowTokenForDisplay(int displayId) {
final long identity = Binder.clearCallingIdentity();
try {
final IBinder overlayWindowToken = new Binder();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 93ba362..60d4ee6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -154,6 +154,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
+import com.android.internal.util.Preconditions;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -4500,6 +4501,20 @@
private int mSystemUiUid = 0;
AccessibilityDisplayListener(Context context, Handler handler) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ // Avoid concerns about one thread adding displays while another thread removes
+ // them by ensuring the looper is the main looper and the DisplayListener
+ // callbacks are always executed on the one main thread.
+ final boolean isMainHandler = handler.getLooper() == Looper.getMainLooper();
+ final String errorMessage =
+ "AccessibilityDisplayListener must use the main handler";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainHandler, errorMessage);
+ } else if (!isMainHandler) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
+ }
+
mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
mDisplayManager.registerDisplayListener(this, handler);
initializeDisplayList();
@@ -4541,11 +4556,21 @@
@Override
public void onDisplayAdded(int displayId) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+ final String errorMessage = "onDisplayAdded must be called from the main thread";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainThread, errorMessage);
+ } else if (!isMainThread) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
+ }
final Display display = mDisplayManager.getDisplay(displayId);
if (!isValidDisplay(display)) {
return;
}
+ final List<AccessibilityServiceConnection> services;
synchronized (mLock) {
mDisplaysList.add(display);
mA11yOverlayLayers.put(
@@ -4554,21 +4579,42 @@
mInputFilter.onDisplayAdded(display);
}
AccessibilityUserState userState = getCurrentUserStateLocked();
- if (displayId != Display.DEFAULT_DISPLAY) {
- final List<AccessibilityServiceConnection> services = userState.mBoundServices;
- for (int i = 0; i < services.size(); i++) {
- AccessibilityServiceConnection boundClient = services.get(i);
- boundClient.onDisplayAdded(displayId);
+ if (Flags.addWindowTokenWithoutLock()) {
+ services = new ArrayList<>(userState.mBoundServices);
+ } else {
+ services = userState.mBoundServices;
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ for (int i = 0; i < services.size(); i++) {
+ AccessibilityServiceConnection boundClient = services.get(i);
+ boundClient.addWindowTokenForDisplay(displayId);
+ }
}
}
updateMagnificationLocked(userState);
updateWindowsForAccessibilityCallbackLocked(userState);
notifyClearAccessibilityCacheLocked();
}
+ if (Flags.addWindowTokenWithoutLock()) {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ for (int i = 0; i < services.size(); i++) {
+ AccessibilityServiceConnection boundClient = services.get(i);
+ boundClient.addWindowTokenForDisplay(displayId);
+ }
+ }
+ }
}
@Override
public void onDisplayRemoved(int displayId) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+ final String errorMessage = "onDisplayRemoved must be called from the main thread";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainThread, errorMessage);
+ } else if (!isMainThread) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
+ }
synchronized (mLock) {
if (!removeDisplayFromList(displayId)) {
return;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 9e70073..7a2a602 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -44,7 +44,6 @@
import android.view.Display;
import android.view.MotionEvent;
-
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -169,6 +168,10 @@
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
+ AccessibilityUserState userState = mUserStateWeakReference.get();
+ if (userState != null && Flags.addWindowTokenWithoutLock()) {
+ addWindowTokensForAllDisplays();
+ }
synchronized (mLock) {
if (mService != service) {
if (mService != null) {
@@ -184,7 +187,6 @@
}
}
mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
- AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
userState.addServiceLocked(this);
mSystemSupport.onClientChangeLocked(false);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index ab6cc71..693526a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -224,7 +224,9 @@
void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
if (!mBoundServices.contains(serviceConnection)) {
- serviceConnection.onAdded();
+ if (!Flags.addWindowTokenWithoutLock()) {
+ serviceConnection.addWindowTokensForAllDisplays();
+ }
mBoundServices.add(serviceConnection);
mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection);
mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 208acdf..53c629a 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -25,9 +25,11 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
+import android.os.Looper;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -35,6 +37,7 @@
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
import com.android.server.utils.Slogf;
import com.android.server.wm.WindowManagerInternal;
@@ -98,46 +101,47 @@
accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s) when called by user %d",
accessibilityServiceInfo.getId(), Binder.getCallingUserHandle().getIdentifier());
- synchronized (mLock) {
- if (mUiAutomationService != null) {
- throw new IllegalStateException(
- "UiAutomationService " + mUiAutomationService.mServiceInterface
- + "already registered!");
- }
-
- try {
- owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!",
- re);
- return;
- }
-
- mUiAutomationFlags = flags;
- mSystemSupport = systemSupport;
- // Ignore registering UiAutomation if it is not allowed to use the accessibility
- // subsystem.
- if (!useAccessibility()) {
- return;
- }
- mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
- mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
- systemActionPerformer, awm);
- mUiAutomationServiceOwner = owner;
- mUiAutomationService.mServiceInterface = serviceClient;
- try {
- mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
- 0);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Failed registering death link: " + re);
- destroyUiAutomationService();
- return;
- }
-
- mUiAutomationService.onAdded();
-
- mUiAutomationService.connectServiceUnknownThread();
+ if (mUiAutomationService != null) {
+ throw new IllegalStateException(
+ "UiAutomationService " + mUiAutomationService.mServiceInterface
+ + "already registered!");
}
+
+ try {
+ owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!",
+ re);
+ return;
+ }
+
+ mUiAutomationFlags = flags;
+ mSystemSupport = systemSupport;
+ // Ignore registering UiAutomation if it is not allowed to use the accessibility
+ // subsystem.
+ if (!useAccessibility()) {
+ return;
+ }
+ mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
+ mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
+ systemActionPerformer, awm);
+ mUiAutomationServiceOwner = owner;
+ mUiAutomationService.mServiceInterface = serviceClient;
+ try {
+ mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
+ 0);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Failed registering death link: " + re);
+ destroyUiAutomationService();
+ return;
+ }
+
+ if (!Flags.addWindowTokenWithoutLock()) {
+ mUiAutomationService.addWindowTokensForAllDisplays();
+ }
+ // UiAutomationService#connectServiceUnknownThread posts to a handler
+ // so this call should return immediately.
+ mUiAutomationService.connectServiceUnknownThread();
}
void unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient) {
@@ -253,6 +257,13 @@
super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
securityPolicy, systemSupport, trace, windowManagerInternal,
systemActionPerformer, awm);
+ final boolean isMainHandler = mainHandler.getLooper() == Looper.getMainLooper();
+ final String errorMessage = "UiAutomationService must use the main handler";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainHandler, errorMessage);
+ } else if (!isMainHandler) {
+ Slog.e(LOG_TAG, errorMessage);
+ }
mMainHandler = mainHandler;
setDisplayTypes(DISPLAY_TYPE_DEFAULT | DISPLAY_TYPE_PROXY);
}
@@ -274,6 +285,9 @@
// If the serviceInterface is null, the UiAutomation has been shut down on
// another thread.
if (serviceInterface != null) {
+ if (Flags.addWindowTokenWithoutLock()) {
+ mUiAutomationService.addWindowTokensForAllDisplays();
+ }
if (mTrace.isA11yTracingEnabledForTypes(
AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
@@ -286,7 +300,7 @@
mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
}
} catch (RemoteException re) {
- Slog.w(LOG_TAG, "Error initialized connection", re);
+ Slog.w(LOG_TAG, "Error initializing connection", re);
destroyUiAutomationService();
}
});
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 2ca84f8..30b9d0b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -40,6 +40,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
@@ -166,9 +167,12 @@
@VisibleForTesting boolean mIsSinglePanningEnabled;
- /**
- * FullScreenMagnificationGestureHandler Constructor.
- */
+ private final FullScreenMagnificationVibrationHelper mFullScreenMagnificationVibrationHelper;
+
+ @VisibleForTesting final OverscrollHandler mOverscrollHandler;
+
+ private final boolean mIsWatch;
+
public FullScreenMagnificationGestureHandler(@UiContext Context context,
FullScreenMagnificationController fullScreenMagnificationController,
AccessibilityTraceManager trace,
@@ -254,11 +258,13 @@
mDetectingState = new DetectingState(context);
mViewportDraggingState = new ViewportDraggingState();
mPanningScalingState = new PanningScalingState(context);
- mSinglePanningState = new SinglePanningState(context,
- fullScreenMagnificationVibrationHelper);
+ mSinglePanningState = new SinglePanningState(context);
+ mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper;
setSinglePanningEnabled(
context.getResources()
.getBoolean(R.bool.config_enable_a11y_magnification_single_panning));
+ mOverscrollHandler = new OverscrollHandler();
+ mIsWatch = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
if (mDetectShortcutTrigger) {
mScreenStateReceiver = new ScreenStateReceiver(context, this);
@@ -468,15 +474,21 @@
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
int action = event.getActionMasked();
-
if (action == ACTION_POINTER_UP
&& event.getPointerCount() == 2 // includes the pointer currently being released
&& mPreviousState == mViewportDraggingState) {
-
+ // if feature flag is enabled, currently only true on watches
+ if (mIsSinglePanningEnabled) {
+ mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
+ mOverscrollHandler.clearEdgeState();
+ }
persistScaleAndTransitionTo(mViewportDraggingState);
-
} else if (action == ACTION_UP || action == ACTION_CANCEL) {
-
+ // if feature flag is enabled, currently only true on watches
+ if (mIsSinglePanningEnabled) {
+ mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
+ mOverscrollHandler.clearEdgeState();
+ }
persistScaleAndTransitionTo(mDetectingState);
}
}
@@ -502,7 +514,12 @@
}
public void persistScaleAndTransitionTo(State state) {
- mFullScreenMagnificationController.persistScale(mDisplayId);
+ // If device is a watch don't change user settings scale. On watches, warp effect
+ // is enabled and the current display scale could be differ from the default user
+ // settings scale (should not change the scale due to the warp effect)
+ if (!mIsWatch) {
+ mFullScreenMagnificationController.persistScale(mDisplayId);
+ }
clear();
transitionTo(state);
}
@@ -546,6 +563,9 @@
}
mFullScreenMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX,
distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ if (mIsSinglePanningEnabled) {
+ mOverscrollHandler.onScrollStateChanged(first, second);
+ }
return /* event consumed: */ true;
}
@@ -893,6 +913,11 @@
if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
transitionToViewportDraggingStateAndClear(event);
} else if (isActivated() && event.getPointerCount() == 2) {
+ if (mIsSinglePanningEnabled
+ && overscrollState(event, mFirstPointerDownLocation)
+ == OVERSCROLL_VERTICAL_EDGE) {
+ transitionToDelegatingStateAndClear();
+ }
//Primary pointer is swiping, so transit to PanningScalingState
transitToPanningScalingStateAndClear();
} else if (mIsSinglePanningEnabled
@@ -1264,6 +1289,7 @@
+ ", mMagnificationController=" + mFullScreenMagnificationController
+ ", mDisplayId=" + mDisplayId
+ ", mIsSinglePanningEnabled=" + mIsSinglePanningEnabled
+ + ", mOverscrollHandler=" + mOverscrollHandler
+ '}';
}
@@ -1411,32 +1437,8 @@
private final GestureDetector mScrollGestureDetector;
private MotionEventInfo mEvent;
- private final FullScreenMagnificationVibrationHelper
- mFullScreenMagnificationVibrationHelper;
-
- @VisibleForTesting int mOverscrollState;
-
- // mPivotEdge is the point on the edge of the screen when the magnified view hits the edge
- // This point sets the center of magnified view when warp/scale effect is triggered
- private final PointF mPivotEdge;
-
- // mReachedEdgeCoord is the user's pointer location on the screen when the magnified view
- // has hit the edge
- private final PointF mReachedEdgeCoord;
- // mEdgeCooldown value will be set to true when user hits the edge and will be set to false
- // once the user moves x distance away from the edge. This is so that vibrating haptic
- // doesn't get triggered by slight movements
- private boolean mEdgeCooldown;
-
- SinglePanningState(
- Context context,
- FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper) {
+ SinglePanningState(Context context) {
mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
- mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper;
- mOverscrollState = OVERSCROLL_NONE;
- mPivotEdge = new PointF(Float.NaN, Float.NaN);
- mReachedEdgeCoord = new PointF(Float.NaN, Float.NaN);
- mEdgeCooldown = false;
}
@Override
@@ -1445,17 +1447,8 @@
switch (action) {
case ACTION_UP:
case ACTION_CANCEL:
- if (mOverscrollState == OVERSCROLL_LEFT_EDGE
- || mOverscrollState == OVERSCROLL_RIGHT_EDGE) {
- mFullScreenMagnificationController.setScaleAndCenter(
- mDisplayId,
- mFullScreenMagnificationController.getPersistedScale(mDisplayId),
- mPivotEdge.x,
- mPivotEdge.y,
- true,
- AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
- }
- clear();
+ mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
+ mOverscrollHandler.clearEdgeState();
transitionTo(mDetectingState);
break;
}
@@ -1482,23 +1475,7 @@
+ " isAtEdge: "
+ mFullScreenMagnificationController.isAtEdge(mDisplayId));
}
- if (mFullScreenMagnificationController.isAtEdge(mDisplayId)) {
- playEdgeVibration(second);
- setPivotEdge();
- }
- if (mOverscrollState == OVERSCROLL_NONE) {
- mOverscrollState = overscrollState(second, new PointF(first.getX(), first.getY()));
- } else if (mOverscrollState == OVERSCROLL_VERTICAL_EDGE) {
- clear();
- transitionTo(mDelegatingState);
- } else {
- boolean reset = warpEffectReset(second);
- if (reset) {
- mFullScreenMagnificationController.reset(mDisplayId, /* animate */ true);
- clear();
- transitionTo(mDetectingState);
- }
- }
+ mOverscrollHandler.onScrollStateChanged(first, second);
return /* event consumed: */ true;
}
@@ -1515,35 +1492,37 @@
public String toString() {
return "SinglePanningState{"
+ "isEdgeOfView="
- + mFullScreenMagnificationController.isAtEdge(mDisplayId)
- + "overscrollStatus="
- + mOverscrollState
- + "}";
+ + mFullScreenMagnificationController.isAtEdge(mDisplayId);
}
- private void playEdgeVibration(MotionEvent event) {
- if (mOverscrollState == OVERSCROLL_NONE) {
- vibrateIfNeeded(event);
- }
+ }
+
+ /** Overscroll Handler handles the logic when user is at the edge and scrolls past an edge */
+ final class OverscrollHandler {
+
+ @VisibleForTesting int mOverscrollState;
+
+ // mPivotEdge is the point on the edge of the screen when the magnified view hits the edge
+ // This point sets the center of magnified view when warp/scale effect is triggered
+ private final PointF mPivotEdge;
+
+ // mReachedEdgeCoord is the user's pointer location on the screen when the magnified view
+ // has hit the edge
+ private final PointF mReachedEdgeCoord;
+
+ // mEdgeCooldown value will be set to true when user hits the edge and will be set to false
+ // once the user moves x distance away from the edge. This is so that vibrating haptic
+ // doesn't get triggered by slight movements
+ private boolean mEdgeCooldown;
+
+ OverscrollHandler() {
+ mOverscrollState = OVERSCROLL_NONE;
+ mPivotEdge = new PointF(Float.NaN, Float.NaN);
+ mReachedEdgeCoord = new PointF(Float.NaN, Float.NaN);
+ mEdgeCooldown = false;
}
- private void setPivotEdge() {
- if (!pointerValid(mPivotEdge)) {
- Rect bounds = new Rect();
- mFullScreenMagnificationController.getMagnificationBounds(mDisplayId, bounds);
- if (mOverscrollState == OVERSCROLL_LEFT_EDGE) {
- mPivotEdge.set(
- bounds.left,
- mFullScreenMagnificationController.getCenterY(mDisplayId));
- } else if (mOverscrollState == OVERSCROLL_RIGHT_EDGE) {
- mPivotEdge.set(
- bounds.right,
- mFullScreenMagnificationController.getCenterY(mDisplayId));
- }
- }
- }
-
- private boolean warpEffectReset(MotionEvent second) {
+ protected boolean warpEffectReset(MotionEvent second) {
float scale = calculateOverscrollScale(second);
if (scale < 0) return false;
mFullScreenMagnificationController.setScaleAndCenter(
@@ -1566,7 +1545,7 @@
float overshootDistX = second.getX() - mReachedEdgeCoord.x;
if ((mOverscrollState == OVERSCROLL_LEFT_EDGE && overshootDistX < 0)
|| (mOverscrollState == OVERSCROLL_RIGHT_EDGE && overshootDistX > 0)) {
- clear();
+ clearEdgeState();
return -1.0f;
}
float overshootDistY = second.getY() - mReachedEdgeCoord.y;
@@ -1611,21 +1590,109 @@
}
private void vibrateIfNeeded(MotionEvent event) {
+ if (mOverscrollState != OVERSCROLL_NONE) {
+ return;
+ }
if ((mFullScreenMagnificationController.isAtLeftEdge(mDisplayId)
|| mFullScreenMagnificationController.isAtRightEdge(mDisplayId))
&& !mEdgeCooldown) {
mFullScreenMagnificationVibrationHelper.vibrateIfSettingEnabled();
+ }
+ }
+
+ private void setPivotEdge(MotionEvent event) {
+ if (!pointerValid(mPivotEdge)) {
+ Rect bounds = new Rect();
+ mFullScreenMagnificationController.getMagnificationBounds(mDisplayId, bounds);
+ if (mOverscrollState == OVERSCROLL_LEFT_EDGE) {
+ mPivotEdge.set(
+ bounds.left, mFullScreenMagnificationController.getCenterY(mDisplayId));
+ } else if (mOverscrollState == OVERSCROLL_RIGHT_EDGE) {
+ mPivotEdge.set(
+ bounds.right,
+ mFullScreenMagnificationController.getCenterY(mDisplayId));
+ }
mReachedEdgeCoord.set(event.getX(), event.getY());
mEdgeCooldown = true;
}
}
- @Override
- public void clear() {
+ private void onScrollStateChanged(MotionEvent first, MotionEvent second) {
+ if (mFullScreenMagnificationController.isAtEdge(mDisplayId)) {
+ vibrateIfNeeded(second);
+ setPivotEdge(second);
+ }
+ switch (mOverscrollState) {
+ case OVERSCROLL_NONE:
+ onNoOverscroll(first, second);
+ break;
+ case OVERSCROLL_VERTICAL_EDGE:
+ onVerticalOverscroll();
+ break;
+ case OVERSCROLL_LEFT_EDGE:
+ case OVERSCROLL_RIGHT_EDGE:
+ onHorizontalOverscroll(second);
+ break;
+ default:
+ Slog.d(mLogTag, "Invalid overscroll state");
+ break;
+ }
+ }
+
+ public void onNoOverscroll(MotionEvent first, MotionEvent second) {
+ mOverscrollState = overscrollState(second, new PointF(first.getX(), first.getY()));
+ }
+
+ public void onVerticalOverscroll() {
+ clearEdgeState();
+ transitionTo(mDelegatingState);
+ }
+
+ public void onHorizontalOverscroll(MotionEvent second) {
+ boolean reset = warpEffectReset(second);
+ if (reset) {
+ mFullScreenMagnificationController.reset(mDisplayId, /* animate */ true);
+ clearEdgeState();
+ transitionTo(mDelegatingState);
+ }
+ }
+
+ private void setScaleAndCenterToEdgeIfNeeded() {
+ if (mOverscrollState == OVERSCROLL_LEFT_EDGE
+ || mOverscrollState == OVERSCROLL_RIGHT_EDGE) {
+ mFullScreenMagnificationController.setScaleAndCenter(
+ mDisplayId,
+ mFullScreenMagnificationController.getPersistedScale(mDisplayId),
+ mPivotEdge.x,
+ mPivotEdge.y,
+ true,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ }
+ }
+
+ private void clearEdgeState() {
mOverscrollState = OVERSCROLL_NONE;
mPivotEdge.set(Float.NaN, Float.NaN);
mReachedEdgeCoord.set(Float.NaN, Float.NaN);
mEdgeCooldown = false;
}
+
+ @Override
+ public String toString() {
+ return "OverscrollHandler {"
+ + "mOverscrollState="
+ + mOverscrollState
+ + "mPivotEdge.x="
+ + mPivotEdge.x
+ + "mPivotEdge.y="
+ + mPivotEdge.y
+ + "mReachedEdgeCoord.x="
+ + mReachedEdgeCoord.x
+ + "mReachedEdgeCoord.y="
+ + mReachedEdgeCoord.y
+ + "mEdgeCooldown="
+ + mEdgeCooldown
+ + "}";
+ }
}
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index ba45339..c791498 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -155,6 +155,7 @@
"debug.cdm.cdmservice.removal_time_window";
private static final long ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT = DAYS.toMillis(90);
+ private static final int MAX_CN_LENGTH = 500;
private final ActivityManager mActivityManager;
private final OnPackageVisibilityChangeListener mOnPackageVisibilityChangeListener;
@@ -757,6 +758,9 @@
String callingPackage = component.getPackageName();
checkCanCallNotificationApi(callingPackage);
// TODO: check userId.
+ if (component.flattenToString().length() > MAX_CN_LENGTH) {
+ throw new IllegalArgumentException("Component name is too long.");
+ }
final long identity = Binder.clearCallingIdentity();
try {
return PendingIntent.getActivityAsUser(getContext(),
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index e8839a2..720687e 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -562,7 +562,8 @@
private byte[] constructToken(D2DHandshakeContext.Role role, byte[] authValue)
throws GeneralSecurityException {
MessageDigest hash = MessageDigest.getInstance("SHA-256");
- byte[] roleUtf8 = role.name().getBytes(StandardCharsets.UTF_8);
+ String roleName = role == Role.INITIATOR ? "Initiator" : "Responder";
+ byte[] roleUtf8 = roleName.getBytes(StandardCharsets.UTF_8);
int tokenLength = roleUtf8.length + authValue.length;
return hash.digest(ByteBuffer.allocate(tokenLength)
.put(roleUtf8)
diff --git a/services/core/Android.bp b/services/core/Android.bp
index d9c2694..6521fab 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -194,6 +194,7 @@
"notification_flags_lib",
"camera_platform_flags_core_java_lib",
"biometrics_flags_lib",
+ "am_flags_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index c20f0aa..9716cf6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -225,7 +225,7 @@
/**
* The default value to {@link #KEY_ENABLE_NEW_OOMADJ}.
*/
- private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false;
+ private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/Android.bp b/services/core/java/com/android/server/am/Android.bp
new file mode 100644
index 0000000..af1200e
--- /dev/null
+++ b/services/core/java/com/android/server/am/Android.bp
@@ -0,0 +1,10 @@
+aconfig_declarations {
+ name: "am_flags",
+ package: "com.android.server.am",
+ srcs: ["*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "am_flags_lib",
+ aconfig_declarations: "am_flags",
+}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 5fa0ffa..6005b64 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1442,7 +1442,6 @@
uidRec.setFrozen(false);
postUidFrozenMessage(uidRec.getUid(), false);
}
- reportProcessFreezableChangedLocked(app);
opt.setFreezerOverride(false);
if (pid == 0 || !opt.isFrozen()) {
@@ -1481,6 +1480,7 @@
if (processKilled) {
return;
}
+ reportProcessFreezableChangedLocked(app);
long freezeTime = opt.getFreezeUnfreezeTime();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3d11c68..0615ecf 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3087,6 +3087,8 @@
if (old == proc && proc.isPersistent()) {
// We are re-adding a persistent process. Whatevs! Just leave it there.
Slog.w(TAG, "Re-adding persistent process " + proc);
+ // Ensure that the mCrashing flag is cleared, since this is a restart
+ proc.resetCrashingOnRestart();
} else if (old != null) {
if (old.isKilled()) {
// The old process has been killed, we probably haven't had
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index cfbb5a5..d8a2695 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -618,6 +618,10 @@
mPkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
}
+ void resetCrashingOnRestart() {
+ mErrorState.setCrashing(false);
+ }
+
@GuardedBy(anyOf = {"mService", "mProcLock"})
UidRecord getUidRecord() {
return mUidRecord;
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
new file mode 100644
index 0000000..b03cc62
--- /dev/null
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.server.am"
+
+flag {
+ name: "oomadjuster_correctness_rewrite"
+ namespace: "android_platform_power_optimization"
+ description: "Utilize new OomAdjuster implementation"
+ bug: "298055811"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 507ae26..9e92c8d 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -500,6 +500,8 @@
public static final String DEFAULT_ID = "default";
+ public static final int DEFAULT_LOW_REFRESH_RATE = 60;
+
private static final float BRIGHTNESS_DEFAULT = 0.5f;
private static final String ETC_DIR = "etc";
private static final String DISPLAY_CONFIG_DIR = "displayconfig";
@@ -513,7 +515,6 @@
private static final int DEFAULT_PEAK_REFRESH_RATE = 0;
private static final int DEFAULT_REFRESH_RATE = 60;
private static final int DEFAULT_REFRESH_RATE_IN_HBM = 0;
- private static final int DEFAULT_LOW_REFRESH_RATE = 60;
private static final int DEFAULT_HIGH_REFRESH_RATE = 0;
private static final float[] DEFAULT_BRIGHTNESS_THRESHOLDS = new float[]{};
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 213ee64..3529b04 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static android.view.Display.Mode.INVALID_MODE_ID;
+
import android.hardware.display.DeviceProductInfo;
import android.hardware.display.DisplayViewport;
import android.util.DisplayMetrics;
@@ -275,6 +277,11 @@
public int defaultModeId;
/**
+ * The mode of the display which is preferred by user.
+ */
+ public int userPreferredModeId = INVALID_MODE_ID;
+
+ /**
* The supported modes of the display.
*/
public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY;
@@ -472,6 +479,7 @@
|| modeId != other.modeId
|| renderFrameRate != other.renderFrameRate
|| defaultModeId != other.defaultModeId
+ || userPreferredModeId != other.userPreferredModeId
|| !Arrays.equals(supportedModes, other.supportedModes)
|| !Arrays.equals(supportedColorModes, other.supportedColorModes)
|| !Objects.equals(hdrCapabilities, other.hdrCapabilities)
@@ -517,6 +525,7 @@
modeId = other.modeId;
renderFrameRate = other.renderFrameRate;
defaultModeId = other.defaultModeId;
+ userPreferredModeId = other.userPreferredModeId;
supportedModes = other.supportedModes;
colorMode = other.colorMode;
supportedColorModes = other.supportedColorModes;
@@ -559,6 +568,7 @@
sb.append(", modeId ").append(modeId);
sb.append(", renderFrameRate ").append(renderFrameRate);
sb.append(", defaultModeId ").append(defaultModeId);
+ sb.append(", userPreferredModeId ").append(userPreferredModeId);
sb.append(", supportedModes ").append(Arrays.toString(supportedModes));
sb.append(", colorMode ").append(colorMode);
sb.append(", supportedColorModes ").append(Arrays.toString(supportedColorModes));
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 46ef6c3..9ef84cb 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -557,7 +557,7 @@
mLogicalDisplayMapper = new LogicalDisplayMapper(mContext,
new FoldSettingProvider(mContext, new SettingsWrapper()), mDisplayDeviceRepo,
new LogicalDisplayListener(), mSyncRoot, mHandler, mFlags);
- mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
+ mDisplayModeDirector = new DisplayModeDirector(context, mHandler, mFlags);
mBrightnessSynchronizer = new BrightnessSynchronizer(mContext);
Resources resources = mContext.getResources();
mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
@@ -2028,9 +2028,6 @@
mDisplayBrightnesses.delete(displayId);
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
- sendDisplayEventLocked(display, event);
- scheduleTraversalLocked(false);
-
if (mDisplayWindowPolicyControllers.contains(displayId)) {
final IVirtualDevice virtualDevice =
mDisplayWindowPolicyControllers.removeReturnOld(displayId).first;
@@ -2041,6 +2038,9 @@
});
}
}
+
+ sendDisplayEventLocked(display, event);
+ scheduleTraversalLocked(false);
}
private void handleLogicalDisplaySwappedLocked(@NonNull LogicalDisplay display) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 924b1b3..0f3e7c5 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -640,6 +640,7 @@
mInfo.modeId = mActiveModeId;
mInfo.renderFrameRate = mActiveRenderFrameRate;
mInfo.defaultModeId = getPreferredModeId();
+ mInfo.userPreferredModeId = mUserPreferredModeId;
mInfo.supportedModes = getDisplayModes(mSupportedModes);
mInfo.colorMode = mActiveColorMode;
mInfo.allmSupported = mAllmSupported;
@@ -1344,6 +1345,7 @@
public interface DisplayEventListener {
void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected);
+ void onHotplugConnectionError(long timestampNanos, int connectionError);
void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
long renderPeriod);
void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
@@ -1366,6 +1368,11 @@
}
@Override
+ public void onHotplugConnectionError(long timestampNanos, int errorCode) {
+ mListener.onHotplugConnectionError(timestampNanos, errorCode);
+ }
+
+ @Override
public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
long renderPeriod) {
mListener.onModeChanged(timestampNanos, physicalDisplayId, modeId, renderPeriod);
@@ -1391,6 +1398,15 @@
}
@Override
+ public void onHotplugConnectionError(long timestampNanos, int connectionError) {
+ if (DEBUG) {
+ Slog.d(TAG, "onHotplugConnectionError("
+ + "timestampNanos=" + timestampNanos
+ + ", connectionError=" + connectionError + ")");
+ }
+ }
+
+ @Override
public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
long renderPeriod) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 0405ebe..d4d104e 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -470,6 +470,7 @@
mBaseDisplayInfo.modeId = deviceInfo.modeId;
mBaseDisplayInfo.renderFrameRate = deviceInfo.renderFrameRate;
mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
+ mBaseDisplayInfo.userPreferredModeId = deviceInfo.userPreferredModeId;
mBaseDisplayInfo.supportedModes = Arrays.copyOf(
deviceInfo.supportedModes, deviceInfo.supportedModes.length);
mBaseDisplayInfo.colorMode = deviceInfo.colorMode;
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 3f6bf1a..ff768d6 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -47,6 +47,22 @@
Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1,
Flags::enableAdaptiveToneImprovements1);
+ private final FlagState mDisplayResolutionRangeVotingState = new FlagState(
+ Flags.FLAG_ENABLE_DISPLAY_RESOLUTION_RANGE_VOTING,
+ Flags::enableDisplayResolutionRangeVoting);
+
+ private final FlagState mUserPreferredModeVoteState = new FlagState(
+ Flags.FLAG_ENABLE_USER_PREFERRED_MODE_VOTE,
+ Flags::enableUserPreferredModeVote);
+
+ private final FlagState mExternalDisplayLimitModeState = new FlagState(
+ Flags.FLAG_ENABLE_MODE_LIMIT_FOR_EXTERNAL_DISPLAY,
+ Flags::enableModeLimitForExternalDisplay);
+
+ private final FlagState mDisplaysRefreshRatesSynchronizationState = new FlagState(
+ Flags.FLAG_ENABLE_DISPLAYS_REFRESH_RATES_SYNCHRONIZATION,
+ Flags::enableDisplaysRefreshRatesSynchronization);
+
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
return mConnectedDisplayManagementFlagState.isEnabled();
@@ -68,6 +84,33 @@
return mAdaptiveToneImprovements1.isEnabled();
}
+ /** Returns whether resolution range voting feature is enabled or not. */
+ public boolean isDisplayResolutionRangeVotingEnabled() {
+ return mDisplayResolutionRangeVotingState.isEnabled();
+ }
+
+ /**
+ * @return Whether user preferred mode is added as a vote in
+ * {@link com.android.server.display.mode.DisplayModeDirector}
+ */
+ public boolean isUserPreferredModeVoteEnabled() {
+ return mUserPreferredModeVoteState.isEnabled();
+ }
+
+ /**
+ * @return Whether external display mode limitation is enabled.
+ */
+ public boolean isExternalDisplayLimitModeEnabled() {
+ return mExternalDisplayLimitModeState.isEnabled();
+ }
+
+ /**
+ * @return Whether displays refresh rate synchronization is enabled.
+ */
+ public boolean isDisplaysRefreshRatesSynchronizationEnabled() {
+ return mDisplaysRefreshRatesSynchronizationState.isEnabled();
+ }
+
private static class FlagState {
private final String mName;
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 4d86004..a5b8cbb 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -5,7 +5,7 @@
flag {
name: "enable_connected_display_management"
namespace: "display_manager"
- description: "Feature flag for Connected Display managment"
+ description: "Feature flag for Connected Display management"
bug: "280739508"
is_fixed_read_only: true
}
@@ -34,3 +34,34 @@
is_fixed_read_only: true
}
+flag {
+ name: "enable_display_resolution_range_voting"
+ namespace: "display_manager"
+ description: "Feature flag to enable voting for ranges of resolutions"
+ bug: "299297058"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "enable_user_preferred_mode_vote"
+ namespace: "display_manager"
+ description: "Feature flag to use voting for UserPreferredMode for display"
+ bug: "297018612"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "enable_mode_limit_for_external_display"
+ namespace: "display_manager"
+ description: "Feature limiting external display resolution and refresh rate"
+ bug: "242093547"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "enable_displays_refresh_rates_synchronization"
+ namespace: "display_manager"
+ description: "Enables synchronization of refresh rates across displays"
+ bug: "294015845"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 2c2af3f..71ea8cc 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -19,6 +19,9 @@
import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT;
+import static android.view.Display.Mode.INVALID_MODE_ID;
+
+import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;
import android.annotation.IntegerRes;
import android.annotation.NonNull;
@@ -71,6 +74,7 @@
import com.android.server.LocalServices;
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.display.utils.DeviceConfigParsingUtils;
@@ -84,9 +88,11 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Function;
import java.util.function.IntSupplier;
@@ -96,6 +102,8 @@
* picked by the system based on system-wide and display-specific configuration.
*/
public class DisplayModeDirector {
+ public static final float SYNCHRONIZED_REFRESH_RATE_TARGET = DEFAULT_LOW_REFRESH_RATE;
+ public static final float SYNCHRONIZED_REFRESH_RATE_TOLERANCE = 1;
private static final String TAG = "DisplayModeDirector";
private boolean mLoggingEnabled;
@@ -151,12 +159,38 @@
@DisplayManager.SwitchingType
private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
- public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
- this(context, handler, new RealInjector(context));
+ /**
+ * Whether resolution range voting feature is enabled.
+ */
+ private final boolean mIsDisplayResolutionRangeVotingEnabled;
+
+ /**
+ * Whether user preferred mode voting feature is enabled.
+ */
+ private final boolean mIsUserPreferredModeVoteEnabled;
+
+ /**
+ * Whether limit display mode feature is enabled.
+ */
+ private final boolean mIsExternalDisplayLimitModeEnabled;
+
+ private final boolean mIsDisplaysRefreshRatesSynchronizationEnabled;
+
+ public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
+ @NonNull DisplayManagerFlags displayManagerFlags) {
+ this(context, handler, new RealInjector(context), displayManagerFlags);
}
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
- @NonNull Injector injector) {
+ @NonNull Injector injector,
+ @NonNull DisplayManagerFlags displayManagerFlags) {
+ mIsDisplayResolutionRangeVotingEnabled = displayManagerFlags
+ .isDisplayResolutionRangeVotingEnabled();
+ mIsUserPreferredModeVoteEnabled = displayManagerFlags.isUserPreferredModeVoteEnabled();
+ mIsExternalDisplayLimitModeEnabled = displayManagerFlags
+ .isExternalDisplayLimitModeEnabled();
+ mIsDisplaysRefreshRatesSynchronizationEnabled = displayManagerFlags
+ .isDisplaysRefreshRatesSynchronizationEnabled();
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
mInjector = injector;
@@ -230,6 +264,8 @@
public float maxRenderFrameRate;
public int width;
public int height;
+ public int minWidth;
+ public int minHeight;
public boolean disableRefreshRateSwitching;
public float appRequestBaseModeRefreshRate;
@@ -244,6 +280,8 @@
maxRenderFrameRate = Float.POSITIVE_INFINITY;
width = Vote.INVALID_SIZE;
height = Vote.INVALID_SIZE;
+ minWidth = 0;
+ minHeight = 0;
disableRefreshRateSwitching = false;
appRequestBaseModeRefreshRate = 0f;
}
@@ -256,6 +294,8 @@
+ ", maxRenderFrameRate=" + maxRenderFrameRate
+ ", width=" + width
+ ", height=" + height
+ + ", minWidth=" + minWidth
+ + ", minHeight=" + minHeight
+ ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
+ ", appRequestBaseModeRefreshRate=" + appRequestBaseModeRefreshRate;
}
@@ -277,7 +317,6 @@
continue;
}
-
// For physical refresh rates, just use the tightest bounds of all the votes.
// The refresh rate cannot be lower than the minimal render frame rate.
final float minPhysicalRefreshRate = Math.max(vote.refreshRateRanges.physical.min,
@@ -298,10 +337,18 @@
// For display size, disable refresh rate switching and base mode refresh rate use only
// the first vote we come across (i.e. the highest priority vote that includes the
// attribute).
- if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE
- && vote.height > 0 && vote.width > 0) {
- summary.width = vote.width;
- summary.height = vote.height;
+ if (vote.height > 0 && vote.width > 0) {
+ if (summary.width == Vote.INVALID_SIZE && summary.height == Vote.INVALID_SIZE) {
+ summary.width = vote.width;
+ summary.height = vote.height;
+ summary.minWidth = vote.minWidth;
+ summary.minHeight = vote.minHeight;
+ } else if (mIsDisplayResolutionRangeVotingEnabled) {
+ summary.width = Math.min(summary.width, vote.width);
+ summary.height = Math.min(summary.height, vote.height);
+ summary.minWidth = Math.max(summary.minWidth, vote.minWidth);
+ summary.minHeight = Math.max(summary.minHeight, vote.minHeight);
+ }
}
if (!summary.disableRefreshRateSwitching && vote.disableRefreshRateSwitching) {
summary.disableRefreshRateSwitching = true;
@@ -413,6 +460,8 @@
|| primarySummary.width == Vote.INVALID_SIZE) {
primarySummary.width = defaultMode.getPhysicalWidth();
primarySummary.height = defaultMode.getPhysicalHeight();
+ } else if (mIsDisplayResolutionRangeVotingEnabled) {
+ updateSummaryWithBestAllowedResolution(modes, primarySummary);
}
availableModes = filterModes(modes, primarySummary);
@@ -654,6 +703,38 @@
return availableModes;
}
+ private void updateSummaryWithBestAllowedResolution(final Display.Mode[] supportedModes,
+ VoteSummary outSummary) {
+ final int maxAllowedWidth = outSummary.width;
+ final int maxAllowedHeight = outSummary.height;
+ if (mLoggingEnabled) {
+ Slog.i(TAG, "updateSummaryWithBestAllowedResolution " + outSummary);
+ }
+ outSummary.width = Vote.INVALID_SIZE;
+ outSummary.height = Vote.INVALID_SIZE;
+
+ int maxNumberOfPixels = 0;
+ for (Display.Mode mode : supportedModes) {
+ if (mode.getPhysicalWidth() > maxAllowedWidth
+ || mode.getPhysicalHeight() > maxAllowedHeight
+ || mode.getPhysicalWidth() < outSummary.minWidth
+ || mode.getPhysicalHeight() < outSummary.minHeight) {
+ continue;
+ }
+
+ int numberOfPixels = mode.getPhysicalHeight() * mode.getPhysicalWidth();
+ if (numberOfPixels > maxNumberOfPixels || (mode.getPhysicalWidth() == maxAllowedWidth
+ && mode.getPhysicalHeight() == maxAllowedHeight)) {
+ if (mLoggingEnabled) {
+ Slog.i(TAG, "updateSummaryWithBestAllowedResolution updated with " + mode);
+ }
+ maxNumberOfPixels = numberOfPixels;
+ outSummary.width = mode.getPhysicalWidth();
+ outSummary.height = mode.getPhysicalHeight();
+ }
+ }
+ }
+
/**
* Gets the observer responsible for application display mode requests.
*/
@@ -1393,11 +1474,38 @@
private final Context mContext;
private final Handler mHandler;
private final VotesStorage mVotesStorage;
+ private int mExternalDisplayPeakWidth;
+ private int mExternalDisplayPeakHeight;
+ private int mExternalDisplayPeakRefreshRate;
+ private final boolean mRefreshRateSynchronizationEnabled;
+ private final Set<Integer> mExternalDisplaysConnected = new HashSet<>();
DisplayObserver(Context context, Handler handler, VotesStorage votesStorage) {
mContext = context;
mHandler = handler;
mVotesStorage = votesStorage;
+ mExternalDisplayPeakRefreshRate = mContext.getResources().getInteger(
+ R.integer.config_externalDisplayPeakRefreshRate);
+ mExternalDisplayPeakWidth = mContext.getResources().getInteger(
+ R.integer.config_externalDisplayPeakWidth);
+ mExternalDisplayPeakHeight = mContext.getResources().getInteger(
+ R.integer.config_externalDisplayPeakHeight);
+ mRefreshRateSynchronizationEnabled = mContext.getResources().getBoolean(
+ R.bool.config_refreshRateSynchronizationEnabled);
+ }
+
+ private boolean isExternalDisplayLimitModeEnabled() {
+ return mExternalDisplayPeakWidth > 0
+ && mExternalDisplayPeakHeight > 0
+ && mExternalDisplayPeakRefreshRate > 0
+ && mIsExternalDisplayLimitModeEnabled
+ && mIsDisplayResolutionRangeVotingEnabled
+ && mIsUserPreferredModeVoteEnabled;
+ }
+
+ private boolean isRefreshRateSynchronizationEnabled() {
+ return mRefreshRateSynchronizationEnabled
+ && mIsDisplaysRefreshRatesSynchronizationEnabled;
}
public void observe() {
@@ -1428,6 +1536,9 @@
DisplayInfo displayInfo = getDisplayInfo(displayId);
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
+ updateUserSettingDisplayPreferredSize(displayInfo);
+ updateDisplaysPeakRefreshRateAndResolution(displayInfo);
+ addDisplaysSynchronizedPeakRefreshRate(displayInfo);
}
@Override
@@ -1437,6 +1548,9 @@
mDefaultModeByDisplay.remove(displayId);
}
updateLayoutLimitedFrameRate(displayId, null);
+ removeUserSettingDisplayPreferredSize(displayId);
+ removeDisplaysPeakRefreshRateAndResolution(displayId);
+ removeDisplaysSynchronizedPeakRefreshRate(displayId);
}
@Override
@@ -1444,6 +1558,7 @@
DisplayInfo displayInfo = getDisplayInfo(displayId);
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
+ updateUserSettingDisplayPreferredSize(displayInfo);
}
@Nullable
@@ -1460,6 +1575,111 @@
mVotesStorage.updateVote(displayId, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE, vote);
}
+ private void removeUserSettingDisplayPreferredSize(int displayId) {
+ if (!mIsUserPreferredModeVoteEnabled) {
+ return;
+ }
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE,
+ null);
+ }
+
+ private void updateUserSettingDisplayPreferredSize(@Nullable DisplayInfo info) {
+ if (info == null || !mIsUserPreferredModeVoteEnabled) {
+ return;
+ }
+
+ var preferredMode = findDisplayPreferredMode(info);
+ if (preferredMode == null) {
+ removeUserSettingDisplayPreferredSize(info.displayId);
+ return;
+ }
+
+ mVotesStorage.updateVote(info.displayId,
+ Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE,
+ Vote.forSize(/* width */ preferredMode.getPhysicalWidth(),
+ /* height */ preferredMode.getPhysicalHeight()));
+ }
+
+ @Nullable
+ private Display.Mode findDisplayPreferredMode(@NonNull DisplayInfo info) {
+ if (info.userPreferredModeId == INVALID_MODE_ID) {
+ return null;
+ }
+ for (var mode : info.supportedModes) {
+ if (mode.getModeId() == info.userPreferredModeId) {
+ return mode;
+ }
+ }
+ return null;
+ }
+
+ private void removeDisplaysPeakRefreshRateAndResolution(int displayId) {
+ if (!isExternalDisplayLimitModeEnabled()) {
+ return;
+ }
+
+ mVotesStorage.updateVote(displayId,
+ Vote.PRIORITY_LIMIT_MODE, null);
+ }
+
+ private void updateDisplaysPeakRefreshRateAndResolution(@Nullable final DisplayInfo info) {
+ // Only consider external display, only in case the refresh rate and resolution limits
+ // are non-zero.
+ if (info == null || info.type != Display.TYPE_EXTERNAL
+ || !isExternalDisplayLimitModeEnabled()) {
+ return;
+ }
+
+ mVotesStorage.updateVote(info.displayId,
+ Vote.PRIORITY_LIMIT_MODE,
+ Vote.forSizeAndPhysicalRefreshRatesRange(
+ /* minWidth */ 0, /* minHeight */ 0,
+ mExternalDisplayPeakWidth,
+ mExternalDisplayPeakHeight,
+ /* minPhysicalRefreshRate */ 0,
+ mExternalDisplayPeakRefreshRate));
+ }
+
+ /**
+ * Sets 60Hz target refresh rate as the vote with
+ * {@link Vote#PRIORITY_SYNCHRONIZED_REFRESH_RATE} priority.
+ */
+ private void addDisplaysSynchronizedPeakRefreshRate(@Nullable final DisplayInfo info) {
+ if (info == null || info.type != Display.TYPE_EXTERNAL
+ || !isRefreshRateSynchronizationEnabled()) {
+ return;
+ }
+ synchronized (mLock) {
+ mExternalDisplaysConnected.add(info.displayId);
+ if (mExternalDisplaysConnected.size() != 1) {
+ return;
+ }
+ }
+ // set minRefreshRate as the max refresh rate.
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE,
+ Vote.forPhysicalRefreshRates(
+ SYNCHRONIZED_REFRESH_RATE_TARGET
+ - SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
+ SYNCHRONIZED_REFRESH_RATE_TARGET
+ + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
+ }
+
+ private void removeDisplaysSynchronizedPeakRefreshRate(final int displayId) {
+ if (!isRefreshRateSynchronizationEnabled()) {
+ return;
+ }
+ synchronized (mLock) {
+ if (!mExternalDisplaysConnected.contains(displayId)) {
+ return;
+ }
+ mExternalDisplaysConnected.remove(displayId);
+ if (mExternalDisplaysConnected.size() != 0) {
+ return;
+ }
+ }
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE, null);
+ }
+
private void updateDisplayModes(int displayId, @Nullable DisplayInfo info) {
if (info == null) {
return;
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index a42d8f2..b6a6069 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -18,6 +18,8 @@
import android.view.SurfaceControl;
+import java.util.Objects;
+
final class Vote {
// DEFAULT_RENDER_FRAME_RATE votes for render frame rate [0, DEFAULT]. As the lowest
// priority vote, it's overridden by all other considerations. It acts to set a default
@@ -36,12 +38,15 @@
// It votes [minRefreshRate, Float.POSITIVE_INFINITY]
static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
+ // User setting preferred display resolution.
+ static final int PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE = 4;
+
// APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
// frame rate in certain cases, mostly to preserve power.
// @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
// @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
// It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
- static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 4;
+ static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 5;
// We split the app request into different priorities in case we can satisfy one desire
// without the other.
@@ -67,40 +72,47 @@
// The preferred refresh rate is set on the main surface of the app outside of
// DisplayModeDirector.
// @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
- static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5;
- static final int PRIORITY_APP_REQUEST_SIZE = 6;
+ static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 6;
+
+ static final int PRIORITY_APP_REQUEST_SIZE = 7;
// SETTING_PEAK_RENDER_FRAME_RATE has a high priority and will restrict the bounds of the
// rest of low priority voters. It votes [0, max(PEAK, MIN)]
- static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 7;
+ static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 8;
+
+ // Restrict all displays to 60Hz when external display is connected. It votes [59Hz, 61Hz].
+ static final int PRIORITY_SYNCHRONIZED_REFRESH_RATE = 9;
+
+ // Restrict displays max available resolution and refresh rates. It votes [0, LIMIT]
+ static final int PRIORITY_LIMIT_MODE = 10;
// To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
// rate to max value (same as for PRIORITY_UDFPS) on lock screen
- static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 8;
+ static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 11;
// For concurrent displays we want to limit refresh rate on all displays
- static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 9;
+ static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 12;
// LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
// Settings.Global.LOW_POWER_MODE is on.
- static final int PRIORITY_LOW_POWER_MODE = 10;
+ static final int PRIORITY_LOW_POWER_MODE = 13;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
// It's used to avoid refresh rate switches in certain conditions which may result in the
// user seeing the display flickering when the switches occur.
- static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 11;
+ static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 14;
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
- static final int PRIORITY_SKIN_TEMPERATURE = 12;
+ static final int PRIORITY_SKIN_TEMPERATURE = 15;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
- static final int PRIORITY_PROXIMITY = 13;
+ static final int PRIORITY_PROXIMITY = 16;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
- static final int PRIORITY_UDFPS = 14;
+ static final int PRIORITY_UDFPS = 17;
// Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
// APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
@@ -127,6 +139,14 @@
*/
public final int height;
/**
+ * Min requested width of the display in pixels, or 0;
+ */
+ public final int minWidth;
+ /**
+ * Min requested height of the display in pixels, or 0;
+ */
+ public final int minHeight;
+ /**
* Information about the refresh rate frame rate ranges DM would like to set the display to.
*/
public final SurfaceControl.RefreshRateRanges refreshRateRanges;
@@ -144,42 +164,82 @@
public final float appRequestBaseModeRefreshRate;
static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
- return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate, 0,
- Float.POSITIVE_INFINITY,
- minRefreshRate == maxRefreshRate, 0f);
+ return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
+ /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
+ /* minPhysicalRefreshRate= */ minRefreshRate,
+ /* maxPhysicalRefreshRate= */ maxRefreshRate,
+ /* minRenderFrameRate= */ 0,
+ /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
+ /* disableRefreshRateSwitching= */ minRefreshRate == maxRefreshRate,
+ /* baseModeRefreshRate= */ 0f);
}
static Vote forRenderFrameRates(float minFrameRate, float maxFrameRate) {
- return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, minFrameRate,
+ return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
+ /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
+ /* minPhysicalRefreshRate= */ 0,
+ /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
+ minFrameRate,
maxFrameRate,
- false, 0f);
+ /* disableRefreshRateSwitching= */ false,
+ /* baseModeRefreshRate= */ 0f);
}
static Vote forSize(int width, int height) {
- return new Vote(width, height, 0, Float.POSITIVE_INFINITY, 0, Float.POSITIVE_INFINITY,
- false,
- 0f);
+ return new Vote(/* minWidth= */ width, /* minHeight= */ height,
+ width, height,
+ /* minPhysicalRefreshRate= */ 0,
+ /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
+ /* minRenderFrameRate= */ 0,
+ /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
+ /* disableRefreshRateSwitching= */ false,
+ /* baseModeRefreshRate= */ 0f);
+ }
+
+ static Vote forSizeAndPhysicalRefreshRatesRange(int minWidth, int minHeight,
+ int width, int height, float minRefreshRate, float maxRefreshRate) {
+ return new Vote(minWidth, minHeight,
+ width, height,
+ minRefreshRate,
+ maxRefreshRate,
+ /* minRenderFrameRate= */ 0,
+ /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
+ /* disableRefreshRateSwitching= */ minRefreshRate == maxRefreshRate,
+ /* baseModeRefreshRate= */ 0f);
}
static Vote forDisableRefreshRateSwitching() {
- return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
- Float.POSITIVE_INFINITY, true,
- 0f);
+ return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
+ /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
+ /* minPhysicalRefreshRate= */ 0,
+ /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
+ /* minRenderFrameRate= */ 0,
+ /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
+ /* disableRefreshRateSwitching= */ true,
+ /* baseModeRefreshRate= */ 0f);
}
static Vote forBaseModeRefreshRate(float baseModeRefreshRate) {
- return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
- Float.POSITIVE_INFINITY, false,
- baseModeRefreshRate);
+ return new Vote(/* minWidth= */ 0, /* minHeight= */ 0,
+ /* width= */ INVALID_SIZE, /* height= */ INVALID_SIZE,
+ /* minPhysicalRefreshRate= */ 0,
+ /* maxPhysicalRefreshRate= */ Float.POSITIVE_INFINITY,
+ /* minRenderFrameRate= */ 0,
+ /* maxRenderFrameRate= */ Float.POSITIVE_INFINITY,
+ /* disableRefreshRateSwitching= */ false,
+ /* baseModeRefreshRate= */ baseModeRefreshRate);
}
- private Vote(int width, int height,
+ private Vote(int minWidth, int minHeight,
+ int width, int height,
float minPhysicalRefreshRate,
float maxPhysicalRefreshRate,
float minRenderFrameRate,
float maxRenderFrameRate,
boolean disableRefreshRateSwitching,
float baseModeRefreshRate) {
+ this.minWidth = minWidth;
+ this.minHeight = minHeight;
this.width = width;
this.height = height;
this.refreshRateRanges = new SurfaceControl.RefreshRateRanges(
@@ -215,6 +275,12 @@
return "PRIORITY_UDFPS";
case PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE:
return "PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE";
+ case PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE:
+ return "PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE";
+ case PRIORITY_LIMIT_MODE:
+ return "PRIORITY_LIMIT_MODE";
+ case PRIORITY_SYNCHRONIZED_REFRESH_RATE:
+ return "PRIORITY_SYNCHRONIZED_REFRESH_RATE";
case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
@@ -229,9 +295,29 @@
@Override
public String toString() {
return "Vote: {"
- + "width: " + width + ", height: " + height
+ + "minWidth: " + minWidth + ", minHeight: " + minHeight
+ + ", width: " + width + ", height: " + height
+ ", refreshRateRanges: " + refreshRateRanges
+ ", disableRefreshRateSwitching: " + disableRefreshRateSwitching
+ ", appRequestBaseModeRefreshRate: " + appRequestBaseModeRefreshRate + "}";
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(minWidth, minHeight, width, height, refreshRateRanges,
+ disableRefreshRateSwitching, appRequestBaseModeRefreshRate);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Vote)) return false;
+ final var vote = (Vote) o;
+ return minWidth == vote.minWidth && minHeight == vote.minHeight
+ && width == vote.width && height == vote.height
+ && disableRefreshRateSwitching == vote.disableRefreshRateSwitching
+ && Float.compare(vote.appRequestBaseModeRefreshRate,
+ appRequestBaseModeRefreshRate) == 0
+ && refreshRateRanges.equals(vote.refreshRateRanges);
+ }
}
diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java
index bdd2ab7..49c587a 100644
--- a/services/core/java/com/android/server/display/mode/VotesStorage.java
+++ b/services/core/java/com/android/server/display/mode/VotesStorage.java
@@ -31,7 +31,8 @@
private static final String TAG = "VotesStorage";
// Special ID used to indicate that given vote is to be applied globally, rather than to a
// specific display.
- private static final int GLOBAL_ID = -1;
+ @VisibleForTesting
+ static final int GLOBAL_ID = -1;
private boolean mLoggingEnabled;
@@ -91,6 +92,7 @@
+ ", vote=" + vote);
return;
}
+ boolean changed = false;
SparseArray<Vote> votes;
synchronized (mStorageLock) {
if (mVotesByDisplay.contains(displayId)) {
@@ -99,10 +101,13 @@
votes = new SparseArray<>();
mVotesByDisplay.put(displayId, votes);
}
- if (vote != null) {
+ var currentVote = votes.get(priority);
+ if (vote != null && !vote.equals(currentVote)) {
votes.put(priority, vote);
- } else {
+ changed = true;
+ } else if (vote == null && currentVote != null) {
votes.remove(priority);
+ changed = true;
}
}
Trace.traceCounter(Trace.TRACE_TAG_POWER,
@@ -111,7 +116,9 @@
if (mLoggingEnabled) {
Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes);
}
- mListener.onChanged();
+ if (changed) {
+ mListener.onChanged();
+ }
}
/** dump class values, for debugging */
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index c6f6fe2..fe91050 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1021,7 +1021,7 @@
synchronized (mSnoozing) {
mSnoozing.remove(user);
}
- rebindServices(true, user);
+ unbindUserServices(user);
}
public void onUserSwitched(int user) {
@@ -1408,12 +1408,24 @@
void unbindOtherUserServices(int currentUser) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("ManagedServices.unbindOtherUserServices_current" + currentUser);
- final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+ unbindServicesImpl(currentUser, true /* allExceptUser */);
+ t.traceEnd();
+ }
+ void unbindUserServices(int user) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("ManagedServices.unbindUserServices" + user);
+ unbindServicesImpl(user, false /* allExceptUser */);
+ t.traceEnd();
+ }
+
+ void unbindServicesImpl(int user, boolean allExceptUser) {
+ final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
synchronized (mMutex) {
final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
for (ManagedServiceInfo info : removableBoundServices) {
- if (info.userid != currentUser) {
+ if ((allExceptUser && (info.userid != user))
+ || (!allExceptUser && (info.userid == user))) {
Set<ComponentName> toUnbind =
componentsToUnbind.get(info.userid, new ArraySet<>());
toUnbind.add(info.component);
@@ -1422,7 +1434,6 @@
}
}
unbindFromServices(componentsToUnbind);
- t.traceEnd();
}
protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 802dfb1..a3c71c2 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -177,6 +177,7 @@
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.compat.annotation.ChangeId;
@@ -263,6 +264,7 @@
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -4159,7 +4161,7 @@
String pkg) {
checkCallerIsSystemOrSameApp(pkg);
return mPreferencesHelper.getNotificationChannelGroups(
- pkg, Binder.getCallingUid(), false, false, true);
+ pkg, Binder.getCallingUid(), false, false, true, true, null);
}
@Override
@@ -4280,7 +4282,36 @@
String pkg, int uid, boolean includeDeleted) {
enforceSystemOrSystemUI("getNotificationChannelGroupsForPackage");
return mPreferencesHelper.getNotificationChannelGroups(
- pkg, uid, includeDeleted, true, false);
+ pkg, uid, includeDeleted, true, false, true, null);
+ }
+
+ @Override
+ public ParceledListSlice<NotificationChannelGroup>
+ getRecentBlockedNotificationChannelGroupsForPackage(String pkg, int uid) {
+ enforceSystemOrSystemUI("getRecentBlockedNotificationChannelGroupsForPackage");
+ Set<String> recentlySentChannels = new HashSet<>();
+ long now = System.currentTimeMillis();
+ long startTime = now - (DateUtils.DAY_IN_MILLIS * 14);
+ UsageEvents events = mUsageStatsManagerInternal.queryEventsForUser(
+ UserHandle.getUserId(uid), startTime, now, UsageEvents.SHOW_ALL_EVENT_DATA);
+ // get all channelids that sent notifs in the past 2 weeks
+ if (events != null) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ while (events.hasNextEvent()) {
+ events.getNextEvent(event);
+ if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+ if (pkg.equals(event.mPackage)) {
+ String channelId = event.mNotificationChannelId;
+ if (channelId != null) {
+ recentlySentChannels.add(channelId);
+ }
+ }
+ }
+ }
+ }
+
+ return mPreferencesHelper.getNotificationChannelGroups(
+ pkg, uid, false, true, false, true, recentlySentChannels);
}
@Override
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index b132a23..de698d9 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1459,9 +1459,9 @@
}
}
- @Override
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
- int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
+ int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty,
+ boolean includeBlocked, Set<String> activeChannelFilter) {
Objects.requireNonNull(pkg);
Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
synchronized (mPackagePreferences) {
@@ -1473,7 +1473,11 @@
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
- if (includeDeleted || !nc.isDeleted()) {
+ boolean includeChannel = (includeDeleted || !nc.isDeleted())
+ && (activeChannelFilter == null
+ || (includeBlocked && nc.getImportance() == IMPORTANCE_NONE)
+ || activeChannelFilter.contains(nc.getId()));
+ if (includeChannel) {
if (nc.getGroup() != null) {
if (r.groups.get(nc.getGroup()) != null) {
NotificationChannelGroup ncg = groups.get(nc.getGroup());
@@ -1481,7 +1485,6 @@
ncg = r.groups.get(nc.getGroup()).clone();
ncg.setChannels(new ArrayList<>());
groups.put(nc.getGroup(), ncg);
-
}
ncg.addChannel(nc);
}
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index fec3591..8df24c9 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -40,8 +40,6 @@
int uid);
void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
boolean fromTargetApp, int callingUid, boolean isSystemOrSystemUi);
- ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
- int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty);
boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromTargetApp, boolean hasDndAccess, int callingUid,
boolean isSystemOrSystemUi);
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 52eef47..b9464d9 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -18,9 +18,6 @@
import static android.app.AppGlobals.getPackageManager;
import static android.content.Intent.ACTION_OVERLAY_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_ADDED;
-import static android.content.Intent.ACTION_PACKAGE_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
@@ -31,10 +28,10 @@
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
import static android.content.om.OverlayManagerTransaction.Request.TYPE_UNREGISTER_FABRICATED;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
+import static android.os.Process.INVALID_UID;
import static android.os.Trace.TRACE_TAG_RRO;
import static android.os.Trace.traceBegin;
import static android.os.Trace.traceEnd;
-
import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
import android.annotation.NonNull;
@@ -82,6 +79,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.content.PackageMonitor;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -261,6 +259,8 @@
private final OverlayActorEnforcer mActorEnforcer;
+ private final PackageMonitor mPackageMonitor = new OverlayManagerPackageMonitor();
+
private int mPrevStartedUserId = -1;
public OverlayManagerService(@NonNull final Context context) {
@@ -277,16 +277,9 @@
OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
- HandlerThread packageReceiverThread = new HandlerThread(TAG);
- packageReceiverThread.start();
-
- final IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(ACTION_PACKAGE_ADDED);
- packageFilter.addAction(ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme("package");
- getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
- packageFilter, null, packageReceiverThread.getThreadHandler());
+ HandlerThread packageMonitorThread = new HandlerThread(TAG);
+ packageMonitorThread.start();
+ mPackageMonitor.register(context, packageMonitorThread.getLooper(), true);
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(ACTION_USER_ADDED);
@@ -372,166 +365,171 @@
return defaultPackages.toArray(new String[defaultPackages.size()]);
}
- private final class PackageReceiver extends BroadcastReceiver {
+ private final class OverlayManagerPackageMonitor extends PackageMonitor {
+
@Override
- public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
- final String action = intent.getAction();
- if (action == null) {
- Slog.e(TAG, "Cannot handle package broadcast with null action");
- return;
- }
- final Uri data = intent.getData();
- if (data == null) {
- Slog.e(TAG, "Cannot handle package broadcast with null data");
- return;
- }
- final String packageName = data.getSchemeSpecificPart();
-
- final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- final boolean systemUpdateUninstall =
- intent.getBooleanExtra(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, false);
-
- final int[] userIds;
- final int extraUid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
- if (extraUid == UserHandle.USER_NULL) {
- userIds = mUserManager.getUserIds();
- } else {
- userIds = new int[] { UserHandle.getUserId(extraUid) };
- }
-
- switch (action) {
- case ACTION_PACKAGE_ADDED:
- if (replacing) {
- onPackageReplaced(packageName, userIds);
- } else {
- onPackageAdded(packageName, userIds);
- }
- break;
- case ACTION_PACKAGE_CHANGED:
- // ignore the intent if it was sent by the package manager as a result of the
- // overlay manager having sent ACTION_OVERLAY_CHANGED
- if (!ACTION_OVERLAY_CHANGED.equals(intent.getStringExtra(EXTRA_REASON))) {
- onPackageChanged(packageName, userIds);
- }
- break;
- case ACTION_PACKAGE_REMOVED:
- if (replacing) {
- onPackageReplacing(packageName, systemUpdateUninstall, userIds);
- } else {
- onPackageRemoved(packageName, userIds);
- }
- break;
- default:
- // do nothing
- break;
- }
+ public void onPackageAppearedWithExtras(String packageName, Bundle extras) {
+ handlePackageAdd(packageName, extras);
}
- private void onPackageAdded(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
- for (final int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageAdded(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageAdded(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageAdded internal error", e);
- }
+ @Override
+ public void onPackageChangedWithExtras(String packageName, Bundle extras) {
+ handlePackageChange(packageName, extras);
+ }
+
+ @Override
+ public void onPackageDisappearedWithExtras(String packageName, Bundle extras) {
+ handlePackageRemove(packageName, extras);
+ }
+ }
+
+ private int[] getUserIds(int uid) {
+ final int[] userIds;
+ if (uid == INVALID_UID) {
+ userIds = mUserManager.getUserIds();
+ } else {
+ userIds = new int[] { UserHandle.getUserId(uid) };
+ }
+ return userIds;
+ }
+
+ private void handlePackageAdd(String packageName, Bundle extras) {
+ final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+ final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+ final int[] userIds = getUserIds(uid);
+ if (replacing) {
+ onPackageReplaced(packageName, userIds);
+ } else {
+ onPackageAdded(packageName, userIds);
+ }
+ }
+
+ private void handlePackageChange(String packageName, Bundle extras) {
+ final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+ final int[] userIds = getUserIds(uid);
+ if (!ACTION_OVERLAY_CHANGED.equals(extras.getString(EXTRA_REASON))) {
+ onPackageChanged(packageName, userIds);
+ }
+ }
+
+ private void handlePackageRemove(String packageName, Bundle extras) {
+ final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+ final boolean systemUpdateUninstall =
+ extras.getBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, false);
+ final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+ final int[] userIds = getUserIds(uid);
+
+ if (replacing) {
+ onPackageReplacing(packageName, systemUpdateUninstall, userIds);
+ } else {
+ onPackageRemoved(packageName, userIds);
+ }
+ }
+
+ private void onPackageAdded(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
+ for (final int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageAdded(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageAdded(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageAdded internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageChanged(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageChanged(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageChanged internal error", e);
- }
+ private void onPackageChanged(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageChanged(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageChanged internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageReplacing(@NonNull final String packageName,
- boolean systemUpdateUninstall, @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName,
- systemUpdateUninstall, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageReplacing internal error", e);
- }
+ private void onPackageReplacing(@NonNull final String packageName,
+ boolean systemUpdateUninstall, @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName,
+ systemUpdateUninstall, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplacing internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageReplaced(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- var packageState = mPackageManager.onPackageUpdated(packageName, userId);
- if (packageState != null && !mPackageManager.isInstantApp(packageName,
- userId)) {
- try {
- updateTargetPackagesLocked(
- mImpl.onPackageReplaced(packageName, userId));
- } catch (OperationFailedException e) {
- Slog.e(TAG, "onPackageReplaced internal error", e);
- }
+ private void onPackageReplaced(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+ if (packageState != null && !mPackageManager.isInstantApp(packageName,
+ userId)) {
+ try {
+ updateTargetPackagesLocked(
+ mImpl.onPackageReplaced(packageName, userId));
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplaced internal error", e);
}
}
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
+ }
- private void onPackageRemoved(@NonNull final String packageName,
- @NonNull final int[] userIds) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
- for (int userId : userIds) {
- synchronized (mLock) {
- mPackageManager.onPackageRemoved(packageName, userId);
- updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId));
- }
+ private void onPackageRemoved(@NonNull final String packageName,
+ @NonNull final int[] userIds) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
+ for (int userId : userIds) {
+ synchronized (mLock) {
+ mPackageManager.onPackageRemoved(packageName, userId);
+ updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId));
}
- } finally {
- traceEnd(TRACE_TAG_RRO);
}
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
}
}
@@ -684,7 +682,7 @@
synchronized (mLock) {
try {
mImpl.setEnabledExclusive(
- overlay, false /* withinCategory */, realUserId)
+ overlay, false /* withinCategory */, realUserId)
.ifPresent(
OverlayManagerService.this::updateTargetPackagesLocked);
return true;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index dc75a98..097656c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1983,7 +1983,6 @@
public void run() {
if (mPendingHomeKeyEvent != null) {
handleShortPressOnHome(mPendingHomeKeyEvent);
- mPendingHomeKeyEvent.recycle();
mPendingHomeKeyEvent = null;
}
}
@@ -2028,7 +2027,7 @@
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_PIP_MENU
|| mPictureInPictureVisible) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
- mPendingHomeKeyEvent = KeyEvent.obtain(event);
+ mPendingHomeKeyEvent = event;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
ViewConfiguration.getDoubleTapTimeout());
return true;
@@ -2036,11 +2035,7 @@
}
// Post to main thread to avoid blocking input pipeline.
- final KeyEvent shortPressEvent = KeyEvent.obtain(event);
- mHandler.post(() -> {
- handleShortPressOnHome(shortPressEvent);
- shortPressEvent.recycle();
- });
+ mHandler.post(() -> handleShortPressOnHome(event));
return true;
}
@@ -2067,14 +2062,9 @@
if (repeatCount == 0) {
mHomePressed = true;
if (mPendingHomeKeyEvent != null) {
- mPendingHomeKeyEvent.recycle();
mPendingHomeKeyEvent = null;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
- final KeyEvent doublePressEvent = KeyEvent.obtain(event);
- mHandler.post(() -> {
- handleDoubleTapOnHome(doublePressEvent);
- doublePressEvent.recycle();
- });
+ mHandler.post(() -> handleDoubleTapOnHome(event));
// TODO(multi-display): Remove display id check once we support recents on
// multi-display
} else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
@@ -2084,11 +2074,7 @@
} else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
if (!keyguardOn) {
// Post to main thread to avoid blocking input pipeline.
- final KeyEvent longPressEvent = KeyEvent.obtain(event);
- mHandler.post(() -> {
- handleLongPressOnHome(longPressEvent);
- longPressEvent.recycle();
- });
+ mHandler.post(() -> handleLongPressOnHome(event));
}
}
return true;
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index 96f4a01..c2666f6 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -297,7 +297,8 @@
return;
}
- for (ClientState clientState : mClients.values()) {
+
+ for (ClientState clientState : mClients.values().toArray(new ClientState[0])) {
tryRespondWithError(
clientState.mDelegatingListener.mRemoteListener,
SpeechRecognizer.ERROR_SERVER_DISCONNECTED);
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 4b463c7..b87e761 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -275,15 +275,18 @@
// The previous animation leash will be dropped when preparing fade-in animation, so
// simply apply new animation without restoring the transformation.
fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
- } else if (op.mAction == Operation.ACTION_SEAMLESS
- && op.mLeash != null && op.mLeash.isValid()) {
+ } else if (op.isValidSeamless()) {
if (DEBUG) Slog.d(TAG, "finishOp undo seamless " + windowToken.getTopChild());
final SurfaceControl.Transaction t = windowToken.getSyncTransaction();
- t.setMatrix(op.mLeash, 1, 0, 0, 1);
- t.setPosition(op.mLeash, 0, 0);
+ clearTransform(t, op.mLeash);
}
}
+ private static void clearTransform(SurfaceControl.Transaction t, SurfaceControl sc) {
+ t.setMatrix(sc, 1, 0, 0, 1);
+ t.setPosition(sc, 0, 0);
+ }
+
/**
* Completes all operations such as applying fade-in animation on the previously hidden window
* tokens. This is called if all windows are ready in new rotation or timed out.
@@ -400,7 +403,19 @@
synchronized (mService.mGlobalLock) {
Slog.i(TAG, "Async rotation timeout: " + (!mIsStartTransactionCommitted
? " start transaction is not committed" : mTargetWindowTokens));
- mIsStartTransactionCommitted = true;
+ if (!mIsStartTransactionCommitted) {
+ // The transaction commit timeout will be handled by:
+ // 1. BLASTSyncEngine will notify onTransactionCommitTimeout() and then
+ // apply the start transaction of transition.
+ // 2. The TransactionCommittedListener in setupStartTransaction() will be
+ // notified to finish the operations of mTargetWindowTokens.
+ // 3. The slow remote side will also apply the start transaction which may
+ // contain stale surface transform.
+ // 4. Finally, the slow remote reports transition finished. The cleanup
+ // transaction from step (1) will be applied when finishing transition,
+ // which will recover the stale state from (3).
+ return;
+ }
mDisplayContent.finishAsyncRotationIfPossible();
mService.mWindowPlacerLocked.performSurfacePlacement();
}
@@ -539,6 +554,20 @@
});
}
+ /** Called when the start transition is ready, but it is not applied in time. */
+ void onTransactionCommitTimeout(SurfaceControl.Transaction t) {
+ if (mIsStartTransactionCommitted) return;
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final Operation op = mTargetWindowTokens.valueAt(i);
+ op.mIsCompletionPending = true;
+ if (op.isValidSeamless()) {
+ Slog.d(TAG, "Transaction timeout. Clear transform for "
+ + mTargetWindowTokens.keyAt(i).getTopChild());
+ clearTransform(t, op.mLeash);
+ }
+ }
+ }
+
/** Called when the transition by shell is done. */
void onTransitionFinished() {
if (mTransitionOp == OP_CHANGE) {
@@ -681,6 +710,10 @@
mAction = action;
}
+ boolean isValidSeamless() {
+ return mAction == ACTION_SEAMLESS && mLeash != null && mLeash.isValid();
+ }
+
@Override
public String toString() {
return "Operation{a=" + mAction + " pending=" + mIsCompletionPending + '}';
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 98ee98b..4444709 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -95,6 +95,7 @@
interface TransactionReadyListener {
void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
+ default void onTransactionCommitTimeout() {}
}
/**
@@ -249,6 +250,7 @@
" commit callback. Application ANR likely to follow.");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
synchronized (mWm.mGlobalLock) {
+ mListener.onTransactionCommitTimeout();
onCommitted(merged.mNativeObject != 0
? merged : mWm.mTransactionFactory.get());
}
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 4bafccd..e97bda2 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -418,7 +418,7 @@
callerAppUid = realCallingUid;
}
// don't abort if the callerApp or other processes of that uid are allowed in any way
- if (callerApp != null && useCallingUidState) {
+ if (callerApp != null && (useCallingUidState || callerAppBasedOnPiSender)) {
// first check the original calling process
final @BalCode int balAllowedForCaller = callerApp
.areBackgroundActivityStartsAllowed(appSwitchState);
@@ -509,6 +509,7 @@
+ "; intent: " + intent
+ "; callerApp: " + callerApp
+ "; inVisibleTask: " + (callerApp != null && callerApp.hasActivityInVisibleTask())
+ + "; resultIfPiSenderAllowsBal: " + balCodeToString(resultIfPiSenderAllowsBal)
+ "]";
if (resultIfPiSenderAllowsBal != BAL_BLOCK) {
// We should have returned before if !logVerdictChangeByPiDefaultChange
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index d461d1e..a1b8949 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -130,6 +130,7 @@
private final int mUndockedHdmiRotation;
private final RotationAnimationPair mTmpRotationAnim = new RotationAnimationPair();
private final RotationHistory mRotationHistory = new RotationHistory();
+ private final RotationLockHistory mRotationLockHistory = new RotationLockHistory();
private OrientationListener mOrientationListener;
private StatusBarManagerInternal mStatusBarManagerInternal;
@@ -922,7 +923,8 @@
}
@VisibleForTesting
- void setUserRotation(int userRotationMode, int userRotation) {
+ void setUserRotation(int userRotationMode, int userRotation, String caller) {
+ mRotationLockHistory.addRecord(userRotationMode, userRotation, caller);
mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
if (useDefaultSettingsProvider()) {
// We'll be notified via settings listener, so we don't need to update internal values.
@@ -953,17 +955,17 @@
}
}
- void freezeRotation(int rotation) {
+ void freezeRotation(int rotation, String caller) {
if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis(mDisplayContent)) {
rotation = RotationUtils.reverseRotationDirectionAroundZAxis(rotation);
}
rotation = (rotation == -1) ? mRotation : rotation;
- setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);
+ setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation, caller);
}
- void thawRotation() {
- setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
+ void thawRotation(String caller) {
+ setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation, caller);
}
boolean isRotationFrozen() {
@@ -1712,6 +1714,15 @@
r.dump(prefix, pw);
}
}
+
+ if (!mRotationLockHistory.mRecords.isEmpty()) {
+ pw.println();
+ pw.println(prefix + " RotationLockHistory");
+ prefix = " " + prefix;
+ for (RotationLockHistory.Record r : mRotationLockHistory.mRecords) {
+ r.dump(prefix, pw);
+ }
+ }
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -2133,6 +2144,40 @@
}
}
+ private static class RotationLockHistory {
+ private static final int MAX_SIZE = 8;
+
+ private static class Record {
+ @WindowManagerPolicy.UserRotationMode final int mUserRotationMode;
+ @Surface.Rotation final int mUserRotation;
+ final String mCaller;
+ final long mTimestamp = System.currentTimeMillis();
+
+ private Record(int userRotationMode, int userRotation, String caller) {
+ mUserRotationMode = userRotationMode;
+ mUserRotation = userRotation;
+ mCaller = caller;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + TimeUtils.logTimeOfDay(mTimestamp) + ": "
+ + "mode=" + WindowManagerPolicy.userRotationModeToString(mUserRotationMode)
+ + ", rotation=" + Surface.rotationToString(mUserRotation)
+ + ", caller=" + mCaller);
+ }
+ }
+
+ private final ArrayDeque<RotationLockHistory.Record> mRecords = new ArrayDeque<>(MAX_SIZE);
+
+ void addRecord(@WindowManagerPolicy.UserRotationMode int userRotationMode,
+ @Surface.Rotation int userRotation, String caller) {
+ if (mRecords.size() >= MAX_SIZE) {
+ mRecords.removeFirst();
+ }
+ mRecords.addLast(new Record(userRotationMode, userRotation, caller));
+ }
+ }
+
private static class RotationHistory {
private static final int MAX_SIZE = 8;
private static final int NO_FOLD_CONTROLLER = -2;
diff --git a/services/core/java/com/android/server/wm/DisplayRotationReversionController.java b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java
index d3a8a82..4eb2d88 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationReversionController.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationReversionController.java
@@ -118,7 +118,8 @@
if (mDisplayContent.getDisplayRotation().isRotationFrozen()) {
mDisplayContent.getDisplayRotation().setUserRotation(
mUserRotationModeOverridden,
- mUserRotationOverridden);
+ mUserRotationOverridden,
+ /* caller= */ "DisplayRotationReversionController#revertOverride");
return true;
} else {
return false;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 0674ec1..bbe44c5 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -42,6 +42,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ClipData;
@@ -100,6 +101,8 @@
final IWindowSessionCallback mCallback;
final int mUid;
final int mPid;
+ @NonNull
+ final WindowProcessController mProcess;
private final String mStringName;
SurfaceSession mSurfaceSession;
private int mNumWindow = 0;
@@ -126,11 +129,23 @@
final boolean mSetsUnrestrictedKeepClearAreas;
public Session(WindowManagerService service, IWindowSessionCallback callback) {
+ this(service, callback, Binder.getCallingPid(), Binder.getCallingUid());
+ }
+
+ @VisibleForTesting
+ Session(WindowManagerService service, IWindowSessionCallback callback,
+ int callingPid, int callingUid) {
mService = service;
mCallback = callback;
- mUid = Binder.getCallingUid();
- mPid = Binder.getCallingPid();
- mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
+ mPid = callingPid;
+ mUid = callingUid;
+ synchronized (service.mGlobalLock) {
+ mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
+ mProcess = service.mAtmService.mProcessMap.getProcess(mPid);
+ }
+ if (mProcess == null) {
+ throw new IllegalStateException("Unknown pid=" + mPid + " uid=" + mUid);
+ }
mCanAddInternalSystemWindow = service.mContext.checkCallingOrSelfPermission(
INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED;
mCanForceShowingInsets = service.mAtmService.isCallerRecents(mUid)
@@ -715,13 +730,8 @@
void windowAddedLocked() {
if (mPackageName == null) {
- final WindowProcessController wpc = mService.mAtmService.mProcessMap.getProcess(mPid);
- if (wpc != null) {
- mPackageName = wpc.mInfo.packageName;
- mRelayoutTag = "relayoutWindow: " + mPackageName;
- } else {
- Slog.e(TAG_WM, "Unknown process pid=" + mPid);
- }
+ mPackageName = mProcess.mInfo.packageName;
+ mRelayoutTag = "relayoutWindow: " + mPackageName;
}
if (mSurfaceSession == null) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index bbafa25..fac98b8 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1683,6 +1683,18 @@
}
}
+ @Override
+ public void onTransactionCommitTimeout() {
+ if (mCleanupTransaction == null) return;
+ for (int i = mTargetDisplays.size() - 1; i >= 0; --i) {
+ final DisplayContent dc = mTargetDisplays.get(i);
+ final AsyncRotationController asyncRotationController = dc.getAsyncRotationController();
+ if (asyncRotationController != null && containsChangeFor(dc, mTargets)) {
+ asyncRotationController.onTransactionCommitTimeout(mCleanupTransaction);
+ }
+ }
+ }
+
/**
* Collect tasks which moved-to-top as part of this transition. This also updates the
* controller's latest-reported when relevant.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9a1920d..a6d285a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4281,8 +4281,8 @@
}
@Override
- public void freezeRotation(int rotation) {
- freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation);
+ public void freezeRotation(int rotation, String caller) {
+ freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation, caller);
}
/**
@@ -4292,7 +4292,7 @@
* @param rotation The desired rotation to freeze to, or -1 to use the current rotation.
*/
@Override
- public void freezeDisplayRotation(int displayId, int rotation) {
+ public void freezeDisplayRotation(int displayId, int rotation, String caller) {
// TODO(multi-display): Track which display is rotated.
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
"freezeRotation()")) {
@@ -4302,6 +4302,9 @@
throw new IllegalArgumentException("Rotation argument must be -1 or a valid "
+ "rotation constant.");
}
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s",
+ getDefaultDisplayRotation(), rotation, caller);
final long origId = Binder.clearCallingIdentity();
try {
@@ -4311,7 +4314,7 @@
Slog.w(TAG, "Trying to freeze rotation for a missing display.");
return;
}
- display.getDisplayRotation().freezeRotation(rotation);
+ display.getDisplayRotation().freezeRotation(rotation, caller);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -4321,8 +4324,8 @@
}
@Override
- public void thawRotation() {
- thawDisplayRotation(Display.DEFAULT_DISPLAY);
+ public void thawRotation(String caller) {
+ thawDisplayRotation(Display.DEFAULT_DISPLAY, caller);
}
/**
@@ -4330,13 +4333,14 @@
* Persists across reboots.
*/
@Override
- public void thawDisplayRotation(int displayId) {
+ public void thawDisplayRotation(int displayId, String caller) {
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
"thawRotation()")) {
throw new SecurityException("Requires SET_ORIENTATION permission");
}
- ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d", getDefaultDisplayRotation());
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d, caller=%s",
+ getDefaultDisplayRotation(), caller);
final long origId = Binder.clearCallingIdentity();
try {
@@ -4346,7 +4350,7 @@
Slog.w(TAG, "Trying to thaw rotation for a missing display.");
return;
}
- display.getDisplayRotation().thawRotation();
+ display.getDisplayRotation().thawRotation(caller);
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 74a0bafd3..8fad950 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -438,7 +438,8 @@
}
if ("free".equals(lockMode)) {
- mInternal.thawDisplayRotation(displayId);
+ mInternal.thawDisplayRotation(displayId,
+ /* caller= */ "WindowManagerShellCommand#free");
return 0;
}
@@ -451,7 +452,8 @@
try {
final int rotation =
arg != null ? Integer.parseInt(arg) : -1 /* lock to current rotation */;
- mInternal.freezeDisplayRotation(displayId, rotation);
+ mInternal.freezeDisplayRotation(displayId, rotation,
+ /* caller= */ "WindowManagerShellCommand#lock");
return 0;
} catch (IllegalArgumentException e) {
getErrPrintWriter().println("Error: " + e.getMessage());
@@ -1433,7 +1435,8 @@
mInterface.setForcedDisplayScalingMode(displayId, DisplayContent.FORCE_SCALING_MODE_AUTO);
// user-rotation
- mInternal.thawDisplayRotation(displayId);
+ mInternal.thawDisplayRotation(displayId,
+ /* caller= */ "WindowManagerShellCommand#runReset");
// fixed-to-user-rotation
mInterface.setFixedToUserRotation(displayId, IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b12cc0b..ebef606 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -754,8 +754,6 @@
static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */
- private final WindowProcessController mWpcForDisplayAreaConfigChanges;
-
class DrawHandler {
Consumer<SurfaceControl.Transaction> mConsumer;
int mSeqId;
@@ -1129,7 +1127,6 @@
mBaseLayer = 0;
mSubLayer = 0;
mWinAnimator = null;
- mWpcForDisplayAreaConfigChanges = null;
mOverrideScale = 1f;
return;
}
@@ -1186,11 +1183,6 @@
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", this, parentWindow);
parentWindow.addChild(this, sWindowSubLayerComparator);
}
-
- // System process or invalid process cannot register to display area config change.
- mWpcForDisplayAreaConfigChanges = (s.mPid == MY_PID || s.mPid < 0)
- ? null
- : service.mAtmService.getProcessController(s.mPid, s.mUid);
}
boolean shouldWindowHandleBeTrusted(Session s) {
@@ -3621,14 +3613,17 @@
/** @return {@code true} if the process registered to a display area as a config listener. */
private boolean registeredForDisplayAreaConfigChanges() {
final WindowState parentWindow = getParentWindow();
- final WindowProcessController wpc = parentWindow != null
- ? parentWindow.mWpcForDisplayAreaConfigChanges
- : mWpcForDisplayAreaConfigChanges;
- return wpc != null && wpc.registeredForDisplayAreaConfigChanges();
+ final Session session = parentWindow != null ? parentWindow.mSession : mSession;
+ if (session.mPid == MY_PID) {
+ // System process cannot register to display area config change.
+ return false;
+ }
+ return session.mProcess.registeredForDisplayAreaConfigChanges();
}
+ @NonNull
WindowProcessController getProcess() {
- return mWpcForDisplayAreaConfigChanges;
+ return mSession.mProcess;
}
/**
diff --git a/services/foldables/devicestateprovider/Android.bp b/services/foldables/devicestateprovider/Android.bp
new file mode 100644
index 0000000..34737ef
--- /dev/null
+++ b/services/foldables/devicestateprovider/Android.bp
@@ -0,0 +1,13 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+ name: "foldable-device-state-provider",
+ srcs: [
+ "src/**/*.java"
+ ],
+ libs: [
+ "services",
+ ],
+}
diff --git a/services/foldables/devicestateprovider/OWNERS b/services/foldables/devicestateprovider/OWNERS
index b2dcd0c..5732844 100644
--- a/services/foldables/devicestateprovider/OWNERS
+++ b/services/foldables/devicestateprovider/OWNERS
@@ -1,6 +1,6 @@
akulian@google.com
-kennethford@google.com
jiamingliu@google.com
kchyn@google.com
+kennethford@google.com
nickchameyev@google.com
nicomazz@google.com
\ No newline at end of file
diff --git a/services/foldables/devicestateprovider/README.md b/services/foldables/devicestateprovider/README.md
new file mode 100644
index 0000000..90174c0
--- /dev/null
+++ b/services/foldables/devicestateprovider/README.md
@@ -0,0 +1,3 @@
+# Foldable Device State Provider library
+
+This library provides foldable-specific classes that could be used to implement a custom DeviceStateProvider.
\ No newline at end of file
diff --git a/services/foldables/devicestateprovider/TEST_MAPPING b/services/foldables/devicestateprovider/TEST_MAPPING
new file mode 100644
index 0000000..47de131
--- /dev/null
+++ b/services/foldables/devicestateprovider/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "foldable-device-state-provider-tests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
new file mode 100644
index 0000000..aea46d1
--- /dev/null
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -0,0 +1,537 @@
+/*
+ * 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.policy;
+
+import static android.hardware.SensorManager.SENSOR_DELAY_FASTEST;
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.annotation.IntRange;
+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.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.PowerManager;
+import android.hardware.display.DisplayManager;
+import android.os.Trace;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.devicestate.DeviceState;
+import com.android.server.devicestate.DeviceStateProvider;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.BooleanSupplier;
+import java.util.function.Function;
+
+/**
+ * Device state provider for foldable devices.
+ *
+ * It is an implementation of {@link DeviceStateProvider} tailored specifically for
+ * foldable devices and allows simple callback-based configuration with hall sensor
+ * and hinge angle sensor values.
+ */
+public final class FoldableDeviceStateProvider implements DeviceStateProvider,
+ SensorEventListener, PowerManager.OnThermalStatusChangedListener,
+ DisplayManager.DisplayListener {
+
+ private static final String TAG = "FoldableDeviceStateProvider";
+ private static final boolean DEBUG = false;
+
+ // Lock for internal state.
+ private final Object mLock = new Object();
+
+ // List of supported states in ascending order based on their identifier.
+ private final DeviceState[] mOrderedStates;
+
+ // Map of state identifier to a boolean supplier that returns true when all required conditions
+ // are met for the device to be in the state.
+ private final SparseArray<BooleanSupplier> mStateConditions = new SparseArray<>();
+
+ private final Sensor mHingeAngleSensor;
+ private final DisplayManager mDisplayManager;
+ private final Sensor mHallSensor;
+
+ @Nullable
+ @GuardedBy("mLock")
+ private Listener mListener = null;
+ @GuardedBy("mLock")
+ private int mLastReportedState = INVALID_DEVICE_STATE;
+ @GuardedBy("mLock")
+ private SensorEvent mLastHingeAngleSensorEvent = null;
+ @GuardedBy("mLock")
+ private SensorEvent mLastHallSensorEvent = null;
+ @GuardedBy("mLock")
+ private @PowerManager.ThermalStatus
+ int mThermalStatus = PowerManager.THERMAL_STATUS_NONE;
+ @GuardedBy("mLock")
+ private boolean mIsScreenOn = false;
+
+ @GuardedBy("mLock")
+ private boolean mPowerSaveModeEnabled;
+
+ public FoldableDeviceStateProvider(@NonNull Context context,
+ @NonNull SensorManager sensorManager,
+ @NonNull Sensor hingeAngleSensor,
+ @NonNull Sensor hallSensor,
+ @NonNull DisplayManager displayManager,
+ @NonNull DeviceStateConfiguration[] deviceStateConfigurations) {
+
+ Preconditions.checkArgument(deviceStateConfigurations.length > 0,
+ "Device state configurations array must not be empty");
+
+ mHingeAngleSensor = hingeAngleSensor;
+ mHallSensor = hallSensor;
+ mDisplayManager = displayManager;
+
+ sensorManager.registerListener(this, mHingeAngleSensor, SENSOR_DELAY_FASTEST);
+ sensorManager.registerListener(this, mHallSensor, SENSOR_DELAY_FASTEST);
+
+ mOrderedStates = new DeviceState[deviceStateConfigurations.length];
+ for (int i = 0; i < deviceStateConfigurations.length; i++) {
+ final DeviceStateConfiguration configuration = deviceStateConfigurations[i];
+ mOrderedStates[i] = configuration.mDeviceState;
+
+ if (mStateConditions.get(configuration.mDeviceState.getIdentifier()) != null) {
+ throw new IllegalArgumentException("Device state configurations must have unique"
+ + " device state identifiers, found duplicated identifier: " +
+ configuration.mDeviceState.getIdentifier());
+ }
+
+ mStateConditions.put(configuration.mDeviceState.getIdentifier(), () ->
+ configuration.mPredicate.apply(this));
+ }
+
+ mDisplayManager.registerDisplayListener(
+ /* listener = */ this,
+ /* handler= */ null,
+ /* eventsMask= */ DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
+
+ Arrays.sort(mOrderedStates, Comparator.comparingInt(DeviceState::getIdentifier));
+
+ PowerManager powerManager = context.getSystemService(PowerManager.class);
+ if (powerManager != null) {
+ // If any of the device states are thermal sensitive, i.e. it should be disabled when
+ // the device is overheating, then we will update the list of supported states when
+ // thermal status changes.
+ if (hasThermalSensitiveState(deviceStateConfigurations)) {
+ powerManager.addThermalStatusListener(this);
+ }
+
+ // If any of the device states are power sensitive, i.e. it should be disabled when
+ // power save mode is enabled, then we will update the list of supported states when
+ // power save mode is toggled.
+ if (hasPowerSaveSensitiveState(deviceStateConfigurations)) {
+ IntentFilter filter = new IntentFilter(
+ PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
+ BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL.equals(
+ intent.getAction())) {
+ onPowerSaveModeChanged(powerManager.isPowerSaveMode());
+ }
+ }
+ };
+ context.registerReceiver(receiver, filter);
+ }
+ }
+ }
+
+ @Override
+ public void setListener(Listener listener) {
+ synchronized (mLock) {
+ if (mListener != null) {
+ throw new RuntimeException("Provider already has a listener set.");
+ }
+ mListener = listener;
+ }
+ notifySupportedStatesChanged(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED);
+ notifyDeviceStateChangedIfNeeded();
+ }
+
+ /** Notifies the listener that the set of supported device states has changed. */
+ private void notifySupportedStatesChanged(@SupportedStatesUpdatedReason int reason) {
+ List<DeviceState> supportedStates = new ArrayList<>();
+ Listener listener;
+ synchronized (mLock) {
+ if (mListener == null) {
+ return;
+ }
+ listener = mListener;
+ for (DeviceState deviceState : mOrderedStates) {
+ if (isThermalStatusCriticalOrAbove(mThermalStatus)
+ && deviceState.hasFlag(
+ DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) {
+ continue;
+ }
+ if (mPowerSaveModeEnabled && deviceState.hasFlag(
+ DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
+ continue;
+ }
+ supportedStates.add(deviceState);
+ }
+ }
+
+ listener.onSupportedDeviceStatesChanged(
+ supportedStates.toArray(new DeviceState[supportedStates.size()]), reason);
+ }
+
+ /** Computes the current device state and notifies the listener of a change, if needed. */
+ void notifyDeviceStateChangedIfNeeded() {
+ int stateToReport = INVALID_DEVICE_STATE;
+ Listener listener;
+ synchronized (mLock) {
+ if (mListener == null) {
+ return;
+ }
+
+ listener = mListener;
+
+ int newState = INVALID_DEVICE_STATE;
+ for (int i = 0; i < mOrderedStates.length; i++) {
+ int state = mOrderedStates[i].getIdentifier();
+ if (DEBUG) {
+ Slog.d(TAG, "Checking conditions for " + mOrderedStates[i].getName() + "("
+ + i + ")");
+ }
+ boolean conditionSatisfied;
+ try {
+ conditionSatisfied = mStateConditions.get(state).getAsBoolean();
+ } catch (IllegalStateException e) {
+ // Failed to compute the current state based on current available data. Continue
+ // with the expectation that notifyDeviceStateChangedIfNeeded() will be called
+ // when a callback with the missing data is triggered. May trigger another state
+ // change if another state is satisfied currently.
+ Slog.w(TAG, "Unable to check current state = " + state, e);
+ dumpSensorValues();
+ continue;
+ }
+
+ if (conditionSatisfied) {
+ if (DEBUG) {
+ Slog.d(TAG, "Device State conditions satisfied, transition to " + state);
+ }
+ newState = state;
+ break;
+ }
+ }
+ if (newState == INVALID_DEVICE_STATE) {
+ Slog.e(TAG, "No declared device states match any of the required conditions.");
+ dumpSensorValues();
+ }
+
+ if (newState != INVALID_DEVICE_STATE && newState != mLastReportedState) {
+ mLastReportedState = newState;
+ stateToReport = newState;
+ }
+ }
+
+ if (stateToReport != INVALID_DEVICE_STATE) {
+ listener.onStateChanged(stateToReport);
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ synchronized (mLock) {
+ if (event.sensor == mHallSensor) {
+ mLastHallSensorEvent = event;
+ } else if (event.sensor == mHingeAngleSensor) {
+ mLastHingeAngleSensorEvent = event;
+ }
+ }
+ notifyDeviceStateChangedIfNeeded();
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Do nothing.
+ }
+
+ private float getSensorValue(@Nullable SensorEvent sensorEvent) {
+ if (sensorEvent == null) {
+ throw new IllegalStateException("Have not received sensor event.");
+ }
+
+ if (sensorEvent.values.length < 1) {
+ throw new IllegalStateException("Values in the sensor event are empty");
+ }
+
+ return sensorEvent.values[0];
+ }
+
+ @GuardedBy("mLock")
+ private void dumpSensorValues() {
+ Slog.i(TAG, "Sensor values:");
+ dumpSensorValues("Hall Sensor", mHallSensor, mLastHallSensorEvent);
+ dumpSensorValues("Hinge Angle Sensor",mHingeAngleSensor, mLastHingeAngleSensorEvent);
+ Slog.i(TAG, "isScreenOn: " + isScreenOn());
+ }
+
+ @GuardedBy("mLock")
+ private void dumpSensorValues(String sensorType, Sensor sensor, @Nullable SensorEvent event) {
+ String sensorString = sensor == null ? "null" : sensor.getName();
+ String eventValues = event == null ? "null" : Arrays.toString(event.values);
+ Slog.i(TAG, sensorType + " : " + sensorString + " : " + eventValues);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == DEFAULT_DISPLAY) {
+ // Could potentially be moved to the background if needed.
+ try {
+ Trace.beginSection("FoldableDeviceStateProvider#onDisplayChanged()");
+ int displayState = mDisplayManager.getDisplay(displayId).getState();
+ synchronized (mLock) {
+ mIsScreenOn = displayState == Display.STATE_ON;
+ }
+ } finally {
+ Trace.endSection();
+ }
+ }
+ }
+
+ /**
+ * Configuration for a single device state, contains information about the state like
+ * identifier, name, flags and a predicate that should return true if the state should
+ * be selected.
+ */
+ public static class DeviceStateConfiguration {
+ private final DeviceState mDeviceState;
+ private final Function<FoldableDeviceStateProvider, Boolean> mPredicate;
+
+ private DeviceStateConfiguration(DeviceState deviceState,
+ Function<FoldableDeviceStateProvider, Boolean> predicate) {
+ mDeviceState = deviceState;
+ mPredicate = predicate;
+ }
+
+ public static DeviceStateConfiguration createConfig(
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @NonNull String name,
+ @DeviceState.DeviceStateFlags int flags,
+ Function<FoldableDeviceStateProvider, Boolean> predicate
+ ) {
+ return new DeviceStateConfiguration(new DeviceState(identifier, name, flags),
+ predicate);
+ }
+
+ public static DeviceStateConfiguration createConfig(
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @NonNull String name,
+ Function<FoldableDeviceStateProvider, Boolean> predicate
+ ) {
+ return new DeviceStateConfiguration(new DeviceState(identifier, name, /* flags= */ 0),
+ predicate);
+ }
+
+ /**
+ * Creates a device state configuration for a closed tent-mode aware state.
+ *
+ * During tent mode:
+ * - The inner display is OFF
+ * - The outer display is ON
+ * - The device is partially unfolded (left and right edges could be on the table)
+ * In this mode the device the device so it could be used in a posture where both left
+ * and right edges of the unfolded device are on the table.
+ *
+ * The predicate returns false after the hinge angle reaches
+ * {@code tentModeSwitchAngleDegrees}. Then it switches back only when the hinge angle
+ * becomes less than {@code maxClosedAngleDegrees}. Hinge angle is 0 degrees when the device
+ * is fully closed and 180 degrees when it is fully unfolded.
+ *
+ * For example, when tentModeSwitchAngleDegrees = 90 and maxClosedAngleDegrees = 5 degrees:
+ * - when unfolding the device from fully closed posture (last state == closed or it is
+ * undefined yet) this state will become not matching after reaching the angle
+ * of 90 degrees, it allows the device to switch the outer display to the inner display
+ * only when reaching this threshold
+ * - when folding (last state != 'closed') this state will become matching after reaching
+ * the angle less than 5 degrees and when hall sensor detected that the device is closed,
+ * so the switch from the inner display to the outer will become only when the device
+ * is fully closed.
+ *
+ * @param identifier state identifier
+ * @param name state name
+ * @param flags state flags
+ * @param minClosedAngleDegrees minimum (inclusive) hinge angle value for the closed state
+ * @param maxClosedAngleDegrees maximum (non-inclusive) hinge angle value for the closed
+ * state
+ * @param tentModeSwitchAngleDegrees the angle when this state should switch when unfolding
+ * @return device state configuration
+ */
+ public static DeviceStateConfiguration createTentModeClosedState(
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @NonNull String name,
+ @DeviceState.DeviceStateFlags int flags,
+ int minClosedAngleDegrees,
+ int maxClosedAngleDegrees,
+ int tentModeSwitchAngleDegrees
+ ) {
+ return new DeviceStateConfiguration(new DeviceState(identifier, name, flags),
+ (stateContext) -> {
+ final boolean hallSensorClosed = stateContext.isHallSensorClosed();
+ final float hingeAngle = stateContext.getHingeAngle();
+ final int lastState = stateContext.getLastReportedDeviceState();
+ final boolean isScreenOn = stateContext.isScreenOn();
+
+ final int switchingDegrees =
+ isScreenOn ? tentModeSwitchAngleDegrees : maxClosedAngleDegrees;
+
+ final int closedDeviceState = identifier;
+ final boolean isLastStateClosed = lastState == closedDeviceState
+ || lastState == INVALID_DEVICE_STATE;
+
+ final boolean shouldBeClosedBecauseTentMode = isLastStateClosed
+ && hingeAngle >= minClosedAngleDegrees
+ && hingeAngle < switchingDegrees;
+
+ final boolean shouldBeClosedBecauseFullyShut = hallSensorClosed
+ && hingeAngle >= minClosedAngleDegrees
+ && hingeAngle < maxClosedAngleDegrees;
+
+ return shouldBeClosedBecauseFullyShut || shouldBeClosedBecauseTentMode;
+ });
+ }
+ }
+
+ /**
+ * @return Whether the screen is on.
+ */
+ public boolean isScreenOn() {
+ synchronized (mLock) {
+ return mIsScreenOn;
+ }
+ }
+ /**
+ * @return current hinge angle value of a foldable device
+ */
+ public float getHingeAngle() {
+ synchronized (mLock) {
+ return getSensorValue(mLastHingeAngleSensorEvent);
+ }
+ }
+
+ /**
+ * @return true if hall sensor detected that the device is closed (fully shut)
+ */
+ public boolean isHallSensorClosed() {
+ synchronized (mLock) {
+ return getSensorValue(mLastHallSensorEvent) > 0f;
+ }
+ }
+
+ /**
+ * @return last reported device state
+ */
+ public int getLastReportedDeviceState() {
+ synchronized (mLock) {
+ return mLastReportedState;
+ }
+ }
+
+ @VisibleForTesting
+ void onPowerSaveModeChanged(boolean isPowerSaveModeEnabled) {
+ synchronized (mLock) {
+ if (mPowerSaveModeEnabled != isPowerSaveModeEnabled) {
+ mPowerSaveModeEnabled = isPowerSaveModeEnabled;
+ notifySupportedStatesChanged(
+ isPowerSaveModeEnabled ? SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED
+ : SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED);
+ }
+ }
+ }
+
+ @Override
+ public void onThermalStatusChanged(@PowerManager.ThermalStatus int thermalStatus) {
+ int previousThermalStatus;
+ synchronized (mLock) {
+ previousThermalStatus = mThermalStatus;
+ mThermalStatus = thermalStatus;
+ }
+
+ boolean isThermalStatusCriticalOrAbove = isThermalStatusCriticalOrAbove(thermalStatus);
+ boolean isPreviousThermalStatusCriticalOrAbove =
+ isThermalStatusCriticalOrAbove(previousThermalStatus);
+ if (isThermalStatusCriticalOrAbove != isPreviousThermalStatusCriticalOrAbove) {
+ Slog.i(TAG, "Updating supported device states due to thermal status change."
+ + " isThermalStatusCriticalOrAbove: " + isThermalStatusCriticalOrAbove);
+ notifySupportedStatesChanged(
+ isThermalStatusCriticalOrAbove
+ ? SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL
+ : SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL);
+ }
+ }
+
+ private static boolean isThermalStatusCriticalOrAbove(
+ @PowerManager.ThermalStatus int thermalStatus) {
+ switch (thermalStatus) {
+ case PowerManager.THERMAL_STATUS_CRITICAL:
+ case PowerManager.THERMAL_STATUS_EMERGENCY:
+ case PowerManager.THERMAL_STATUS_SHUTDOWN:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static boolean hasThermalSensitiveState(DeviceStateConfiguration[] deviceStates) {
+ for (int i = 0; i < deviceStates.length; i++) {
+ DeviceStateConfiguration state = deviceStates[i];
+ if (state.mDeviceState
+ .hasFlag(DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasPowerSaveSensitiveState(DeviceStateConfiguration[] deviceStates) {
+ for (int i = 0; i < deviceStates.length; i++) {
+ if (deviceStates[i].mDeviceState
+ .hasFlag(DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/services/foldables/devicestateprovider/tests/Android.bp b/services/foldables/devicestateprovider/tests/Android.bp
new file mode 100644
index 0000000..a8db05e
--- /dev/null
+++ b/services/foldables/devicestateprovider/tests/Android.bp
@@ -0,0 +1,30 @@
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "foldable-device-state-provider-tests",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libmultiplejvmtiagentsinterferenceagent",
+ "libstaticjvmtiagent",
+ ],
+ static_libs: [
+ "services",
+ "foldable-device-state-provider",
+ "androidx.test.rules",
+ "junit",
+ "truth-prebuilt",
+ "mockito-target-extended-minus-junit4",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.test.ext.junit",
+ "testables",
+ ],
+ test_suites: ["device-tests"]
+}
diff --git a/services/foldables/devicestateprovider/tests/AndroidManifest.xml b/services/foldables/devicestateprovider/tests/AndroidManifest.xml
new file mode 100644
index 0000000..736613d
--- /dev/null
+++ b/services/foldables/devicestateprovider/tests/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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="com.android.foldablesdevicestatelib.tests">
+
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.foldablesdevicestatelib.tests"
+ android:label="Tests for foldable-device-state-provider library">
+ </instrumentation>
+
+</manifest>
\ No newline at end of file
diff --git a/services/foldables/devicestateprovider/tests/AndroidTest.xml b/services/foldables/devicestateprovider/tests/AndroidTest.xml
new file mode 100644
index 0000000..f5fdac7
--- /dev/null
+++ b/services/foldables/devicestateprovider/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<configuration description="foldable-device-state-provider tests">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="foldable-device-state-provider-tests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.foldablesdevicestatelib.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java
new file mode 100644
index 0000000..8fa4ce5
--- /dev/null
+++ b/services/foldables/devicestateprovider/tests/src/com/android/server/policy/FoldableDeviceStateProviderTest.java
@@ -0,0 +1,519 @@
+/*
+ * 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.policy;
+
+
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL;
+import com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_OFF;
+import static android.view.Display.STATE_ON;
+
+import static com.android.server.policy.FoldableDeviceStateProvider.DeviceStateConfiguration.createConfig;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.nullable;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManager;
+import android.hardware.input.InputSensorInfo;
+import android.os.PowerManager;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.view.Display;
+
+import com.android.server.devicestate.DeviceState;
+import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.devicestate.DeviceStateProvider.Listener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.internal.util.reflection.FieldSetter;
+
+/**
+ * Unit tests for {@link FoldableDeviceStateProvider}.
+ * <p/>
+ * Run with <code>atest FoldableDeviceStateProviderTest</code>.
+ */
+@RunWith(AndroidTestingRunner.class)
+public final class FoldableDeviceStateProviderTest {
+
+ private final ArgumentCaptor<DeviceState[]> mDeviceStateArrayCaptor = ArgumentCaptor.forClass(
+ DeviceState[].class);
+ @Captor
+ private ArgumentCaptor<Integer> mIntegerCaptor;
+ @Captor
+ private ArgumentCaptor<DisplayManager.DisplayListener> mDisplayListenerCaptor;
+ @Mock
+ private SensorManager mSensorManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private InputSensorInfo mInputSensorInfo;
+ private Sensor mHallSensor;
+ private Sensor mHingeAngleSensor;
+ @Mock
+ private DisplayManager mDisplayManager;
+ private FoldableDeviceStateProvider mProvider;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mHallSensor = new Sensor(mInputSensorInfo);
+ mHingeAngleSensor = new Sensor(mInputSensorInfo);
+ }
+
+ @Test
+ public void create_emptyConfiguration_throwsException() {
+ assertThrows(IllegalArgumentException.class, this::createProvider);
+ }
+
+ @Test
+ public void create_duplicatedDeviceStateIdentifiers_throwsException() {
+ assertThrows(IllegalArgumentException.class,
+ () -> createProvider(
+ createConfig(
+ /* identifier= */ 0, /* name= */ "ONE", (c) -> true),
+ createConfig(
+ /* identifier= */ 0, /* name= */ "TWO", (c) -> true)
+ ));
+ }
+
+ @Test
+ public void create_allMatchingStatesDefaultsToTheFirstIdentifier() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> true),
+ createConfig(
+ /* identifier= */ 2, /* name= */ "TWO", (c) -> true),
+ createConfig(
+ /* identifier= */ 3, /* name= */ "THREE", (c) -> true)
+ );
+
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ final DeviceState[] expectedStates = new DeviceState[]{
+ new DeviceState(1, "ONE", /* flags= */ 0),
+ new DeviceState(2, "TWO", /* flags= */ 0),
+ new DeviceState(3, "THREE", /* flags= */ 0),
+ };
+ assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(1, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void create_multipleMatchingStatesDefaultsToTheLowestIdentifier() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> false),
+ createConfig(
+ /* identifier= */ 3, /* name= */ "THREE", (c) -> false),
+ createConfig(
+ /* identifier= */ 4, /* name= */ "FOUR", (c) -> true),
+ createConfig(
+ /* identifier= */ 2, /* name= */ "TWO", (c) -> true)
+ );
+
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void test_hingeAngleUpdatedFirstTime_switchesToMatchingState() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 2, /* name= */ "TWO",
+ (c) -> c.getHingeAngle() >= 90f));
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener, never()).onStateChanged(anyInt());
+ clearInvocations(listener);
+
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 100f);
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void test_hallSensorUpdatedFirstTime_switchesToMatchingState() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE",
+ (c) -> !c.isHallSensorClosed()),
+ createConfig(/* identifier= */ 2, /* name= */ "TWO",
+ FoldableDeviceStateProvider::isHallSensorClosed));
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener, never()).onStateChanged(anyInt());
+ clearInvocations(listener);
+
+ // Hall sensor value '1f' is for the closed state
+ sendSensorEvent(mHallSensor, /* value= */ 1f);
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void test_hingeAngleUpdatedSecondTime_switchesToMatchingState() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 2, /* name= */ "TWO",
+ (c) -> c.getHingeAngle() >= 90f));
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 30f);
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(1, mIntegerCaptor.getValue().intValue());
+ clearInvocations(listener);
+
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 100f);
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void test_hallSensorUpdatedSecondTime_switchesToMatchingState() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE",
+ (c) -> !c.isHallSensorClosed()),
+ createConfig(/* identifier= */ 2, /* name= */ "TWO",
+ FoldableDeviceStateProvider::isHallSensorClosed));
+ sendSensorEvent(mHallSensor, /* value= */ 0f);
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(1, mIntegerCaptor.getValue().intValue());
+ clearInvocations(listener);
+
+ // Hall sensor value '1f' is for the closed state
+ sendSensorEvent(mHallSensor, /* value= */ 1f);
+
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void test_invalidSensorValues_onStateChangedIsNotTriggered() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "ONE",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 2, /* name= */ "TWO",
+ (c) -> c.getHingeAngle() >= 90f));
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ clearInvocations(listener);
+
+ // First, switch to a non-default state.
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 100f);
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+
+ clearInvocations(listener);
+
+ // Then, send an invalid sensor event, verify that onStateChanged() is not triggered.
+ sendInvalidSensorEvent(mHingeAngleSensor);
+
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
+ }
+
+ @Test
+ public void test_nullSensorValues_noExceptionThrown() throws Exception {
+ createProvider(createConfig( /* identifier= */ 1, /* name= */ "ONE",
+ (c) -> c.getHingeAngle() < 90f));
+ sendInvalidSensorEvent(null);
+ }
+
+ @Test
+ public void test_flagDisableWhenThermalStatusCritical() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "CLOSED",
+ (c) -> c.getHingeAngle() < 5f),
+ createConfig(/* identifier= */ 2, /* name= */ "HALF_OPENED",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 3, /* name= */ "OPENED",
+ (c) -> c.getHingeAngle() < 180f),
+ createConfig(/* identifier= */ 4, /* name= */ "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE,
+ (c) -> true));
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)},
+ mDeviceStateArrayCaptor.getValue());
+ clearInvocations(listener);
+
+ mProvider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_MODERATE);
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ clearInvocations(listener);
+
+ // The THERMAL_TEST state should be disabled.
+ mProvider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_CRITICAL);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */)},
+ mDeviceStateArrayCaptor.getValue());
+ clearInvocations(listener);
+
+ // The THERMAL_TEST state should be re-enabled.
+ mProvider.onThermalStatusChanged(PowerManager.THERMAL_STATUS_LIGHT);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)},
+ mDeviceStateArrayCaptor.getValue());
+ }
+
+ @Test
+ public void test_flagDisableWhenPowerSaveEnabled() throws Exception {
+ createProvider(createConfig(/* identifier= */ 1, /* name= */ "CLOSED",
+ (c) -> c.getHingeAngle() < 5f),
+ createConfig(/* identifier= */ 2, /* name= */ "HALF_OPENED",
+ (c) -> c.getHingeAngle() < 90f),
+ createConfig(/* identifier= */ 3, /* name= */ "OPENED",
+ (c) -> c.getHingeAngle() < 180f),
+ createConfig(/* identifier= */ 4, /* name= */ "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE,
+ (c) -> true));
+ mProvider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
+ mDeviceStateArrayCaptor.getValue());
+ clearInvocations(listener);
+
+ mProvider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ clearInvocations(listener);
+
+ // The THERMAL_TEST state should be disabled due to power save being enabled.
+ mProvider.onPowerSaveModeChanged(true /* isPowerSaveModeEnabled */);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */) },
+ mDeviceStateArrayCaptor.getValue());
+ clearInvocations(listener);
+
+ // The THERMAL_TEST state should be re-enabled.
+ mProvider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
+ mDeviceStateArrayCaptor.getValue());
+ }
+
+ @Test
+ public void test_previousStateBasedPredicate() {
+ // Create a configuration where state TWO could be matched only if
+ // the previous state was 'THREE'
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> c.getHingeAngle() < 30f),
+ createConfig(
+ /* identifier= */ 2, /* name= */ "TWO",
+ (c) -> c.getLastReportedDeviceState() == 3 && c.getHingeAngle() > 120f),
+ createConfig(
+ /* identifier= */ 3, /* name= */ "THREE",
+ (c) -> c.getHingeAngle() > 90f)
+ );
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 0f);
+ Listener listener = mock(Listener.class);
+ mProvider.setListener(listener);
+
+ // Check that the initial state is 'ONE'
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(1, mIntegerCaptor.getValue().intValue());
+ clearInvocations(listener);
+
+ // Should not match state 'TWO', it should match only state 'THREE'
+ // (because the previous state is not 'THREE', it is 'ONE')
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 180f);
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(3, mIntegerCaptor.getValue().intValue());
+ clearInvocations(listener);
+
+ // Now it should match state 'TWO'
+ // (because the previous state is 'THREE' now)
+ sendSensorEvent(mHingeAngleSensor, /* value= */ 180f);
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+ }
+
+ @Test
+ public void isScreenOn_afterDisplayChangedToOn_returnsTrue() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> true)
+ );
+
+ setScreenOn(true);
+
+ assertThat(mProvider.isScreenOn()).isTrue();
+ }
+
+ @Test
+ public void isScreenOn_afterDisplayChangedToOff_returnsFalse() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> true)
+ );
+
+ setScreenOn(false);
+
+ assertThat(mProvider.isScreenOn()).isFalse();
+ }
+
+ @Test
+ public void isScreenOn_afterDisplayChangedToOnThenOff_returnsFalse() {
+ createProvider(
+ createConfig(
+ /* identifier= */ 1, /* name= */ "ONE", (c) -> true)
+ );
+
+ setScreenOn(true);
+ setScreenOn(false);
+
+ assertThat(mProvider.isScreenOn()).isFalse();
+ }
+
+ private void setScreenOn(boolean isOn) {
+ Display mockDisplay = mock(Display.class);
+ int state = isOn ? STATE_ON : STATE_OFF;
+ when(mockDisplay.getState()).thenReturn(state);
+ when(mDisplayManager.getDisplay(eq(DEFAULT_DISPLAY))).thenReturn(mockDisplay);
+ mDisplayListenerCaptor.getValue().onDisplayChanged(DEFAULT_DISPLAY);
+ }
+
+ private void sendSensorEvent(Sensor sensor, float value) {
+ SensorEvent event = mock(SensorEvent.class);
+ event.sensor = sensor;
+ try {
+ FieldSetter.setField(event, event.getClass().getField("values"),
+ new float[]{value});
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ }
+
+ mProvider.onSensorChanged(event);
+ }
+
+ private void sendInvalidSensorEvent(Sensor sensor) {
+ SensorEvent event = mock(SensorEvent.class);
+ event.sensor = sensor;
+ try {
+ // Set empty values array to make the event invalid
+ FieldSetter.setField(event, event.getClass().getField("values"),
+ new float[]{});
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ }
+ mProvider.onSensorChanged(event);
+ }
+
+ private void createProvider(DeviceStateConfiguration... configurations) {
+ mProvider = new FoldableDeviceStateProvider(mContext, mSensorManager, mHingeAngleSensor,
+ mHallSensor, mDisplayManager, configurations);
+ verify(mDisplayManager)
+ .registerDisplayListener(
+ mDisplayListenerCaptor.capture(),
+ nullable(Handler.class),
+ anyLong());
+ }
+}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionDefinitionsTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionDefinitionsTest.kt
new file mode 100644
index 0000000..832136c
--- /dev/null
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionDefinitionsTest.kt
@@ -0,0 +1,764 @@
+/*
+ * 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.permission.test
+
+import android.content.pm.PermissionInfo
+import android.os.Build
+import com.android.server.permission.access.MutateStateScope
+import com.android.server.permission.access.permission.Permission
+import com.android.server.permission.access.permission.PermissionFlags
+import com.android.server.pm.pkg.PackageState
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Parameterized test for testing permission definitions (adopt permissions, add permission groups,
+ * add permissions, trim permissions, trim permission states and revoke permissions on
+ * package update) for onStorageVolumeAdded() and onPackageAdded() in AppIdPermissionPolicy.
+ *
+ * Note that the evaluatePermissionState() call with permission changes
+ * (i.e. changedPermissionNames in AppIdPermissionPolicy) and the evaluatePermissionState() call
+ * with an installedPackageState is put in this test instead of
+ * AppIdPermissionPolicyPermissionStatesTest because these concepts don't apply to onUserAdded().
+ */
+@RunWith(Parameterized::class)
+class AppIdPermissionPolicyPermissionDefinitionsTest : BaseAppIdPermissionPolicyTest() {
+ @Parameterized.Parameter(0) lateinit var action: Action
+
+ @Test
+ fun testAdoptPermissions_permissionsOfMissingSystemApp_getsAdopted() {
+ testAdoptPermissions(hasMissingPackage = true, isSystem = true)
+
+ assertWithMessage(
+ "After $action is called for a null adopt permission package," +
+ " the permission package name: ${getPermission(PERMISSION_NAME_0)?.packageName}" +
+ " did not match the expected package name: $PACKAGE_NAME_0"
+ )
+ .that(getPermission(PERMISSION_NAME_0)?.packageName)
+ .isEqualTo(PACKAGE_NAME_0)
+ }
+
+ @Test
+ fun testAdoptPermissions_permissionsOfExistingSystemApp_notAdopted() {
+ testAdoptPermissions(isSystem = true)
+
+ assertWithMessage(
+ "After $action is called for a non-null adopt permission" +
+ " package, the permission package name:" +
+ " ${getPermission(PERMISSION_NAME_0)?.packageName} should not match the" +
+ " package name: $PACKAGE_NAME_0"
+ )
+ .that(getPermission(PERMISSION_NAME_0)?.packageName)
+ .isNotEqualTo(PACKAGE_NAME_0)
+ }
+
+ @Test
+ fun testAdoptPermissions_permissionsOfNonSystemApp_notAdopted() {
+ testAdoptPermissions(hasMissingPackage = true)
+
+ assertWithMessage(
+ "After $action is called for a non-system adopt permission" +
+ " package, the permission package name:" +
+ " ${getPermission(PERMISSION_NAME_0)?.packageName} should not match the" +
+ " package name: $PACKAGE_NAME_0"
+ )
+ .that(getPermission(PERMISSION_NAME_0)?.packageName)
+ .isNotEqualTo(PACKAGE_NAME_0)
+ }
+
+ private fun testAdoptPermissions(
+ hasMissingPackage: Boolean = false,
+ isSystem: Boolean = false
+ ) {
+ val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_1)
+ val packageToAdoptPermission = if (hasMissingPackage) {
+ mockPackageState(APP_ID_1, PACKAGE_NAME_1, isSystem = isSystem)
+ } else {
+ mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1,
+ permissions = listOf(parsedPermission)
+ ),
+ isSystem = isSystem
+ )
+ }
+ addPackageState(packageToAdoptPermission)
+ addPermission(parsedPermission)
+
+ mutateState {
+ val installedPackage = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(
+ PACKAGE_NAME_0,
+ permissions = listOf(defaultPermission),
+ adoptPermissions = listOf(PACKAGE_NAME_1)
+ )
+ )
+ addPackageState(installedPackage, newState)
+ testAction(installedPackage)
+ }
+ }
+
+ @Test
+ fun testPermissionGroupDefinition_newPermissionGroup_getsDeclared() {
+ mutateState {
+ val packageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+ addPackageState(packageState, newState)
+ testAction(packageState)
+ }
+
+ assertWithMessage(
+ "After $action is called when there is no existing" +
+ " permission groups, the new permission group $PERMISSION_GROUP_NAME_0 is not added"
+ )
+ .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.name)
+ .isEqualTo(PERMISSION_GROUP_NAME_0)
+ }
+
+ @Test
+ fun testPermissionGroupDefinition_systemAppTakingOverPermissionGroupDefinition_getsTakenOver() {
+ testTakingOverPermissionAndPermissionGroupDefinitions(newPermissionOwnerIsSystem = true)
+
+ assertWithMessage(
+ "After $action is called when $PERMISSION_GROUP_NAME_0 already" +
+ " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover the" +
+ " ownership of this permission group"
+ )
+ .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName)
+ .isEqualTo(PACKAGE_NAME_0)
+ }
+
+ @Test
+ fun testPermissionGroupDefinition_instantApps_remainsUnchanged() {
+ testTakingOverPermissionAndPermissionGroupDefinitions(
+ newPermissionOwnerIsInstant = true,
+ permissionGroupAlreadyExists = false
+ )
+
+ assertWithMessage(
+ "After $action is called for an instant app," +
+ " the new permission group $PERMISSION_GROUP_NAME_0 should not be added"
+ )
+ .that(getPermissionGroup(PERMISSION_GROUP_NAME_0))
+ .isNull()
+ }
+
+ @Test
+ fun testPermissionGroupDefinition_nonSystemAppTakingOverGroupDefinition_remainsUnchanged() {
+ testTakingOverPermissionAndPermissionGroupDefinitions()
+
+ assertWithMessage(
+ "After $action is called when $PERMISSION_GROUP_NAME_0 already" +
+ " exists in the system, non-system app $PACKAGE_NAME_0 shouldn't takeover" +
+ " ownership of this permission group"
+ )
+ .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName)
+ .isEqualTo(PACKAGE_NAME_1)
+ }
+
+ @Test
+ fun testPermissionGroupDefinition_takingOverGroupDeclaredBySystemApp_remainsUnchanged() {
+ testTakingOverPermissionAndPermissionGroupDefinitions(oldPermissionOwnerIsSystem = true)
+
+ assertWithMessage(
+ "After $action is called when $PERMISSION_GROUP_NAME_0 already" +
+ " exists in the system and is owned by a system app, app $PACKAGE_NAME_0" +
+ " shouldn't takeover ownership of this permission group"
+ )
+ .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName)
+ .isEqualTo(PACKAGE_NAME_1)
+ }
+
+ @Test
+ fun testPermissionDefinition_newPermission_getsDeclared() {
+ mutateState {
+ val packageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+ addPackageState(packageState, newState)
+ testAction(packageState)
+ }
+
+ assertWithMessage(
+ "After $action is called when there is no existing" +
+ " permissions, the new permission $PERMISSION_NAME_0 is not added"
+ )
+ .that(getPermission(PERMISSION_NAME_0)?.name)
+ .isEqualTo(PERMISSION_NAME_0)
+ }
+
+ @Test
+ fun testPermissionDefinition_configPermission_getsTakenOver() {
+ testTakingOverPermissionAndPermissionGroupDefinitions(
+ oldPermissionOwnerIsSystem = true,
+ newPermissionOwnerIsSystem = true,
+ type = Permission.TYPE_CONFIG,
+ isReconciled = false
+ )
+
+ assertWithMessage(
+ "After $action is called for a config permission with" +
+ " no owner, the ownership is not taken over by a system app $PACKAGE_NAME_0"
+ )
+ .that(getPermission(PERMISSION_NAME_0)?.packageName)
+ .isEqualTo(PACKAGE_NAME_0)
+ }
+
+ @Test
+ fun testPermissionDefinition_systemAppTakingOverPermissionDefinition_getsTakenOver() {
+ testTakingOverPermissionAndPermissionGroupDefinitions(newPermissionOwnerIsSystem = true)
+
+ assertWithMessage(
+ "After $action is called when $PERMISSION_NAME_0 already" +
+ " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover ownership" +
+ " of this permission"
+ )
+ .that(getPermission(PERMISSION_NAME_0)?.packageName)
+ .isEqualTo(PACKAGE_NAME_0)
+ }
+
+ @Test
+ fun testPermissionDefinition_nonSystemAppTakingOverPermissionDefinition_remainsUnchanged() {
+ testTakingOverPermissionAndPermissionGroupDefinitions()
+
+ assertWithMessage(
+ "After $action is called when $PERMISSION_NAME_0 already" +
+ " exists in the system, the non-system app $PACKAGE_NAME_0 shouldn't takeover" +
+ " ownership of this permission"
+ )
+ .that(getPermission(PERMISSION_NAME_0)?.packageName)
+ .isEqualTo(PACKAGE_NAME_1)
+ }
+
+ @Test
+ fun testPermissionDefinition_takingOverPermissionDeclaredBySystemApp_remainsUnchanged() {
+ testTakingOverPermissionAndPermissionGroupDefinitions(oldPermissionOwnerIsSystem = true)
+
+ assertWithMessage(
+ "After $action is called when $PERMISSION_NAME_0 already" +
+ " exists in system and is owned by a system app, the $PACKAGE_NAME_0 shouldn't" +
+ " takeover ownership of this permission"
+ )
+ .that(getPermission(PERMISSION_NAME_0)?.packageName)
+ .isEqualTo(PACKAGE_NAME_1)
+ }
+
+ private fun testTakingOverPermissionAndPermissionGroupDefinitions(
+ oldPermissionOwnerIsSystem: Boolean = false,
+ newPermissionOwnerIsSystem: Boolean = false,
+ newPermissionOwnerIsInstant: Boolean = false,
+ permissionGroupAlreadyExists: Boolean = true,
+ permissionAlreadyExists: Boolean = true,
+ type: Int = Permission.TYPE_MANIFEST,
+ isReconciled: Boolean = true,
+ ) {
+ val oldPermissionOwnerPackageState = mockPackageState(
+ APP_ID_1,
+ PACKAGE_NAME_1,
+ isSystem = oldPermissionOwnerIsSystem
+ )
+ addPackageState(oldPermissionOwnerPackageState)
+ if (permissionGroupAlreadyExists) {
+ addPermissionGroup(mockParsedPermissionGroup(PERMISSION_GROUP_NAME_0, PACKAGE_NAME_1))
+ }
+ if (permissionAlreadyExists) {
+ addPermission(
+ mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_1),
+ type = type,
+ isReconciled = isReconciled
+ )
+ }
+
+ mutateState {
+ val newPermissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockSimpleAndroidPackage(),
+ isSystem = newPermissionOwnerIsSystem,
+ isInstantApp = newPermissionOwnerIsInstant
+ )
+ addPackageState(newPermissionOwnerPackageState, newState)
+ testAction(newPermissionOwnerPackageState)
+ }
+ }
+
+ @Test
+ fun testPermissionChanged_permissionGroupChanged_getsRevoked() {
+ testPermissionChanged(
+ oldPermissionGroup = PERMISSION_GROUP_NAME_1,
+ newPermissionGroup = PERMISSION_GROUP_NAME_0
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that has a permission group change" +
+ " for a permission it defines, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testPermissionChanged_protectionLevelChanged_getsRevoked() {
+ testPermissionChanged(newProtectionLevel = PermissionInfo.PROTECTION_INTERNAL)
+
+ val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that has a protection level change" +
+ " for a permission it defines, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ private fun testPermissionChanged(
+ oldPermissionGroup: String? = null,
+ newPermissionGroup: String? = null,
+ newProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS
+ ) {
+ val oldPermission = mockParsedPermission(
+ PERMISSION_NAME_0,
+ PACKAGE_NAME_0,
+ group = oldPermissionGroup,
+ protectionLevel = PermissionInfo.PROTECTION_DANGEROUS
+ )
+ val oldPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(oldPermission))
+ )
+ addPackageState(oldPackageState)
+ addPermission(oldPermission)
+ setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED)
+
+ mutateState {
+ val newPermission = mockParsedPermission(
+ PERMISSION_NAME_0,
+ PACKAGE_NAME_0,
+ group = newPermissionGroup,
+ protectionLevel = newProtectionLevel
+ )
+ val newPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(newPermission))
+ )
+ addPackageState(newPackageState, newState)
+ testAction(newPackageState)
+ }
+ }
+
+ @Test
+ fun testPermissionDeclaration_permissionTreeNoLongerDeclared_getsDefinitionRemoved() {
+ testPermissionDeclaration {}
+
+ assertWithMessage(
+ "After $action is called for a package that no longer defines a permission" +
+ " tree, the permission tree: $PERMISSION_NAME_0 in system state should be removed"
+ )
+ .that(getPermissionTree(PERMISSION_NAME_0))
+ .isNull()
+ }
+
+ @Test
+ fun testPermissionDeclaration_permissionTreeByDisabledSystemPackage_remainsUnchanged() {
+ testPermissionDeclaration {
+ val disabledSystemPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+ addDisabledSystemPackageState(disabledSystemPackageState)
+ }
+
+ assertWithMessage(
+ "After $action is called for a package that no longer defines" +
+ " a permission tree while this permission tree is still defined by" +
+ " a disabled system package, the permission tree: $PERMISSION_NAME_0 in" +
+ " system state should not be removed"
+ )
+ .that(getPermissionTree(PERMISSION_TREE_NAME))
+ .isNotNull()
+ }
+
+ @Test
+ fun testPermissionDeclaration_permissionNoLongerDeclared_getsDefinitionRemoved() {
+ testPermissionDeclaration {}
+
+ assertWithMessage(
+ "After $action is called for a package that no longer defines a permission," +
+ " the permission: $PERMISSION_NAME_0 in system state should be removed"
+ )
+ .that(getPermission(PERMISSION_NAME_0))
+ .isNull()
+ }
+
+ @Test
+ fun testPermissionDeclaration_permissionByDisabledSystemPackage_remainsUnchanged() {
+ testPermissionDeclaration {
+ val disabledSystemPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+ addDisabledSystemPackageState(disabledSystemPackageState)
+ }
+
+ assertWithMessage(
+ "After $action is called for a disabled system package and it's updated apk" +
+ " no longer defines a permission, the permission: $PERMISSION_NAME_0 in" +
+ " system state should not be removed"
+ )
+ .that(getPermission(PERMISSION_NAME_0))
+ .isNotNull()
+ }
+
+ private fun testPermissionDeclaration(additionalSetup: () -> Unit) {
+ val oldPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+ addPackageState(oldPackageState)
+ addPermission(defaultPermissionTree)
+ addPermission(defaultPermission)
+
+ additionalSetup()
+
+ mutateState {
+ val newPackageState = mockPackageState(APP_ID_0, mockAndroidPackage(PACKAGE_NAME_0))
+ addPackageState(newPackageState, newState)
+ testAction(newPackageState)
+ }
+ }
+
+ @Test
+ fun testTrimPermissionStates_permissionsNoLongerRequested_getsFlagsRevoked() {
+ val parsedPermission = mockParsedPermission(
+ PERMISSION_NAME_0,
+ PACKAGE_NAME_0,
+ protectionLevel = PermissionInfo.PROTECTION_DANGEROUS
+ )
+ val oldPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(
+ PACKAGE_NAME_0,
+ permissions = listOf(parsedPermission),
+ requestedPermissions = setOf(PERMISSION_NAME_0)
+ )
+ )
+ addPackageState(oldPackageState)
+ addPermission(parsedPermission)
+ setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED)
+
+ mutateState {
+ val newPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+ addPackageState(newPackageState, newState)
+ testAction(newPackageState)
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that no longer requests a permission" +
+ " the actual permission flags $actualFlags should match the" +
+ " expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testRevokePermissionsOnPackageUpdate_storageAndMediaDowngradingPastQ_getsRuntimeRevoked() {
+ testRevokePermissionsOnPackageUpdate(
+ PermissionFlags.RUNTIME_GRANTED,
+ newTargetSdkVersion = Build.VERSION_CODES.P
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that's downgrading past Q" +
+ " the actual permission flags $actualFlags should match the" +
+ " expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testRevokePermissionsOnPackageUpdate_storageAndMediaNotDowngradingPastQ_remainsUnchanged() {
+ val oldFlags = PermissionFlags.RUNTIME_GRANTED
+ testRevokePermissionsOnPackageUpdate(
+ oldFlags,
+ oldTargetSdkVersion = Build.VERSION_CODES.P,
+ newTargetSdkVersion = Build.VERSION_CODES.P
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After $action is called for a package that's not downgrading past Q" +
+ " the actual permission flags $actualFlags should match the" +
+ " expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testRevokePermissionsOnPackageUpdate_policyFixedDowngradingPastQ_remainsUnchanged() {
+ val oldFlags = PermissionFlags.RUNTIME_GRANTED and PermissionFlags.POLICY_FIXED
+ testRevokePermissionsOnPackageUpdate(oldFlags, newTargetSdkVersion = Build.VERSION_CODES.P)
+
+ val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After $action is called for a package that's downgrading past Q" +
+ " the actual permission flags with PermissionFlags.POLICY_FIXED $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testRevokePermissionsOnPackageUpdate_newlyRequestingLegacyExternalStorage_runtimeRevoked() {
+ testRevokePermissionsOnPackageUpdate(
+ PermissionFlags.RUNTIME_GRANTED,
+ oldTargetSdkVersion = Build.VERSION_CODES.P,
+ newTargetSdkVersion = Build.VERSION_CODES.P,
+ oldIsRequestLegacyExternalStorage = false
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package with" +
+ " newlyRequestingLegacyExternalStorage, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testRevokePermissionsOnPackageUpdate_missingOldPackage_remainsUnchanged() {
+ val oldFlags = PermissionFlags.RUNTIME_GRANTED
+ testRevokePermissionsOnPackageUpdate(
+ oldFlags,
+ newTargetSdkVersion = Build.VERSION_CODES.P,
+ isOldPackageMissing = true
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After $action is called for a package that's downgrading past Q" +
+ " and doesn't have the oldPackage, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ private fun testRevokePermissionsOnPackageUpdate(
+ oldFlags: Int,
+ oldTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ newTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ oldIsRequestLegacyExternalStorage: Boolean = true,
+ newIsRequestLegacyExternalStorage: Boolean = true,
+ isOldPackageMissing: Boolean = false
+ ) {
+ val parsedPermission = mockParsedPermission(
+ PERMISSION_READ_EXTERNAL_STORAGE,
+ PACKAGE_NAME_0,
+ protectionLevel = PermissionInfo.PROTECTION_DANGEROUS
+ )
+ val oldPackageState = if (isOldPackageMissing) {
+ mockPackageState(APP_ID_0, PACKAGE_NAME_0)
+ } else {
+ mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(
+ PACKAGE_NAME_0,
+ targetSdkVersion = oldTargetSdkVersion,
+ isRequestLegacyExternalStorage = oldIsRequestLegacyExternalStorage,
+ requestedPermissions = setOf(PERMISSION_READ_EXTERNAL_STORAGE),
+ permissions = listOf(parsedPermission)
+ )
+ )
+ }
+ addPackageState(oldPackageState)
+ addPermission(parsedPermission)
+ setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE, oldFlags)
+
+ mutateState {
+ val newPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(
+ PACKAGE_NAME_0,
+ targetSdkVersion = newTargetSdkVersion,
+ isRequestLegacyExternalStorage = newIsRequestLegacyExternalStorage,
+ requestedPermissions = setOf(PERMISSION_READ_EXTERNAL_STORAGE),
+ permissions = listOf(parsedPermission)
+ )
+ )
+ addPackageState(newPackageState, newState)
+ testAction(newPackageState)
+ }
+ }
+
+ @Test
+ fun testEvaluatePermissionState_normalPermissionRequestedByInstalledPackage_getsGranted() {
+ val oldFlags = PermissionFlags.INSTALL_REVOKED
+ val permissionOwnerPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
+ val installedPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPackageState(installedPackageState)
+ addPermission(defaultPermission)
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)
+
+ mutateState {
+ testAction(installedPackageState)
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+ assertWithMessage(
+ "After $action is called for a package that requests a normal permission" +
+ " with the INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags since it's a new install"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * We set up a permission protection level change from SIGNATURE to NORMAL in order to make
+ * the permission a "changed permission" in order to test evaluatePermissionState() called by
+ * evaluatePermissionStateForAllPackages(). This makes the requestingPackageState not the
+ * installedPackageState so that we can test whether requesting by system package will give us
+ * the expected permission flags.
+ *
+ * Besides, this also helps us test evaluatePermissionStateForAllPackages(). Since both
+ * evaluatePermissionStateForAllPackages() and evaluateAllPermissionStatesForPackage() call
+ * evaluatePermissionState() in their implementations, we use these tests as the only tests
+ * that test evaluatePermissionStateForAllPackages()
+ */
+ @Test
+ fun testEvaluatePermissionState_normalPermissionRequestedBySystemPackage_getsGranted() {
+ testEvaluateNormalPermissionStateWithPermissionChanges(requestingPackageIsSystem = true)
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+ assertWithMessage(
+ "After $action is called for a system package that requests a normal" +
+ " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_normalCompatibilityPermission_getsGranted() {
+ testEvaluateNormalPermissionStateWithPermissionChanges(
+ permissionName = PERMISSION_POST_NOTIFICATIONS,
+ requestingPackageTargetSdkVersion = Build.VERSION_CODES.S
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_POST_NOTIFICATIONS)
+ val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+ assertWithMessage(
+ "After $action is called for a package that requests a normal compatibility" +
+ " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_normalPermissionPreviouslyRevoked_getsInstallRevoked() {
+ testEvaluateNormalPermissionStateWithPermissionChanges()
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.INSTALL_REVOKED
+ assertWithMessage(
+ "After $action is called for a package that requests a normal" +
+ " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ private fun testEvaluateNormalPermissionStateWithPermissionChanges(
+ permissionName: String = PERMISSION_NAME_0,
+ requestingPackageTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ requestingPackageIsSystem: Boolean = false
+ ) {
+ val oldParsedPermission = mockParsedPermission(
+ permissionName,
+ PACKAGE_NAME_0,
+ protectionLevel = PermissionInfo.PROTECTION_SIGNATURE
+ )
+ val oldPermissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(oldParsedPermission))
+ )
+ val requestingPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1,
+ requestedPermissions = setOf(permissionName),
+ targetSdkVersion = requestingPackageTargetSdkVersion
+ ),
+ isSystem = requestingPackageIsSystem,
+ )
+ addPackageState(oldPermissionOwnerPackageState)
+ addPackageState(requestingPackageState)
+ addPermission(oldParsedPermission)
+ val oldFlags = PermissionFlags.INSTALL_REVOKED
+ setPermissionFlags(APP_ID_1, USER_ID_0, permissionName, oldFlags)
+
+ mutateState {
+ val newParsedPermission = mockParsedPermission(permissionName, PACKAGE_NAME_0)
+ val newPermissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(newParsedPermission))
+ )
+ addPackageState(newPermissionOwnerPackageState, newState)
+ testAction(newPermissionOwnerPackageState)
+ }
+ }
+
+ private fun MutateStateScope.testAction(packageState: PackageState) {
+ with(appIdPermissionPolicy) {
+ when (action) {
+ Action.ON_PACKAGE_ADDED -> onPackageAdded(packageState)
+ Action.ON_STORAGE_VOLUME_ADDED -> onStorageVolumeMounted(
+ null,
+ listOf(packageState.packageName),
+ true
+ )
+ }
+ }
+ }
+
+ enum class Action { ON_PACKAGE_ADDED, ON_STORAGE_VOLUME_ADDED }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): Array<Action> = Action.values()
+ }
+}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
new file mode 100644
index 0000000..823ce45
--- /dev/null
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionResetTest.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.permission.test
+
+import android.content.pm.PermissionInfo
+import com.android.server.permission.access.MutateStateScope
+import com.android.server.permission.access.permission.PermissionFlags
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * A parameterized test for testing resetting runtime permissions for onPackageUninstalled()
+ * and resetRuntimePermissions() in AppIdPermissionPolicy
+ */
+@RunWith(Parameterized::class)
+class AppIdPermissionPolicyPermissionResetTest : BaseAppIdPermissionPolicyTest() {
+ @Parameterized.Parameter(0) lateinit var action: Action
+
+ @Test
+ fun testResetRuntimePermissions_runtimeGranted_getsRevoked() {
+ val oldFlags = PermissionFlags.RUNTIME_GRANTED
+ val expectedNewFlags = 0
+ testResetRuntimePermissions(oldFlags, expectedNewFlags)
+ }
+
+ @Test
+ fun testResetRuntimePermissions_roleGranted_getsGranted() {
+ val oldFlags = PermissionFlags.ROLE
+ val expectedNewFlags = PermissionFlags.ROLE or PermissionFlags.RUNTIME_GRANTED
+ testResetRuntimePermissions(oldFlags, expectedNewFlags)
+ }
+
+ @Test
+ fun testResetRuntimePermissions_nullAndroidPackage_remainsUnchanged() {
+ val oldFlags = PermissionFlags.RUNTIME_GRANTED
+ val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED
+ testResetRuntimePermissions(oldFlags, expectedNewFlags, isAndroidPackageMissing = true)
+ }
+
+ private fun testResetRuntimePermissions(
+ oldFlags: Int,
+ expectedNewFlags: Int,
+ isAndroidPackageMissing: Boolean = false
+ ) {
+ val parsedPermission = mockParsedPermission(
+ PERMISSION_NAME_0,
+ PACKAGE_NAME_0,
+ protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
+ )
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
+ )
+ val requestingPackageState = if (isAndroidPackageMissing) {
+ mockPackageState(APP_ID_1, PACKAGE_NAME_1)
+ } else {
+ mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ }
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)
+ addPackageState(permissionOwnerPackageState)
+ addPackageState(requestingPackageState)
+ addPermission(parsedPermission)
+
+ mutateState { testAction() }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ assertWithMessage(
+ "After resetting runtime permissions, permission flags did not match" +
+ " expected values: expectedNewFlags is $expectedNewFlags," +
+ " actualFlags is $actualFlags, while the oldFlags is $oldFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ private fun MutateStateScope.testAction(
+ packageName: String = PACKAGE_NAME_1,
+ appId: Int = APP_ID_1,
+ userId: Int = USER_ID_0
+ ) {
+ with(appIdPermissionPolicy) {
+ when (action) {
+ Action.ON_PACKAGE_UNINSTALLED -> onPackageUninstalled(packageName, appId, userId)
+ Action.RESET_RUNTIME_PERMISSIONS -> resetRuntimePermissions(packageName, userId)
+ }
+ }
+ }
+
+ enum class Action { ON_PACKAGE_UNINSTALLED, RESET_RUNTIME_PERMISSIONS }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): Array<Action> = Action.values()
+ }
+}
\ No newline at end of file
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
new file mode 100644
index 0000000..f085bd7
--- /dev/null
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyPermissionStatesTest.kt
@@ -0,0 +1,884 @@
+/*
+ * 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.permission.test
+
+import android.content.pm.PermissionInfo
+import android.os.Build
+import com.android.server.permission.access.MutableAccessState
+import com.android.server.permission.access.MutateStateScope
+import com.android.server.permission.access.immutable.IndexedListSet
+import com.android.server.permission.access.immutable.MutableIndexedListSet
+import com.android.server.permission.access.immutable.MutableIndexedMap
+import com.android.server.permission.access.permission.PermissionFlags
+import com.android.server.pm.permission.PermissionAllowlist
+import com.android.server.pm.pkg.PackageState
+import com.android.server.testutils.mock
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * A parameterized test for testing evaluating permission states and inheriting implicit permission
+ * states for onUserAdded(), onStorageVolumeAdded() and onPackageAdded() in AppIdPermissionPolicy
+ */
+@RunWith(Parameterized::class)
+class AppIdPermissionPolicyPermissionStatesTest : BaseAppIdPermissionPolicyTest() {
+ @Parameterized.Parameter(0) lateinit var action: Action
+
+ @Before
+ override fun setUp() {
+ super.setUp()
+ if (action == Action.ON_USER_ADDED) {
+ createUserState(USER_ID_NEW)
+ }
+ }
+
+ @Test
+ fun testEvaluatePermissionState_normalPermissionAlreadyGranted_remainsUnchanged() {
+ val oldFlags = PermissionFlags.INSTALL_GRANTED or PermissionFlags.INSTALL_REVOKED
+ testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_NORMAL) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After $action is called for a package that requests a normal permission" +
+ " with an existing INSTALL_GRANTED flag, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_normalPermissionNotInstallRevoked_getsGranted() {
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_NORMAL,
+ isNewInstall = true
+ ) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+ assertWithMessage(
+ "After $action is called for a package that requests a normal permission" +
+ " with no existing flags, the actual permission flags $actualFlags" +
+ " should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_normalAppOpPermission_getsRoleAndUserSetFlagsPreserved() {
+ val oldFlags = PermissionFlags.ROLE or PermissionFlags.USER_SET
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_NORMAL or PermissionInfo.PROTECTION_FLAG_APPOP
+ ) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.INSTALL_GRANTED or oldFlags
+ assertWithMessage(
+ "After $action is called for a package that requests a normal app op" +
+ " permission with existing ROLE and USER_SET flags, the actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_internalWasGrantedWithMissingPackage_getsProtectionGranted() {
+ val oldFlags = PermissionFlags.PROTECTION_GRANTED
+ testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_INTERNAL) {
+ val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
+ addPackageState(packageStateWithMissingPackage)
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After $action is called for a package that requests an internal permission" +
+ " with missing android package and $oldFlags flag, the actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_internalAppOpPermission_getsRoleAndUserSetFlagsPreserved() {
+ val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or
+ PermissionFlags.USER_SET
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_APPOP
+ ) {
+ val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
+ addPackageState(packageStateWithMissingPackage)
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After $action is called for a package that requests an internal permission" +
+ " with missing android package and $oldFlags flag and the permission isAppOp," +
+ " the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_internalDevelopmentPermission_getsRuntimeGrantedPreserved() {
+ val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_DEVELOPMENT
+ ) {
+ val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
+ addPackageState(packageStateWithMissingPackage)
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After $action is called for a package that requests an internal permission" +
+ " with missing android package and $oldFlags flag and permission isDevelopment," +
+ " the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_internalRolePermission_getsRoleAndRuntimeGrantedPreserved() {
+ val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or
+ PermissionFlags.RUNTIME_GRANTED
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_ROLE
+ ) {
+ val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
+ addPackageState(packageStateWithMissingPackage)
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After $action is called for a package that requests an internal permission" +
+ " with missing android package and $oldFlags flag and the permission isRole," +
+ " the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_signaturePrivilegedPermissionNotAllowlisted_isNotGranted() {
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_SIGNATURE or PermissionInfo.PROTECTION_FLAG_PRIVILEGED,
+ isInstalledPackageSystem = true,
+ isInstalledPackagePrivileged = true,
+ isInstalledPackageProduct = true,
+ // To mock the return value of shouldGrantPrivilegedOrOemPermission()
+ isInstalledPackageVendor = true,
+ isNewInstall = true
+ ) {
+ val platformPackage = mockPackageState(
+ PLATFORM_APP_ID,
+ mockAndroidPackage(PLATFORM_PACKAGE_NAME)
+ )
+ setupAllowlist(PACKAGE_NAME_1, false)
+ addPackageState(platformPackage)
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After $action is called for a package that requests a signature privileged" +
+ " permission that's not allowlisted, the actual permission" +
+ " flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_nonPrivilegedShouldGrantBySignature_getsProtectionGranted() {
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_SIGNATURE,
+ isInstalledPackageSystem = true,
+ isInstalledPackagePrivileged = true,
+ isInstalledPackageProduct = true,
+ isInstalledPackageSignatureMatching = true,
+ isInstalledPackageVendor = true,
+ isNewInstall = true
+ ) {
+ val platformPackage = mockPackageState(
+ PLATFORM_APP_ID,
+ mockAndroidPackage(PLATFORM_PACKAGE_NAME, isSignatureMatching = true)
+ )
+ setupAllowlist(PACKAGE_NAME_1, false)
+ addPackageState(platformPackage)
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED
+ assertWithMessage(
+ "After $action is called for a package that requests a signature" +
+ " non-privileged permission, the actual permission" +
+ " flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_privilegedAllowlistShouldGrantByProtectionFlags_getsGranted() {
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_SIGNATURE or PermissionInfo.PROTECTION_FLAG_PRIVILEGED,
+ isInstalledPackageSystem = true,
+ isInstalledPackagePrivileged = true,
+ isInstalledPackageProduct = true,
+ isNewInstall = true
+ ) {
+ val platformPackage = mockPackageState(
+ PLATFORM_APP_ID,
+ mockAndroidPackage(PLATFORM_PACKAGE_NAME)
+ )
+ setupAllowlist(PACKAGE_NAME_1, true)
+ addPackageState(platformPackage)
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED
+ assertWithMessage(
+ "After $action is called for a package that requests a signature privileged" +
+ " permission that's allowlisted and should grant by protection flags, the actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ private fun setupAllowlist(
+ packageName: String,
+ allowlistState: Boolean,
+ state: MutableAccessState = oldState
+ ) {
+ state.mutateExternalState().setPrivilegedPermissionAllowlistPackages(
+ MutableIndexedListSet<String>().apply { add(packageName) }
+ )
+ val mockAllowlist = mock<PermissionAllowlist> {
+ whenever(
+ getProductPrivilegedAppAllowlistState(packageName, PERMISSION_NAME_0)
+ ).thenReturn(allowlistState)
+ }
+ state.mutateExternalState().setPermissionAllowlist(mockAllowlist)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_nonRuntimeFlagsOnRuntimePermissions_getsCleared() {
+ val oldFlags = PermissionFlags.INSTALL_GRANTED or PermissionFlags.PREGRANT or
+ PermissionFlags.RUNTIME_GRANTED
+ testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.PREGRANT or PermissionFlags.RUNTIME_GRANTED
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime permission" +
+ " with existing $oldFlags flags, the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_newPermissionsForPreM_requiresUserReview() {
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP,
+ isNewInstall = true
+ ) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime permission" +
+ " with no existing flags in pre M, actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_legacyOrImplicitGrantedPreviouslyRevoked_getsAppOpRevoked() {
+ val oldFlags = PermissionFlags.USER_FIXED
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP
+ ) {
+ setPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0, oldFlags)
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.USER_FIXED or
+ PermissionFlags.APP_OP_REVOKED
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime permission" +
+ " that should be LEGACY_GRANTED or IMPLICIT_GRANTED that was previously revoked," +
+ " the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_legacyGrantedForPostM_userReviewRequirementRemoved() {
+ val oldFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT
+ testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime permission" +
+ " that used to require user review, the user review requirement should be removed" +
+ " if it's upgraded to post M. The actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_legacyGrantedPermissionsAlreadyReviewedForPostM_getsGranted() {
+ val oldFlags = PermissionFlags.LEGACY_GRANTED
+ testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime permission" +
+ " that was already reviewed by the user, the permission should be RUNTIME_GRANTED" +
+ " if it's upgraded to post M. The actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_leanbackNotificationPermissionsForPostM_getsImplicitGranted() {
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_POST_NOTIFICATIONS,
+ isNewInstall = true
+ ) {
+ oldState.mutateExternalState().setLeanback(true)
+ }
+
+ val actualFlags = getPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_POST_NOTIFICATIONS
+ )
+ val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime notification" +
+ " permission when isLeanback, the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_implicitSourceFromNonRuntime_getsImplicitGranted() {
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ implicitPermissions = setOf(PERMISSION_NAME_0),
+ isNewInstall = true
+ ) {
+ oldState.mutateExternalState().setImplicitToSourcePermissions(
+ MutableIndexedMap<String, IndexedListSet<String>>().apply {
+ put(PERMISSION_NAME_0, MutableIndexedListSet<String>().apply {
+ add(PERMISSION_NAME_1)
+ })
+ }
+ )
+ addPermission(mockParsedPermission(PERMISSION_NAME_1, PACKAGE_NAME_0))
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime implicit" +
+ " permission that's source from a non-runtime permission, the actual permission" +
+ " flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * For a legacy granted or implicit permission during the app upgrade, when the permission
+ * should no longer be legacy or implicit granted, we want to remove the APP_OP_REVOKED flag
+ * so that the app can request the permission.
+ */
+ @Test
+ fun testEvaluatePermissionState_noLongerLegacyOrImplicitGranted_canBeRequested() {
+ val oldFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.APP_OP_REVOKED or
+ PermissionFlags.RUNTIME_GRANTED
+ testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime permission" +
+ " that is no longer LEGACY_GRANTED or IMPLICIT_GRANTED, the actual permission" +
+ " flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_noLongerImplicit_getsRuntimeAndImplicitFlagsRemoved() {
+ val oldFlags = PermissionFlags.IMPLICIT or PermissionFlags.RUNTIME_GRANTED or
+ PermissionFlags.USER_SET or PermissionFlags.USER_FIXED
+ testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime permission" +
+ " that is no longer implicit and we shouldn't retain as nearby device" +
+ " permissions, the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_noLongerImplicitNearbyWasGranted_getsRuntimeGranted() {
+ val oldFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionName = PERMISSION_BLUETOOTH_CONNECT,
+ requestedPermissions = setOf(
+ PERMISSION_BLUETOOTH_CONNECT,
+ PERMISSION_ACCESS_BACKGROUND_LOCATION
+ )
+ ) {
+ setPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_ACCESS_BACKGROUND_LOCATION,
+ PermissionFlags.RUNTIME_GRANTED
+ )
+ }
+
+ val actualFlags = getPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_BLUETOOTH_CONNECT
+ )
+ val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime nearby device" +
+ " permission that was granted by implicit, the actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_noLongerImplicitSystemOrPolicyFixedWasGranted_runtimeGranted() {
+ val oldFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT or
+ PermissionFlags.SYSTEM_FIXED
+ testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.SYSTEM_FIXED
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime permission" +
+ " that was granted and is no longer implicit and is SYSTEM_FIXED or POLICY_FIXED," +
+ " the actual permission flags $actualFlags should match the expected" +
+ " flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_restrictedPermissionsNotExempt_getsRestrictionFlags() {
+ val oldFlags = PermissionFlags.RESTRICTION_REVOKED
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionInfoFlags = PermissionInfo.FLAG_HARD_RESTRICTED
+ ) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = oldFlags
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime hard" +
+ " restricted permission that is not exempted, the actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testEvaluatePermissionState_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
+ val oldFlags = 0
+ testEvaluatePermissionState(
+ oldFlags,
+ PermissionInfo.PROTECTION_DANGEROUS,
+ permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
+ ) {}
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.UPGRADE_EXEMPT
+ assertWithMessage(
+ "After $action is called for a package that requests a runtime soft" +
+ " restricted permission that is exempted, the actual permission flags" +
+ " $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testInheritImplicitPermissionStates_runtimeExistingImplicit_sourceFlagsNotInherited() {
+ val oldImplicitPermissionFlags = PermissionFlags.USER_FIXED
+ testInheritImplicitPermissionStates(
+ implicitPermissionFlags = oldImplicitPermissionFlags,
+ isNewInstallAndNewPermission = false
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = oldImplicitPermissionFlags or PermissionFlags.IMPLICIT_GRANTED or
+ PermissionFlags.APP_OP_REVOKED
+ assertWithMessage(
+ "After $action is called for a package that requests a permission that is" +
+ " implicit, existing and runtime, it should not inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testInheritImplicitPermissionStates_nonRuntimeNewImplicit_sourceFlagsNotInherited() {
+ testInheritImplicitPermissionStates(
+ implicitPermissionProtectionLevel = PermissionInfo.PROTECTION_NORMAL
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
+ assertWithMessage(
+ "After $action is called for a package that requests a permission that is" +
+ " implicit, new and non-runtime, it should not inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testInheritImplicitPermissionStates_runtimeNewImplicitPermissions_sourceFlagsInherited() {
+ val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+ testInheritImplicitPermissionStates(sourceRuntimeFlags = sourceRuntimeFlags)
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED or
+ PermissionFlags.IMPLICIT
+ assertWithMessage(
+ "After $action is called for a package that requests a permission that is" +
+ " implicit, new and runtime, it should inherit the runtime flags from" +
+ " the source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testInheritImplicitPermissionStates_grantingNewFromRevokeImplicit_onlyInheritFromSource() {
+ val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+ testInheritImplicitPermissionStates(
+ implicitPermissionFlags = PermissionFlags.POLICY_FIXED,
+ sourceRuntimeFlags = sourceRuntimeFlags,
+ isAnySourcePermissionNonRuntime = false
+ )
+
+ val actualFlags = getPermissionFlags(APP_ID_1, getUserIdEvaluated(), PERMISSION_NAME_0)
+ val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT
+ assertWithMessage(
+ "After $action is called for a package that requests a permission that is" +
+ " implicit, existing, runtime and revoked, it should only inherit runtime flags" +
+ " from source permission. Hence the actual permission flags $actualFlags should" +
+ " match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ /**
+ * If it's a media implicit permission (one of RETAIN_IMPLICIT_FLAGS_PERMISSIONS), we want to
+ * remove the IMPLICIT flag so that they will be granted when they are no longer implicit.
+ * (instead of revoking it)
+ */
+ @Test
+ fun testInheritImplicitPermissionStates_mediaImplicitPermissions_getsImplicitFlagRemoved() {
+ val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
+ testInheritImplicitPermissionStates(
+ implicitPermissionName = PERMISSION_ACCESS_MEDIA_LOCATION,
+ sourceRuntimeFlags = sourceRuntimeFlags
+ )
+
+ val actualFlags = getPermissionFlags(
+ APP_ID_1,
+ getUserIdEvaluated(),
+ PERMISSION_ACCESS_MEDIA_LOCATION
+ )
+ val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED
+ assertWithMessage(
+ "After $action is called for a package that requests a media permission that" +
+ " is implicit, new and runtime, it should inherit the runtime flags from" +
+ " the source permission and have the IMPLICIT flag removed. Hence the actual" +
+ " permission flags $actualFlags should match the expected flags $expectedNewFlags"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ private fun testInheritImplicitPermissionStates(
+ implicitPermissionName: String = PERMISSION_NAME_0,
+ implicitPermissionFlags: Int = 0,
+ implicitPermissionProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS,
+ sourceRuntimeFlags: Int = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET,
+ isAnySourcePermissionNonRuntime: Boolean = true,
+ isNewInstallAndNewPermission: Boolean = true
+ ) {
+ val userId = getUserIdEvaluated()
+ val implicitPermission = mockParsedPermission(
+ implicitPermissionName,
+ PACKAGE_NAME_0,
+ protectionLevel = implicitPermissionProtectionLevel,
+ )
+ // For source from non-runtime in order to grant by implicit
+ val sourcePermission1 = mockParsedPermission(
+ PERMISSION_NAME_1,
+ PACKAGE_NAME_0,
+ protectionLevel = if (isAnySourcePermissionNonRuntime) {
+ PermissionInfo.PROTECTION_NORMAL
+ } else {
+ PermissionInfo.PROTECTION_DANGEROUS
+ }
+ )
+ // For inheriting runtime flags
+ val sourcePermission2 = mockParsedPermission(
+ PERMISSION_NAME_2,
+ PACKAGE_NAME_0,
+ protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
+ )
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(
+ PACKAGE_NAME_0,
+ permissions = listOf(implicitPermission, sourcePermission1, sourcePermission2)
+ )
+ )
+ val installedPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1,
+ requestedPermissions = setOf(
+ implicitPermissionName,
+ PERMISSION_NAME_1,
+ PERMISSION_NAME_2
+ ),
+ implicitPermissions = setOf(implicitPermissionName)
+ )
+ )
+ oldState.mutateExternalState().setImplicitToSourcePermissions(
+ MutableIndexedMap<String, IndexedListSet<String>>().apply {
+ put(implicitPermissionName, MutableIndexedListSet<String>().apply {
+ add(PERMISSION_NAME_1)
+ add(PERMISSION_NAME_2)
+ })
+ }
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPermission(implicitPermission)
+ addPermission(sourcePermission1)
+ addPermission(sourcePermission2)
+ if (!isNewInstallAndNewPermission) {
+ addPackageState(installedPackageState)
+ setPermissionFlags(APP_ID_1, userId, implicitPermissionName, implicitPermissionFlags)
+ }
+ setPermissionFlags(APP_ID_1, userId, PERMISSION_NAME_2, sourceRuntimeFlags)
+
+ mutateState {
+ if (isNewInstallAndNewPermission) {
+ addPackageState(installedPackageState)
+ setPermissionFlags(
+ APP_ID_1,
+ userId,
+ implicitPermissionName,
+ implicitPermissionFlags,
+ newState
+ )
+ }
+ testAction(installedPackageState)
+ }
+ }
+
+ /**
+ * Setup simple package states for testing evaluatePermissionState().
+ * permissionOwnerPackageState is definer of permissionName with APP_ID_0.
+ * installedPackageState is the installed package that requests permissionName with APP_ID_1.
+ *
+ * @param oldFlags the existing permission flags for APP_ID_1, userId, permissionName
+ * @param protectionLevel the protectionLevel for the permission
+ * @param permissionName the name of the permission (1) being defined (2) of the oldFlags, and
+ * (3) requested by installedPackageState
+ * @param requestedPermissions the permissions requested by installedPackageState
+ * @param implicitPermissions the implicit permissions of installedPackageState
+ * @param permissionInfoFlags the flags for the permission itself
+ * @param isInstalledPackageSystem whether installedPackageState is a system package
+ *
+ * @return installedPackageState
+ */
+ private fun testEvaluatePermissionState(
+ oldFlags: Int,
+ protectionLevel: Int,
+ permissionName: String = PERMISSION_NAME_0,
+ requestedPermissions: Set<String> = setOf(permissionName),
+ implicitPermissions: Set<String> = emptySet(),
+ permissionInfoFlags: Int = 0,
+ isInstalledPackageSystem: Boolean = false,
+ isInstalledPackagePrivileged: Boolean = false,
+ isInstalledPackageProduct: Boolean = false,
+ isInstalledPackageSignatureMatching: Boolean = false,
+ isInstalledPackageVendor: Boolean = false,
+ installedPackageTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ isNewInstall: Boolean = false,
+ additionalSetup: () -> Unit
+ ) {
+ val userId = getUserIdEvaluated()
+ val parsedPermission = mockParsedPermission(
+ permissionName,
+ PACKAGE_NAME_0,
+ protectionLevel = protectionLevel,
+ flags = permissionInfoFlags
+ )
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
+ )
+ val installedPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(
+ PACKAGE_NAME_1,
+ requestedPermissions = requestedPermissions,
+ implicitPermissions = implicitPermissions,
+ targetSdkVersion = installedPackageTargetSdkVersion,
+ isSignatureMatching = isInstalledPackageSignatureMatching
+ ),
+ isSystem = isInstalledPackageSystem,
+ isPrivileged = isInstalledPackagePrivileged,
+ isProduct = isInstalledPackageProduct,
+ isVendor = isInstalledPackageVendor
+ )
+ addPackageState(permissionOwnerPackageState)
+ if (!isNewInstall) {
+ addPackageState(installedPackageState)
+ setPermissionFlags(APP_ID_1, userId, permissionName, oldFlags)
+ }
+ addPermission(parsedPermission)
+
+ additionalSetup()
+
+ mutateState {
+ if (isNewInstall) {
+ addPackageState(installedPackageState, newState)
+ setPermissionFlags(APP_ID_1, userId, permissionName, oldFlags, newState)
+ }
+ testAction(installedPackageState)
+ }
+ }
+
+ private fun getUserIdEvaluated(): Int = when (action) {
+ Action.ON_USER_ADDED -> USER_ID_NEW
+ Action.ON_STORAGE_VOLUME_ADDED, Action.ON_PACKAGE_ADDED -> USER_ID_0
+ }
+
+ private fun MutateStateScope.testAction(packageState: PackageState) {
+ with(appIdPermissionPolicy) {
+ when (action) {
+ Action.ON_USER_ADDED -> onUserAdded(getUserIdEvaluated())
+ Action.ON_STORAGE_VOLUME_ADDED -> onStorageVolumeMounted(
+ null,
+ listOf(packageState.packageName),
+ true
+ )
+ Action.ON_PACKAGE_ADDED -> onPackageAdded(packageState)
+ }
+ }
+ }
+
+ enum class Action { ON_USER_ADDED, ON_STORAGE_VOLUME_ADDED, ON_PACKAGE_ADDED }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): Array<Action> = Action.values()
+ }
+}
\ No newline at end of file
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
deleted file mode 100644
index 3cf57a3..0000000
--- a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/AppIdPermissionPolicyTest.kt
+++ /dev/null
@@ -1,1937 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.permission.test
-
-import android.Manifest
-import android.content.pm.PackageManager
-import android.content.pm.PermissionGroupInfo
-import android.content.pm.PermissionInfo
-import android.content.pm.SigningDetails
-import android.os.Build
-import android.os.Bundle
-import android.util.ArrayMap
-import android.util.SparseArray
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.modules.utils.testing.ExtendedMockitoRule
-import com.android.server.extendedtestutils.wheneverStatic
-import com.android.server.permission.access.MutableAccessState
-import com.android.server.permission.access.MutableUserState
-import com.android.server.permission.access.MutateStateScope
-import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
-import com.android.server.permission.access.permission.AppIdPermissionPolicy
-import com.android.server.permission.access.permission.Permission
-import com.android.server.permission.access.permission.PermissionFlags
-import com.android.server.permission.access.util.hasBits
-import com.android.server.pm.parsing.PackageInfoUtils
-import com.android.server.pm.permission.PermissionAllowlist
-import com.android.server.pm.pkg.AndroidPackage
-import com.android.server.pm.pkg.PackageState
-import com.android.server.pm.pkg.PackageUserState
-import com.android.server.pm.pkg.component.ParsedPermission
-import com.android.server.pm.pkg.component.ParsedPermissionGroup
-import com.android.server.testutils.any
-import com.android.server.testutils.mock
-import com.android.server.testutils.whenever
-import com.google.common.truth.Truth.assertWithMessage
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyLong
-
-/**
- * Mocking unit test for AppIdPermissionPolicy.
- */
-@RunWith(AndroidJUnit4::class)
-class AppIdPermissionPolicyTest {
- private lateinit var oldState: MutableAccessState
- private lateinit var newState: MutableAccessState
-
- private val defaultPermissionGroup = mockParsedPermissionGroup(
- PERMISSION_GROUP_NAME_0,
- PACKAGE_NAME_0
- )
- private val defaultPermissionTree = mockParsedPermission(
- PERMISSION_TREE_NAME,
- PACKAGE_NAME_0,
- isTree = true
- )
- private val defaultPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
-
- private val appIdPermissionPolicy = AppIdPermissionPolicy()
-
- @Rule
- @JvmField
- val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
- .spyStatic(PackageInfoUtils::class.java)
- .build()
-
- @Before
- fun setUp() {
- oldState = MutableAccessState()
- createUserState(USER_ID_0)
- oldState.mutateExternalState().setPackageStates(ArrayMap())
- oldState.mutateExternalState().setDisabledSystemPackageStates(ArrayMap())
- mockPackageInfoUtilsGeneratePermissionInfo()
- mockPackageInfoUtilsGeneratePermissionGroupInfo()
- }
-
- private fun createUserState(userId: Int) {
- oldState.mutateExternalState().mutateUserIds().add(userId)
- oldState.mutateUserStatesNoWrite().put(userId, MutableUserState())
- }
-
- private fun mockPackageInfoUtilsGeneratePermissionInfo() {
- wheneverStatic {
- PackageInfoUtils.generatePermissionInfo(any(ParsedPermission::class.java), anyLong())
- }.thenAnswer { invocation ->
- val parsedPermission = invocation.getArgument<ParsedPermission>(0)
- val generateFlags = invocation.getArgument<Long>(1)
- PermissionInfo(parsedPermission.backgroundPermission).apply {
- name = parsedPermission.name
- packageName = parsedPermission.packageName
- metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
- parsedPermission.metaData
- } else {
- null
- }
- @Suppress("DEPRECATION")
- protectionLevel = parsedPermission.protectionLevel
- group = parsedPermission.group
- flags = parsedPermission.flags
- }
- }
- }
-
- private fun mockPackageInfoUtilsGeneratePermissionGroupInfo() {
- wheneverStatic {
- PackageInfoUtils.generatePermissionGroupInfo(
- any(ParsedPermissionGroup::class.java),
- anyLong()
- )
- }.thenAnswer { invocation ->
- val parsedPermissionGroup = invocation.getArgument<ParsedPermissionGroup>(0)
- val generateFlags = invocation.getArgument<Long>(1)
- @Suppress("DEPRECATION")
- PermissionGroupInfo().apply {
- name = parsedPermissionGroup.name
- packageName = parsedPermissionGroup.packageName
- metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
- parsedPermissionGroup.metaData
- } else {
- null
- }
- flags = parsedPermissionGroup.flags
- }
- }
- }
-
- @Test
- fun testResetRuntimePermissions_runtimeGranted_getsRevoked() {
- val oldFlags = PermissionFlags.RUNTIME_GRANTED
- val expectedNewFlags = 0
- testResetRuntimePermissions(oldFlags, expectedNewFlags)
- }
-
- @Test
- fun testResetRuntimePermissions_roleGranted_getsGranted() {
- val oldFlags = PermissionFlags.ROLE
- val expectedNewFlags = PermissionFlags.ROLE or PermissionFlags.RUNTIME_GRANTED
- testResetRuntimePermissions(oldFlags, expectedNewFlags)
- }
-
- @Test
- fun testResetRuntimePermissions_nullAndroidPackage_remainsUnchanged() {
- val oldFlags = PermissionFlags.RUNTIME_GRANTED
- val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED
- testResetRuntimePermissions(oldFlags, expectedNewFlags, isAndroidPackageMissing = true)
- }
-
- private fun testResetRuntimePermissions(
- oldFlags: Int,
- expectedNewFlags: Int,
- isAndroidPackageMissing: Boolean = false
- ) {
- val parsedPermission = mockParsedPermission(
- PERMISSION_NAME_0,
- PACKAGE_NAME_0,
- protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
- )
- val permissionOwnerPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
- )
- val requestingPackageState = if (isAndroidPackageMissing) {
- mockPackageState(APP_ID_1, PACKAGE_NAME_1)
- } else {
- mockPackageState(
- APP_ID_1,
- mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
- )
- }
- setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)
- addPackageState(permissionOwnerPackageState)
- addPackageState(requestingPackageState)
- addPermission(parsedPermission)
-
- mutateState {
- with(appIdPermissionPolicy) {
- resetRuntimePermissions(PACKAGE_NAME_1, USER_ID_0)
- }
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- assertWithMessage(
- "After resetting runtime permissions, permission flags did not match" +
- " expected values: expectedNewFlags is $expectedNewFlags," +
- " actualFlags is $actualFlags, while the oldFlags is $oldFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_permissionsOfMissingSystemApp_getsAdopted() {
- testAdoptPermissions(hasMissingPackage = true, isSystem = true)
-
- assertWithMessage(
- "After onPackageAdded() is called for a null adopt permission package," +
- " the permission package name: ${getPermission(PERMISSION_NAME_0)?.packageName}" +
- " did not match the expected package name: $PACKAGE_NAME_0"
- )
- .that(getPermission(PERMISSION_NAME_0)?.packageName)
- .isEqualTo(PACKAGE_NAME_0)
- }
-
- @Test
- fun testOnPackageAdded_permissionsOfExistingSystemApp_notAdopted() {
- testAdoptPermissions(isSystem = true)
-
- assertWithMessage(
- "After onPackageAdded() is called for a non-null adopt permission" +
- " package, the permission package name:" +
- " ${getPermission(PERMISSION_NAME_0)?.packageName} should not match the" +
- " package name: $PACKAGE_NAME_0"
- )
- .that(getPermission(PERMISSION_NAME_0)?.packageName)
- .isNotEqualTo(PACKAGE_NAME_0)
- }
-
- @Test
- fun testOnPackageAdded_permissionsOfNonSystemApp_notAdopted() {
- testAdoptPermissions(hasMissingPackage = true)
-
- assertWithMessage(
- "After onPackageAdded() is called for a non-system adopt permission" +
- " package, the permission package name:" +
- " ${getPermission(PERMISSION_NAME_0)?.packageName} should not match the" +
- " package name: $PACKAGE_NAME_0"
- )
- .that(getPermission(PERMISSION_NAME_0)?.packageName)
- .isNotEqualTo(PACKAGE_NAME_0)
- }
-
- private fun testAdoptPermissions(
- hasMissingPackage: Boolean = false,
- isSystem: Boolean = false
- ) {
- val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_1)
- val packageToAdoptPermission = if (hasMissingPackage) {
- mockPackageState(APP_ID_1, PACKAGE_NAME_1, isSystem = isSystem)
- } else {
- mockPackageState(
- APP_ID_1,
- mockAndroidPackage(
- PACKAGE_NAME_1,
- permissions = listOf(parsedPermission)
- ),
- isSystem = isSystem
- )
- }
- addPackageState(packageToAdoptPermission)
- addPermission(parsedPermission)
-
- mutateState {
- val installedPackage = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(
- PACKAGE_NAME_0,
- permissions = listOf(defaultPermission),
- adoptPermissions = listOf(PACKAGE_NAME_1)
- )
- )
- addPackageState(installedPackage, newState)
- with(appIdPermissionPolicy) {
- onPackageAdded(installedPackage)
- }
- }
- }
-
- @Test
- fun testOnPackageAdded_newPermissionGroup_getsDeclared() {
- mutateState {
- val packageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
- addPackageState(packageState, newState)
- with(appIdPermissionPolicy) {
- onPackageAdded(packageState)
- }
- }
-
- assertWithMessage(
- "After onPackageAdded() is called when there is no existing" +
- " permission groups, the new permission group $PERMISSION_GROUP_NAME_0 is not added"
- )
- .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.name)
- .isEqualTo(PERMISSION_GROUP_NAME_0)
- }
-
- @Test
- fun testOnPackageAdded_systemAppTakingOverPermissionGroupDefinition_getsTakenOver() {
- testTakingOverPermissionAndPermissionGroupDefinitions(newPermissionOwnerIsSystem = true)
-
- assertWithMessage(
- "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" +
- " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover the" +
- " ownership of this permission group"
- )
- .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName)
- .isEqualTo(PACKAGE_NAME_0)
- }
-
- @Test
- fun testOnPackageAdded_instantApps_remainsUnchanged() {
- testTakingOverPermissionAndPermissionGroupDefinitions(
- newPermissionOwnerIsInstant = true,
- permissionGroupAlreadyExists = false
- )
-
- assertWithMessage(
- "After onPackageAdded() is called for an instant app," +
- " the new permission group $PERMISSION_GROUP_NAME_0 should not be added"
- )
- .that(getPermissionGroup(PERMISSION_GROUP_NAME_0))
- .isNull()
- }
-
- @Test
- fun testOnPackageAdded_nonSystemAppTakingOverPermissionGroupDefinition_remainsUnchanged() {
- testTakingOverPermissionAndPermissionGroupDefinitions()
-
- assertWithMessage(
- "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" +
- " exists in the system, non-system app $PACKAGE_NAME_0 shouldn't takeover" +
- " ownership of this permission group"
- )
- .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName)
- .isEqualTo(PACKAGE_NAME_1)
- }
-
- @Test
- fun testOnPackageAdded_takingOverPermissionGroupDeclaredBySystemApp_remainsUnchanged() {
- testTakingOverPermissionAndPermissionGroupDefinitions(oldPermissionOwnerIsSystem = true)
-
- assertWithMessage(
- "After onPackageAdded() is called when $PERMISSION_GROUP_NAME_0 already" +
- " exists in the system and is owned by a system app, app $PACKAGE_NAME_0" +
- " shouldn't takeover ownership of this permission group"
- )
- .that(getPermissionGroup(PERMISSION_GROUP_NAME_0)?.packageName)
- .isEqualTo(PACKAGE_NAME_1)
- }
-
- @Test
- fun testOnPackageAdded_newPermission_getsDeclared() {
- mutateState {
- val packageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
- addPackageState(packageState, newState)
- with(appIdPermissionPolicy) {
- onPackageAdded(packageState)
- }
- }
-
- assertWithMessage(
- "After onPackageAdded() is called when there is no existing" +
- " permissions, the new permission $PERMISSION_NAME_0 is not added"
- )
- .that(getPermission(PERMISSION_NAME_0)?.name)
- .isEqualTo(PERMISSION_NAME_0)
- }
-
- @Test
- fun testOnPackageAdded_configPermission_getsTakenOver() {
- testTakingOverPermissionAndPermissionGroupDefinitions(
- oldPermissionOwnerIsSystem = true,
- newPermissionOwnerIsSystem = true,
- type = Permission.TYPE_CONFIG,
- isReconciled = false
- )
-
- assertWithMessage(
- "After onPackageAdded() is called for a config permission with" +
- " no owner, the ownership is not taken over by a system app $PACKAGE_NAME_0"
- )
- .that(getPermission(PERMISSION_NAME_0)?.packageName)
- .isEqualTo(PACKAGE_NAME_0)
- }
-
- @Test
- fun testOnPackageAdded_systemAppTakingOverPermissionDefinition_getsTakenOver() {
- testTakingOverPermissionAndPermissionGroupDefinitions(newPermissionOwnerIsSystem = true)
-
- assertWithMessage(
- "After onPackageAdded() is called when $PERMISSION_NAME_0 already" +
- " exists in the system, the system app $PACKAGE_NAME_0 didn't takeover ownership" +
- " of this permission"
- )
- .that(getPermission(PERMISSION_NAME_0)?.packageName)
- .isEqualTo(PACKAGE_NAME_0)
- }
-
- @Test
- fun testOnPackageAdded_nonSystemAppTakingOverPermissionDefinition_remainsUnchanged() {
- testTakingOverPermissionAndPermissionGroupDefinitions()
-
- assertWithMessage(
- "After onPackageAdded() is called when $PERMISSION_NAME_0 already" +
- " exists in the system, the non-system app $PACKAGE_NAME_0 shouldn't takeover" +
- " ownership of this permission"
- )
- .that(getPermission(PERMISSION_NAME_0)?.packageName)
- .isEqualTo(PACKAGE_NAME_1)
- }
-
- @Test
- fun testOnPackageAdded_takingOverPermissionDeclaredBySystemApp_remainsUnchanged() {
- testTakingOverPermissionAndPermissionGroupDefinitions(oldPermissionOwnerIsSystem = true)
-
- assertWithMessage(
- "After onPackageAdded() is called when $PERMISSION_NAME_0 already" +
- " exists in system and is owned by a system app, the $PACKAGE_NAME_0 shouldn't" +
- " takeover ownership of this permission"
- )
- .that(getPermission(PERMISSION_NAME_0)?.packageName)
- .isEqualTo(PACKAGE_NAME_1)
- }
-
- private fun testTakingOverPermissionAndPermissionGroupDefinitions(
- oldPermissionOwnerIsSystem: Boolean = false,
- newPermissionOwnerIsSystem: Boolean = false,
- newPermissionOwnerIsInstant: Boolean = false,
- permissionGroupAlreadyExists: Boolean = true,
- permissionAlreadyExists: Boolean = true,
- type: Int = Permission.TYPE_MANIFEST,
- isReconciled: Boolean = true,
- ) {
- val oldPermissionOwnerPackageState = mockPackageState(
- APP_ID_1,
- PACKAGE_NAME_1,
- isSystem = oldPermissionOwnerIsSystem
- )
- addPackageState(oldPermissionOwnerPackageState)
- if (permissionGroupAlreadyExists) {
- addPermissionGroup(mockParsedPermissionGroup(PERMISSION_GROUP_NAME_0, PACKAGE_NAME_1))
- }
- if (permissionAlreadyExists) {
- addPermission(
- mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_1),
- type = type,
- isReconciled = isReconciled
- )
- }
-
- mutateState {
- val newPermissionOwnerPackageState = mockPackageState(
- APP_ID_0,
- mockSimpleAndroidPackage(),
- isSystem = newPermissionOwnerIsSystem,
- isInstantApp = newPermissionOwnerIsInstant
- )
- addPackageState(newPermissionOwnerPackageState, newState)
- with(appIdPermissionPolicy) {
- onPackageAdded(newPermissionOwnerPackageState)
- }
- }
- }
-
- @Test
- fun testOnPackageAdded_permissionGroupChanged_getsRevoked() {
- testPermissionChanged(
- oldPermissionGroup = PERMISSION_GROUP_NAME_1,
- newPermissionGroup = PERMISSION_GROUP_NAME_0
- )
-
- val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = 0
- assertWithMessage(
- "After onPackageAdded() is called for a package that has a permission group change" +
- " for a permission it defines, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_protectionLevelChanged_getsRevoked() {
- testPermissionChanged(newProtectionLevel = PermissionInfo.PROTECTION_INTERNAL)
-
- val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = 0
- assertWithMessage(
- "After onPackageAdded() is called for a package that has a protection level change" +
- " for a permission it defines, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- private fun testPermissionChanged(
- oldPermissionGroup: String? = null,
- newPermissionGroup: String? = null,
- newProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS
- ) {
- val oldPermission = mockParsedPermission(
- PERMISSION_NAME_0,
- PACKAGE_NAME_0,
- group = oldPermissionGroup,
- protectionLevel = PermissionInfo.PROTECTION_DANGEROUS
- )
- val oldPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(oldPermission))
- )
- addPackageState(oldPackageState)
- addPermission(oldPermission)
- setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED)
-
- mutateState {
- val newPermission = mockParsedPermission(
- PERMISSION_NAME_0,
- PACKAGE_NAME_0,
- group = newPermissionGroup,
- protectionLevel = newProtectionLevel
- )
- val newPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(newPermission))
- )
- addPackageState(newPackageState, newState)
- with(appIdPermissionPolicy) {
- onPackageAdded(newPackageState)
- }
- }
- }
-
- @Test
- fun testOnPackageAdded_permissionTreeNoLongerDeclared_getsDefinitionRemoved() {
- testPermissionDeclaration {}
-
- assertWithMessage(
- "After onPackageAdded() is called for a package that no longer defines a permission" +
- " tree, the permission tree: $PERMISSION_NAME_0 in system state should be removed"
- )
- .that(getPermissionTree(PERMISSION_NAME_0))
- .isNull()
- }
-
- @Test
- fun testOnPackageAdded_permissionTreeByDisabledSystemPackage_remainsUnchanged() {
- testPermissionDeclaration {
- val disabledSystemPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
- addDisabledSystemPackageState(disabledSystemPackageState)
- }
-
- assertWithMessage(
- "After onPackageAdded() is called for a package that no longer defines" +
- " a permission tree while this permission tree is still defined by" +
- " a disabled system package, the permission tree: $PERMISSION_NAME_0 in" +
- " system state should not be removed"
- )
- .that(getPermissionTree(PERMISSION_TREE_NAME))
- .isNotNull()
- }
-
- @Test
- fun testOnPackageAdded_permissionNoLongerDeclared_getsDefinitionRemoved() {
- testPermissionDeclaration {}
-
- assertWithMessage(
- "After onPackageAdded() is called for a package that no longer defines a permission," +
- " the permission: $PERMISSION_NAME_0 in system state should be removed"
- )
- .that(getPermission(PERMISSION_NAME_0))
- .isNull()
- }
-
- @Test
- fun testOnPackageAdded_permissionByDisabledSystemPackage_remainsUnchanged() {
- testPermissionDeclaration {
- val disabledSystemPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
- addDisabledSystemPackageState(disabledSystemPackageState)
- }
-
- assertWithMessage(
- "After onPackageAdded() is called for a disabled system package and it's updated apk" +
- " no longer defines a permission, the permission: $PERMISSION_NAME_0 in" +
- " system state should not be removed"
- )
- .that(getPermission(PERMISSION_NAME_0))
- .isNotNull()
- }
-
- private fun testPermissionDeclaration(additionalSetup: () -> Unit) {
- val oldPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
- addPackageState(oldPackageState)
- addPermission(defaultPermissionTree)
- addPermission(defaultPermission)
-
- additionalSetup()
-
- mutateState {
- val newPackageState = mockPackageState(APP_ID_0, mockAndroidPackage(PACKAGE_NAME_0))
- addPackageState(newPackageState, newState)
- with(appIdPermissionPolicy) {
- onPackageAdded(newPackageState)
- }
- }
- }
-
- @Test
- fun testOnPackageAdded_permissionsNoLongerRequested_getsFlagsRevoked() {
- val parsedPermission = mockParsedPermission(
- PERMISSION_NAME_0,
- PACKAGE_NAME_0,
- protectionLevel = PermissionInfo.PROTECTION_DANGEROUS
- )
- val oldPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(
- PACKAGE_NAME_0,
- permissions = listOf(parsedPermission),
- requestedPermissions = setOf(PERMISSION_NAME_0)
- )
- )
- addPackageState(oldPackageState)
- addPermission(parsedPermission)
- setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.RUNTIME_GRANTED)
-
- mutateState {
- val newPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
- addPackageState(newPackageState, newState)
- with(appIdPermissionPolicy) {
- onPackageAdded(newPackageState)
- }
- }
-
- val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = 0
- assertWithMessage(
- "After onPackageAdded() is called for a package that no longer requests a permission" +
- " the actual permission flags $actualFlags should match the" +
- " expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_storageAndMediaPermissionsDowngradingPastQ_getsRuntimeRevoked() {
- testRevokePermissionsOnPackageUpdate(
- PermissionFlags.RUNTIME_GRANTED,
- newTargetSdkVersion = Build.VERSION_CODES.P
- )
-
- val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
- val expectedNewFlags = 0
- assertWithMessage(
- "After onPackageAdded() is called for a package that's downgrading past Q" +
- " the actual permission flags $actualFlags should match the" +
- " expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_storageAndMediaPermissionsNotDowngradingPastQ_remainsUnchanged() {
- val oldFlags = PermissionFlags.RUNTIME_GRANTED
- testRevokePermissionsOnPackageUpdate(
- oldFlags,
- oldTargetSdkVersion = Build.VERSION_CODES.P,
- newTargetSdkVersion = Build.VERSION_CODES.P
- )
-
- val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
- val expectedNewFlags = oldFlags
- assertWithMessage(
- "After onPackageAdded() is called for a package that's not downgrading past Q" +
- " the actual permission flags $actualFlags should match the" +
- " expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_policyFixedDowngradingPastQ_remainsUnchanged() {
- val oldFlags = PermissionFlags.RUNTIME_GRANTED and PermissionFlags.POLICY_FIXED
- testRevokePermissionsOnPackageUpdate(oldFlags, newTargetSdkVersion = Build.VERSION_CODES.P)
-
- val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
- val expectedNewFlags = oldFlags
- assertWithMessage(
- "After onPackageAdded() is called for a package that's downgrading past Q" +
- " the actual permission flags with PermissionFlags.POLICY_FIXED $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_newlyRequestingLegacyExternalStorage_getsRuntimeRevoked() {
- testRevokePermissionsOnPackageUpdate(
- PermissionFlags.RUNTIME_GRANTED,
- oldTargetSdkVersion = Build.VERSION_CODES.P,
- newTargetSdkVersion = Build.VERSION_CODES.P,
- oldIsRequestLegacyExternalStorage = false
- )
-
- val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
- val expectedNewFlags = 0
- assertWithMessage(
- "After onPackageAdded() is called for a package with" +
- " newlyRequestingLegacyExternalStorage, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_missingOldPackage_remainsUnchanged() {
- val oldFlags = PermissionFlags.RUNTIME_GRANTED
- testRevokePermissionsOnPackageUpdate(
- oldFlags,
- newTargetSdkVersion = Build.VERSION_CODES.P,
- isOldPackageMissing = true
- )
-
- val actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE)
- val expectedNewFlags = oldFlags
- assertWithMessage(
- "After onPackageAdded() is called for a package that's downgrading past Q" +
- " and doesn't have the oldPackage, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- private fun testRevokePermissionsOnPackageUpdate(
- oldFlags: Int,
- oldTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
- newTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
- oldIsRequestLegacyExternalStorage: Boolean = true,
- newIsRequestLegacyExternalStorage: Boolean = true,
- isOldPackageMissing: Boolean = false
- ) {
- val parsedPermission = mockParsedPermission(
- PERMISSION_READ_EXTERNAL_STORAGE,
- PACKAGE_NAME_0,
- protectionLevel = PermissionInfo.PROTECTION_DANGEROUS
- )
- val oldPackageState = if (isOldPackageMissing) {
- mockPackageState(APP_ID_0, PACKAGE_NAME_0)
- } else {
- mockPackageState(
- APP_ID_0,
- mockAndroidPackage(
- PACKAGE_NAME_0,
- targetSdkVersion = oldTargetSdkVersion,
- isRequestLegacyExternalStorage = oldIsRequestLegacyExternalStorage,
- requestedPermissions = setOf(PERMISSION_READ_EXTERNAL_STORAGE),
- permissions = listOf(parsedPermission)
- )
- )
- }
- addPackageState(oldPackageState)
- addPermission(parsedPermission)
- setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_READ_EXTERNAL_STORAGE, oldFlags)
-
- mutateState {
- val newPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(
- PACKAGE_NAME_0,
- targetSdkVersion = newTargetSdkVersion,
- isRequestLegacyExternalStorage = newIsRequestLegacyExternalStorage,
- requestedPermissions = setOf(PERMISSION_READ_EXTERNAL_STORAGE),
- permissions = listOf(parsedPermission)
- )
- )
- addPackageState(newPackageState, newState)
- with(appIdPermissionPolicy) {
- onPackageAdded(newPackageState)
- }
- }
- }
-
- @Test
- fun testOnPackageAdded_normalPermissionAlreadyGranted_remainsUnchanged() {
- val oldFlags = PermissionFlags.INSTALL_GRANTED or PermissionFlags.INSTALL_REVOKED
- testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_NORMAL) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = oldFlags
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a normal permission" +
- " with an existing INSTALL_GRANTED flag, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_normalPermissionNotInstallRevoked_getsGranted() {
- val oldFlags = 0
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_NORMAL,
- isNewInstall = true
- ) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a normal permission" +
- " with no existing flags, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_normalPermissionRequestedByInstalledPackage_getsGranted() {
- val oldFlags = PermissionFlags.INSTALL_REVOKED
- testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_NORMAL) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a normal permission" +
- " with the INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags since it's a new install"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- /**
- * We setup a permission protection level change from SIGNATURE to NORMAL in order to make
- * the permission a "changed permission" in order to test evaluatePermissionState() called by
- * evaluatePermissionStateForAllPackages(). This makes the requestingPackageState not the
- * installedPackageState so that we can test whether requesting by system package will give us
- * the expected permission flags.
- *
- * Besides, this also helps us test evaluatePermissionStateForAllPackages(). Since both
- * evaluatePermissionStateForAllPackages() and evaluateAllPermissionStatesForPackage() call
- * evaluatePermissionState() in their implementations, we use these tests as the only tests
- * that test evaluatePermissionStateForAllPackages()
- */
- @Test
- fun testOnPackageAdded_normalPermissionRequestedBySystemPackage_getsGranted() {
- testEvaluateNormalPermissionStateWithPermissionChanges(requestingPackageIsSystem = true)
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a system package that requests a normal" +
- " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_normalCompatibilityPermission_getsGranted() {
- testEvaluateNormalPermissionStateWithPermissionChanges(
- permissionName = PERMISSION_POST_NOTIFICATIONS,
- requestingPackageTargetSdkVersion = Build.VERSION_CODES.S
- )
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_POST_NOTIFICATIONS)
- val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a normal compatibility" +
- " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_normalPermissionPreviouslyRevoked_getsInstallRevoked() {
- testEvaluateNormalPermissionStateWithPermissionChanges()
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.INSTALL_REVOKED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a normal" +
- " permission with INSTALL_REVOKED flag, the actual permission flags $actualFlags" +
- " should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- private fun testEvaluateNormalPermissionStateWithPermissionChanges(
- permissionName: String = PERMISSION_NAME_0,
- requestingPackageTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
- requestingPackageIsSystem: Boolean = false
- ) {
- val oldParsedPermission = mockParsedPermission(
- permissionName,
- PACKAGE_NAME_0,
- protectionLevel = PermissionInfo.PROTECTION_SIGNATURE
- )
- val oldPermissionOwnerPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(oldParsedPermission))
- )
- val requestingPackageState = mockPackageState(
- APP_ID_1,
- mockAndroidPackage(
- PACKAGE_NAME_1,
- requestedPermissions = setOf(permissionName),
- targetSdkVersion = requestingPackageTargetSdkVersion
- ),
- isSystem = requestingPackageIsSystem,
- )
- addPackageState(oldPermissionOwnerPackageState)
- addPackageState(requestingPackageState)
- addPermission(oldParsedPermission)
- val oldFlags = PermissionFlags.INSTALL_REVOKED
- setPermissionFlags(APP_ID_1, USER_ID_0, permissionName, oldFlags)
-
- mutateState {
- val newPermissionOwnerPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(
- PACKAGE_NAME_0,
- permissions = listOf(mockParsedPermission(permissionName, PACKAGE_NAME_0))
- )
- )
- addPackageState(newPermissionOwnerPackageState, newState)
- with(appIdPermissionPolicy) {
- onPackageAdded(newPermissionOwnerPackageState)
- }
- }
- }
-
- @Test
- fun testOnPackageAdded_normalAppOpPermission_getsRoleAndUserSetFlagsPreserved() {
- val oldFlags = PermissionFlags.ROLE or PermissionFlags.USER_SET
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_NORMAL or PermissionInfo.PROTECTION_FLAG_APPOP
- ) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.INSTALL_GRANTED or oldFlags
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a normal app op" +
- " permission with existing ROLE and USER_SET flags, the actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_internalPermissionWasGrantedWithMissingPackage_getsProtectionGranted() {
- val oldFlags = PermissionFlags.PROTECTION_GRANTED
- testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_INTERNAL) {
- val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
- addPackageState(packageStateWithMissingPackage)
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = oldFlags
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests an internal permission" +
- " with missing android package and $oldFlags flag, the actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_internalAppOpPermission_getsRoleAndUserSetFlagsPreserved() {
- val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or
- PermissionFlags.USER_SET
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_APPOP
- ) {
- val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
- addPackageState(packageStateWithMissingPackage)
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = oldFlags
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests an internal permission" +
- " with missing android package and $oldFlags flag and the permission isAppOp," +
- " the actual permission flags $actualFlags should match the expected" +
- " flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_internalDevelopmentPermission_getsRuntimeGrantedPreserved() {
- val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_DEVELOPMENT
- ) {
- val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
- addPackageState(packageStateWithMissingPackage)
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = oldFlags
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests an internal permission" +
- " with missing android package and $oldFlags flag and permission isDevelopment," +
- " the actual permission flags $actualFlags should match the expected" +
- " flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_internalRolePermission_getsRoleAndRuntimeGrantedPreserved() {
- val oldFlags = PermissionFlags.PROTECTION_GRANTED or PermissionFlags.ROLE or
- PermissionFlags.RUNTIME_GRANTED
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_INTERNAL or PermissionInfo.PROTECTION_FLAG_ROLE
- ) {
- val packageStateWithMissingPackage = mockPackageState(APP_ID_1, MISSING_ANDROID_PACKAGE)
- addPackageState(packageStateWithMissingPackage)
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = oldFlags
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests an internal permission" +
- " with missing android package and $oldFlags flag and the permission isRole," +
- " the actual permission flags $actualFlags should match the expected" +
- " flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_signaturePrivilegedPermissionNotAllowlisted_isNotGranted() {
- val oldFlags = 0
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_SIGNATURE or PermissionInfo.PROTECTION_FLAG_PRIVILEGED,
- isInstalledPackageSystem = true,
- isInstalledPackagePrivileged = true,
- isInstalledPackageProduct = true,
- // To mock the return value of shouldGrantPrivilegedOrOemPermission()
- isInstalledPackageVendor = true,
- isNewInstall = true
- ) {
- val platformPackage = mockPackageState(
- PLATFORM_APP_ID,
- mockAndroidPackage(PLATFORM_PACKAGE_NAME)
- )
- setupAllowlist(PACKAGE_NAME_1, false)
- addPackageState(platformPackage)
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = oldFlags
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a signature privileged" +
- " permission that's not allowlisted, the actual permission" +
- " flags $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_nonPrivilegedPermissionShouldGrantBySignature_getsProtectionGranted() {
- val oldFlags = 0
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_SIGNATURE,
- isInstalledPackageSystem = true,
- isInstalledPackagePrivileged = true,
- isInstalledPackageProduct = true,
- isInstalledPackageSignatureMatching = true,
- isInstalledPackageVendor = true,
- isNewInstall = true
- ) {
- val platformPackage = mockPackageState(
- PLATFORM_APP_ID,
- mockAndroidPackage(PLATFORM_PACKAGE_NAME, isSignatureMatching = true)
- )
- setupAllowlist(PACKAGE_NAME_1, false)
- addPackageState(platformPackage)
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a signature" +
- " non-privileged permission, the actual permission" +
- " flags $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_privilegedAllowlistPermissionShouldGrantByProtectionFlags_getsGranted() {
- val oldFlags = 0
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_SIGNATURE or PermissionInfo.PROTECTION_FLAG_PRIVILEGED,
- isInstalledPackageSystem = true,
- isInstalledPackagePrivileged = true,
- isInstalledPackageProduct = true,
- isNewInstall = true
- ) {
- val platformPackage = mockPackageState(
- PLATFORM_APP_ID,
- mockAndroidPackage(PLATFORM_PACKAGE_NAME)
- )
- setupAllowlist(PACKAGE_NAME_1, true)
- addPackageState(platformPackage)
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.PROTECTION_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a signature privileged" +
- " permission that's allowlisted and should grant by protection flags, the actual" +
- " permission flags $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- private fun setupAllowlist(
- packageName: String,
- allowlistState: Boolean,
- state: MutableAccessState = oldState
- ) {
- state.mutateExternalState().setPrivilegedPermissionAllowlistPackages(
- MutableIndexedListSet<String>().apply { add(packageName) }
- )
- val mockAllowlist = mock<PermissionAllowlist> {
- whenever(
- getProductPrivilegedAppAllowlistState(packageName, PERMISSION_NAME_0)
- ).thenReturn(allowlistState)
- }
- state.mutateExternalState().setPermissionAllowlist(mockAllowlist)
- }
-
- @Test
- fun testOnPackageAdded_nonRuntimeFlagsOnRuntimePermissions_getsCleared() {
- val oldFlags = PermissionFlags.INSTALL_GRANTED or PermissionFlags.PREGRANT or
- PermissionFlags.RUNTIME_GRANTED
- testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.PREGRANT or PermissionFlags.RUNTIME_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime permission" +
- " with existing $oldFlags flags, the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_newPermissionsForPreM_requiresUserReview() {
- val oldFlags = 0
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_DANGEROUS,
- installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP,
- isNewInstall = true
- ) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime permission" +
- " with no existing flags in pre M, actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_legacyOrImplicitGrantedPermissionPreviouslyRevoked_getsAppOpRevoked() {
- val oldFlags = PermissionFlags.USER_FIXED
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_DANGEROUS,
- installedPackageTargetSdkVersion = Build.VERSION_CODES.LOLLIPOP
- ) {
- setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.USER_FIXED or
- PermissionFlags.APP_OP_REVOKED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime permission" +
- " that should be LEGACY_GRANTED or IMPLICIT_GRANTED that was previously revoked," +
- " the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_legacyGrantedPermissionsForPostM_userReviewRequirementRemoved() {
- val oldFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT
- testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = 0
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime permission" +
- " that used to require user review, the user review requirement should be removed" +
- " if it's upgraded to post M. The actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_legacyGrantedPermissionsAlreadyReviewedForPostM_getsGranted() {
- val oldFlags = PermissionFlags.LEGACY_GRANTED
- testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime permission" +
- " that was already reviewed by the user, the permission should be RUNTIME_GRANTED" +
- " if it's upgraded to post M. The actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_leanbackNotificationPermissionsForPostM_getsImplicitGranted() {
- val oldFlags = 0
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_DANGEROUS,
- permissionName = PERMISSION_POST_NOTIFICATIONS,
- isNewInstall = true
- ) {
- oldState.mutateExternalState().setLeanback(true)
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_POST_NOTIFICATIONS)
- val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime notification" +
- " permission when isLeanback, the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_implicitSourceFromNonRuntime_getsImplicitGranted() {
- val oldFlags = 0
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_DANGEROUS,
- implicitPermissions = setOf(PERMISSION_NAME_0),
- isNewInstall = true
- ) {
- oldState.mutateExternalState().setImplicitToSourcePermissions(
- MutableIndexedMap<String, IndexedListSet<String>>().apply {
- put(PERMISSION_NAME_0, MutableIndexedListSet<String>().apply {
- add(PERMISSION_NAME_1)
- })
- }
- )
- addPermission(mockParsedPermission(PERMISSION_NAME_1, PACKAGE_NAME_0))
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime implicit" +
- " permission that's source from a non-runtime permission, the actual permission" +
- " flags $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- /**
- * For a legacy granted or implicit permission during the app upgrade, when the permission
- * should no longer be legacy or implicit granted, we want to remove the APP_OP_REVOKED flag
- * so that the app can request the permission.
- */
- @Test
- fun testOnPackageAdded_noLongerLegacyOrImplicitGranted_canBeRequested() {
- val oldFlags = PermissionFlags.LEGACY_GRANTED or PermissionFlags.APP_OP_REVOKED or
- PermissionFlags.RUNTIME_GRANTED
- testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = 0
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime permission" +
- " that is no longer LEGACY_GRANTED or IMPLICIT_GRANTED, the actual permission" +
- " flags $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_noLongerImplicitPermissions_getsRuntimeAndImplicitFlagsRemoved() {
- val oldFlags = PermissionFlags.IMPLICIT or PermissionFlags.RUNTIME_GRANTED or
- PermissionFlags.USER_SET or PermissionFlags.USER_FIXED
- testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = 0
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime permission" +
- " that is no longer implicit and we shouldn't retain as nearby device" +
- " permissions, the actual permission flags $actualFlags should match the expected" +
- " flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_noLongerImplicitNearbyPermissionsWasGranted_getsRuntimeGranted() {
- val oldFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_DANGEROUS,
- permissionName = PERMISSION_BLUETOOTH_CONNECT,
- requestedPermissions = setOf(
- PERMISSION_BLUETOOTH_CONNECT,
- PERMISSION_ACCESS_BACKGROUND_LOCATION
- )
- ) {
- setPermissionFlags(
- APP_ID_1,
- USER_ID_0,
- PERMISSION_ACCESS_BACKGROUND_LOCATION,
- PermissionFlags.RUNTIME_GRANTED
- )
- }
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_BLUETOOTH_CONNECT)
- val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime nearby device" +
- " permission that was granted by implicit, the actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_noLongerImplicitSystemOrPolicyFixedWasGranted_getsRuntimeGranted() {
- val oldFlags = PermissionFlags.IMPLICIT_GRANTED or PermissionFlags.IMPLICIT or
- PermissionFlags.SYSTEM_FIXED
- testEvaluatePermissionState(oldFlags, PermissionInfo.PROTECTION_DANGEROUS) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.SYSTEM_FIXED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime permission" +
- " that was granted and is no longer implicit and is SYSTEM_FIXED or POLICY_FIXED," +
- " the actual permission flags $actualFlags should match the expected" +
- " flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_restrictedPermissionsNotExempt_getsRestrictionFlags() {
- val oldFlags = PermissionFlags.RESTRICTION_REVOKED
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_DANGEROUS,
- permissionInfoFlags = PermissionInfo.FLAG_HARD_RESTRICTED
- ) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = oldFlags
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime hard" +
- " restricted permission that is not exempted, the actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
- val oldFlags = 0
- testEvaluatePermissionState(
- oldFlags,
- PermissionInfo.PROTECTION_DANGEROUS,
- permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
- ) {}
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.UPGRADE_EXEMPT
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a runtime soft" +
- " restricted permission that is exempted, the actual permission flags" +
- " $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_runtimeExistingImplicitPermissions_sourceFlagsNotInherited() {
- val oldImplicitPermissionFlags = PermissionFlags.USER_FIXED
- testInheritImplicitPermissionStates(
- implicitPermissionFlags = oldImplicitPermissionFlags,
- isNewInstallAndNewPermission = false
- )
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = oldImplicitPermissionFlags or PermissionFlags.IMPLICIT_GRANTED or
- PermissionFlags.APP_OP_REVOKED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a permission that is" +
- " implicit, existing and runtime, it should not inherit the runtime flags from" +
- " the source permission. Hence the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_nonRuntimeNewImplicitPermissions_sourceFlagsNotInherited() {
- testInheritImplicitPermissionStates(
- implicitPermissionProtectionLevel = PermissionInfo.PROTECTION_NORMAL
- )
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = PermissionFlags.INSTALL_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a permission that is" +
- " implicit, new and non-runtime, it should not inherit the runtime flags from" +
- " the source permission. Hence the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_runtimeNewImplicitPermissions_sourceFlagsInherited() {
- val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
- testInheritImplicitPermissionStates(sourceRuntimeFlags = sourceRuntimeFlags)
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED or
- PermissionFlags.IMPLICIT
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a permission that is" +
- " implicit, new and runtime, it should inherit the runtime flags from" +
- " the source permission. Hence the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- @Test
- fun testOnPackageAdded_grantingNewFromRevokeImplicitPermissions_onlySourceFlagsInherited() {
- val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
- testInheritImplicitPermissionStates(
- implicitPermissionFlags = PermissionFlags.POLICY_FIXED,
- sourceRuntimeFlags = sourceRuntimeFlags,
- isAnySourcePermissionNonRuntime = false
- )
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
- val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a permission that is" +
- " implicit, existing, runtime and revoked, it should only inherit runtime flags" +
- " from source permission. Hence the actual permission flags $actualFlags should" +
- " match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- /**
- * If it's a media implicit permission (one of RETAIN_IMPLICIT_FLAGS_PERMISSIONS), we want to
- * remove the IMPLICIT flag so that they will be granted when they are no longer implicit.
- * (instead of revoking it)
- */
- @Test
- fun testOnPackageAdded_mediaImplicitPermissions_getsImplicitFlagRemoved() {
- val sourceRuntimeFlags = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET
- testInheritImplicitPermissionStates(
- implicitPermissionName = PERMISSION_ACCESS_MEDIA_LOCATION,
- sourceRuntimeFlags = sourceRuntimeFlags
- )
-
- val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_ACCESS_MEDIA_LOCATION)
- val expectedNewFlags = sourceRuntimeFlags or PermissionFlags.IMPLICIT_GRANTED
- assertWithMessage(
- "After onPackageAdded() is called for a package that requests a media permission that" +
- " is implicit, new and runtime, it should inherit the runtime flags from" +
- " the source permission and have the IMPLICIT flag removed. Hence the actual" +
- " permission flags $actualFlags should match the expected flags $expectedNewFlags"
- )
- .that(actualFlags)
- .isEqualTo(expectedNewFlags)
- }
-
- private fun testInheritImplicitPermissionStates(
- implicitPermissionName: String = PERMISSION_NAME_0,
- implicitPermissionFlags: Int = 0,
- implicitPermissionProtectionLevel: Int = PermissionInfo.PROTECTION_DANGEROUS,
- sourceRuntimeFlags: Int = PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET,
- isAnySourcePermissionNonRuntime: Boolean = true,
- isNewInstallAndNewPermission: Boolean = true
- ) {
- val implicitPermission = mockParsedPermission(
- implicitPermissionName,
- PACKAGE_NAME_0,
- protectionLevel = implicitPermissionProtectionLevel,
- )
- // For source from non-runtime in order to grant by implicit
- val sourcePermission1 = mockParsedPermission(
- PERMISSION_NAME_1,
- PACKAGE_NAME_0,
- protectionLevel = if (isAnySourcePermissionNonRuntime) {
- PermissionInfo.PROTECTION_NORMAL
- } else {
- PermissionInfo.PROTECTION_DANGEROUS
- }
- )
- // For inheriting runtime flags
- val sourcePermission2 = mockParsedPermission(
- PERMISSION_NAME_2,
- PACKAGE_NAME_0,
- protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
- )
- val permissionOwnerPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(
- PACKAGE_NAME_0,
- permissions = listOf(implicitPermission, sourcePermission1, sourcePermission2)
- )
- )
- val installedPackageState = mockPackageState(
- APP_ID_1,
- mockAndroidPackage(
- PACKAGE_NAME_1,
- requestedPermissions = setOf(
- implicitPermissionName,
- PERMISSION_NAME_1,
- PERMISSION_NAME_2
- ),
- implicitPermissions = setOf(implicitPermissionName)
- )
- )
- oldState.mutateExternalState().setImplicitToSourcePermissions(
- MutableIndexedMap<String, IndexedListSet<String>>().apply {
- put(implicitPermissionName, MutableIndexedListSet<String>().apply {
- add(PERMISSION_NAME_1)
- add(PERMISSION_NAME_2)
- })
- }
- )
- addPackageState(permissionOwnerPackageState)
- addPermission(implicitPermission)
- addPermission(sourcePermission1)
- addPermission(sourcePermission2)
- if (!isNewInstallAndNewPermission) {
- addPackageState(installedPackageState)
- setPermissionFlags(APP_ID_1, USER_ID_0, implicitPermissionName, implicitPermissionFlags)
- }
- setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_2, sourceRuntimeFlags)
-
- mutateState {
- if (isNewInstallAndNewPermission) {
- addPackageState(installedPackageState)
- setPermissionFlags(
- APP_ID_1,
- USER_ID_0,
- implicitPermissionName,
- implicitPermissionFlags,
- newState
- )
- }
- with(appIdPermissionPolicy) {
- onPackageAdded(installedPackageState)
- }
- }
- }
-
- /**
- * Setup simple package states for testing evaluatePermissionState().
- * permissionOwnerPackageState is definer of permissionName with APP_ID_0.
- * installedPackageState is the installed package that requests permissionName with APP_ID_1.
- *
- * @param oldFlags the existing permission flags for APP_ID_1, USER_ID_0, permissionName
- * @param protectionLevel the protectionLevel for the permission
- * @param permissionName the name of the permission (1) being defined (2) of the oldFlags, and
- * (3) requested by installedPackageState
- * @param requestedPermissions the permissions requested by installedPackageState
- * @param implicitPermissions the implicit permissions of installedPackageState
- * @param permissionInfoFlags the flags for the permission itself
- * @param isInstalledPackageSystem whether installedPackageState is a system package
- *
- * @return installedPackageState
- */
- fun testEvaluatePermissionState(
- oldFlags: Int,
- protectionLevel: Int,
- permissionName: String = PERMISSION_NAME_0,
- requestedPermissions: Set<String> = setOf(permissionName),
- implicitPermissions: Set<String> = emptySet(),
- permissionInfoFlags: Int = 0,
- isInstalledPackageSystem: Boolean = false,
- isInstalledPackagePrivileged: Boolean = false,
- isInstalledPackageProduct: Boolean = false,
- isInstalledPackageSignatureMatching: Boolean = false,
- isInstalledPackageVendor: Boolean = false,
- installedPackageTargetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
- isNewInstall: Boolean = false,
- additionalSetup: () -> Unit
- ) {
- val parsedPermission = mockParsedPermission(
- permissionName,
- PACKAGE_NAME_0,
- protectionLevel = protectionLevel,
- flags = permissionInfoFlags
- )
- val permissionOwnerPackageState = mockPackageState(
- APP_ID_0,
- mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
- )
- val installedPackageState = mockPackageState(
- APP_ID_1,
- mockAndroidPackage(
- PACKAGE_NAME_1,
- requestedPermissions = requestedPermissions,
- implicitPermissions = implicitPermissions,
- targetSdkVersion = installedPackageTargetSdkVersion,
- isSignatureMatching = isInstalledPackageSignatureMatching
- ),
- isSystem = isInstalledPackageSystem,
- isPrivileged = isInstalledPackagePrivileged,
- isProduct = isInstalledPackageProduct,
- isVendor = isInstalledPackageVendor
- )
- addPackageState(permissionOwnerPackageState)
- if (!isNewInstall) {
- addPackageState(installedPackageState)
- setPermissionFlags(APP_ID_1, USER_ID_0, permissionName, oldFlags)
- }
- addPermission(parsedPermission)
-
- additionalSetup()
-
- mutateState {
- if (isNewInstall) {
- addPackageState(installedPackageState, newState)
- setPermissionFlags(APP_ID_1, USER_ID_0, permissionName, oldFlags, newState)
- }
- with(appIdPermissionPolicy) {
- onPackageAdded(installedPackageState)
- }
- }
- }
-
- /**
- * Mock an AndroidPackage with PACKAGE_NAME_0, PERMISSION_NAME_0 and PERMISSION_GROUP_NAME_0
- */
- private fun mockSimpleAndroidPackage(): AndroidPackage =
- mockAndroidPackage(
- PACKAGE_NAME_0,
- permissionGroups = listOf(defaultPermissionGroup),
- permissions = listOf(defaultPermissionTree, defaultPermission)
- )
-
- private inline fun mutateState(action: MutateStateScope.() -> Unit) {
- newState = oldState.toMutable()
- MutateStateScope(oldState, newState).action()
- }
-
- private fun mockPackageState(
- appId: Int,
- packageName: String,
- isSystem: Boolean = false,
- ): PackageState =
- mock {
- whenever(this.appId).thenReturn(appId)
- whenever(this.packageName).thenReturn(packageName)
- whenever(androidPackage).thenReturn(null)
- whenever(this.isSystem).thenReturn(isSystem)
- }
-
- private fun mockPackageState(
- appId: Int,
- androidPackage: AndroidPackage,
- isSystem: Boolean = false,
- isPrivileged: Boolean = false,
- isProduct: Boolean = false,
- isInstantApp: Boolean = false,
- isVendor: Boolean = false
- ): PackageState =
- mock {
- whenever(this.appId).thenReturn(appId)
- whenever(this.androidPackage).thenReturn(androidPackage)
- val packageName = androidPackage.packageName
- whenever(this.packageName).thenReturn(packageName)
- whenever(this.isSystem).thenReturn(isSystem)
- whenever(this.isPrivileged).thenReturn(isPrivileged)
- whenever(this.isProduct).thenReturn(isProduct)
- whenever(this.isVendor).thenReturn(isVendor)
- val userStates = SparseArray<PackageUserState>().apply {
- put(USER_ID_0, mock { whenever(this.isInstantApp).thenReturn(isInstantApp) })
- }
- whenever(this.userStates).thenReturn(userStates)
- }
-
- private fun mockAndroidPackage(
- packageName: String,
- targetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
- isRequestLegacyExternalStorage: Boolean = false,
- adoptPermissions: List<String> = emptyList(),
- implicitPermissions: Set<String> = emptySet(),
- requestedPermissions: Set<String> = emptySet(),
- permissionGroups: List<ParsedPermissionGroup> = emptyList(),
- permissions: List<ParsedPermission> = emptyList(),
- isSignatureMatching: Boolean = false
- ): AndroidPackage =
- mock {
- whenever(this.packageName).thenReturn(packageName)
- whenever(this.targetSdkVersion).thenReturn(targetSdkVersion)
- whenever(this.isRequestLegacyExternalStorage).thenReturn(isRequestLegacyExternalStorage)
- whenever(this.adoptPermissions).thenReturn(adoptPermissions)
- whenever(this.implicitPermissions).thenReturn(implicitPermissions)
- whenever(this.requestedPermissions).thenReturn(requestedPermissions)
- whenever(this.permissionGroups).thenReturn(permissionGroups)
- whenever(this.permissions).thenReturn(permissions)
- val signingDetails = mock<SigningDetails> {
- whenever(
- hasCommonSignerWithCapability(any(), any())
- ).thenReturn(isSignatureMatching)
- whenever(hasAncestorOrSelf(any())).thenReturn(isSignatureMatching)
- whenever(
- checkCapability(any<SigningDetails>(), any())
- ).thenReturn(isSignatureMatching)
- }
- whenever(this.signingDetails).thenReturn(signingDetails)
- }
-
- private fun mockParsedPermission(
- permissionName: String,
- packageName: String,
- backgroundPermission: String? = null,
- group: String? = null,
- protectionLevel: Int = PermissionInfo.PROTECTION_NORMAL,
- flags: Int = 0,
- isTree: Boolean = false
- ): ParsedPermission =
- mock {
- whenever(name).thenReturn(permissionName)
- whenever(this.packageName).thenReturn(packageName)
- whenever(metaData).thenReturn(Bundle())
- whenever(this.backgroundPermission).thenReturn(backgroundPermission)
- whenever(this.group).thenReturn(group)
- whenever(this.protectionLevel).thenReturn(protectionLevel)
- whenever(this.flags).thenReturn(flags)
- whenever(this.isTree).thenReturn(isTree)
- }
-
- private fun mockParsedPermissionGroup(
- permissionGroupName: String,
- packageName: String,
- ): ParsedPermissionGroup =
- mock {
- whenever(name).thenReturn(permissionGroupName)
- whenever(this.packageName).thenReturn(packageName)
- whenever(metaData).thenReturn(Bundle())
- }
-
- private fun addPackageState(packageState: PackageState, state: MutableAccessState = oldState) {
- state.mutateExternalState().apply {
- setPackageStates(
- packageStates.toMutableMap().apply {
- put(packageState.packageName, packageState)
- }
- )
- mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
- .add(packageState.packageName)
- }
- }
-
- private fun addDisabledSystemPackageState(
- packageState: PackageState,
- state: MutableAccessState = oldState
- ) = state.mutateExternalState().apply {
- (disabledSystemPackageStates as ArrayMap)[packageState.packageName] = packageState
- }
-
- private fun addPermission(
- parsedPermission: ParsedPermission,
- type: Int = Permission.TYPE_MANIFEST,
- isReconciled: Boolean = true,
- state: MutableAccessState = oldState
- ) {
- val permissionInfo = PackageInfoUtils.generatePermissionInfo(
- parsedPermission,
- PackageManager.GET_META_DATA.toLong()
- )!!
- val appId = state.externalState.packageStates[permissionInfo.packageName]!!.appId
- val permission = Permission(permissionInfo, isReconciled, type, appId)
- if (parsedPermission.isTree) {
- state.mutateSystemState().mutatePermissionTrees()[permission.name] = permission
- } else {
- state.mutateSystemState().mutatePermissions()[permission.name] = permission
- }
- }
-
- private fun addPermissionGroup(
- parsedPermissionGroup: ParsedPermissionGroup,
- state: MutableAccessState = oldState
- ) {
- state.mutateSystemState().mutatePermissionGroups()[parsedPermissionGroup.name] =
- PackageInfoUtils.generatePermissionGroupInfo(
- parsedPermissionGroup,
- PackageManager.GET_META_DATA.toLong()
- )!!
- }
-
- private fun getPermission(
- permissionName: String,
- state: MutableAccessState = newState
- ): Permission? = state.systemState.permissions[permissionName]
-
- private fun getPermissionTree(
- permissionTreeName: String,
- state: MutableAccessState = newState
- ): Permission? = state.systemState.permissionTrees[permissionTreeName]
-
- private fun getPermissionGroup(
- permissionGroupName: String,
- state: MutableAccessState = newState
- ): PermissionGroupInfo? = state.systemState.permissionGroups[permissionGroupName]
-
- private fun getPermissionFlags(
- appId: Int,
- userId: Int,
- permissionName: String,
- state: MutableAccessState = newState
- ): Int =
- state.userStates[userId]?.appIdPermissionFlags?.get(appId).getWithDefault(permissionName, 0)
-
- private fun setPermissionFlags(
- appId: Int,
- userId: Int,
- permissionName: String,
- flags: Int,
- state: MutableAccessState = oldState
- ) =
- state.mutateUserState(userId)!!.mutateAppIdPermissionFlags().mutateOrPut(appId) {
- MutableIndexedMap()
- }.put(permissionName, flags)
-
- companion object {
- private const val PACKAGE_NAME_0 = "packageName0"
- private const val PACKAGE_NAME_1 = "packageName1"
- private const val MISSING_ANDROID_PACKAGE = "missingAndroidPackage"
- private const val PLATFORM_PACKAGE_NAME = "android"
-
- private const val APP_ID_0 = 0
- private const val APP_ID_1 = 1
- private const val PLATFORM_APP_ID = 2
-
- private const val PERMISSION_GROUP_NAME_0 = "permissionGroupName0"
- private const val PERMISSION_GROUP_NAME_1 = "permissionGroupName1"
-
- private const val PERMISSION_TREE_NAME = "permissionTree"
-
- private const val PERMISSION_NAME_0 = "permissionName0"
- private const val PERMISSION_NAME_1 = "permissionName1"
- private const val PERMISSION_NAME_2 = "permissionName2"
- private const val PERMISSION_READ_EXTERNAL_STORAGE =
- Manifest.permission.READ_EXTERNAL_STORAGE
- private const val PERMISSION_POST_NOTIFICATIONS =
- Manifest.permission.POST_NOTIFICATIONS
- private const val PERMISSION_BLUETOOTH_CONNECT =
- Manifest.permission.BLUETOOTH_CONNECT
- private const val PERMISSION_ACCESS_BACKGROUND_LOCATION =
- Manifest.permission.ACCESS_BACKGROUND_LOCATION
- private const val PERMISSION_ACCESS_MEDIA_LOCATION =
- Manifest.permission.ACCESS_MEDIA_LOCATION
-
- private const val USER_ID_0 = 0
- }
-}
diff --git a/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
new file mode 100644
index 0000000..7966c5c
--- /dev/null
+++ b/services/tests/PermissionServiceMockingTests/src/com/android/server/permission/test/BaseAppIdPermissionPolicyTest.kt
@@ -0,0 +1,446 @@
+/*
+ * 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.permission.test
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.content.pm.PermissionGroupInfo
+import android.content.pm.PermissionInfo
+import android.content.pm.SigningDetails
+import android.os.Build
+import android.os.Bundle
+import android.util.ArrayMap
+import android.util.SparseArray
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.server.extendedtestutils.wheneverStatic
+import com.android.server.permission.access.MutableAccessState
+import com.android.server.permission.access.MutableUserState
+import com.android.server.permission.access.MutateStateScope
+import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.permission.AppIdPermissionPolicy
+import com.android.server.permission.access.permission.Permission
+import com.android.server.permission.access.permission.PermissionFlags
+import com.android.server.permission.access.util.hasBits
+import com.android.server.pm.parsing.PackageInfoUtils
+import com.android.server.pm.pkg.AndroidPackage
+import com.android.server.pm.pkg.PackageState
+import com.android.server.pm.pkg.PackageUserState
+import com.android.server.pm.pkg.component.ParsedPermission
+import com.android.server.pm.pkg.component.ParsedPermissionGroup
+import com.android.server.testutils.any
+import com.android.server.testutils.mock
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyLong
+
+/**
+ * Mocking unit test for AppIdPermissionPolicy.
+ */
+@RunWith(AndroidJUnit4::class)
+open class BaseAppIdPermissionPolicyTest {
+ protected lateinit var oldState: MutableAccessState
+ protected lateinit var newState: MutableAccessState
+
+ protected val defaultPermissionGroup = mockParsedPermissionGroup(
+ PERMISSION_GROUP_NAME_0,
+ PACKAGE_NAME_0
+ )
+ protected val defaultPermissionTree = mockParsedPermission(
+ PERMISSION_TREE_NAME,
+ PACKAGE_NAME_0,
+ isTree = true
+ )
+ protected val defaultPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
+
+ protected val appIdPermissionPolicy = AppIdPermissionPolicy()
+
+ @Rule
+ @JvmField
+ val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
+ .spyStatic(PackageInfoUtils::class.java)
+ .build()
+
+ @Before
+ open fun setUp() {
+ oldState = MutableAccessState()
+ createUserState(USER_ID_0)
+ oldState.mutateExternalState().setPackageStates(ArrayMap())
+ oldState.mutateExternalState().setDisabledSystemPackageStates(ArrayMap())
+ mockPackageInfoUtilsGeneratePermissionInfo()
+ mockPackageInfoUtilsGeneratePermissionGroupInfo()
+ }
+
+ protected fun createUserState(userId: Int) {
+ oldState.mutateExternalState().mutateUserIds().add(userId)
+ oldState.mutateUserStatesNoWrite().put(userId, MutableUserState())
+ }
+
+ private fun mockPackageInfoUtilsGeneratePermissionInfo() {
+ wheneverStatic {
+ PackageInfoUtils.generatePermissionInfo(any(ParsedPermission::class.java), anyLong())
+ }.thenAnswer { invocation ->
+ val parsedPermission = invocation.getArgument<ParsedPermission>(0)
+ val generateFlags = invocation.getArgument<Long>(1)
+ PermissionInfo(parsedPermission.backgroundPermission).apply {
+ name = parsedPermission.name
+ packageName = parsedPermission.packageName
+ metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
+ parsedPermission.metaData
+ } else {
+ null
+ }
+ @Suppress("DEPRECATION")
+ protectionLevel = parsedPermission.protectionLevel
+ group = parsedPermission.group
+ flags = parsedPermission.flags
+ }
+ }
+ }
+
+ private fun mockPackageInfoUtilsGeneratePermissionGroupInfo() {
+ wheneverStatic {
+ PackageInfoUtils.generatePermissionGroupInfo(
+ any(ParsedPermissionGroup::class.java),
+ anyLong()
+ )
+ }.thenAnswer { invocation ->
+ val parsedPermissionGroup = invocation.getArgument<ParsedPermissionGroup>(0)
+ val generateFlags = invocation.getArgument<Long>(1)
+ @Suppress("DEPRECATION")
+ PermissionGroupInfo().apply {
+ name = parsedPermissionGroup.name
+ packageName = parsedPermissionGroup.packageName
+ metaData = if (generateFlags.toInt().hasBits(PackageManager.GET_META_DATA)) {
+ parsedPermissionGroup.metaData
+ } else {
+ null
+ }
+ flags = parsedPermissionGroup.flags
+ }
+ }
+ }
+
+ @Test
+ fun testOnAppIdRemoved_appIdIsRemoved_permissionFlagsCleared() {
+ val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
+ val permissionOwnerPackageState = mockPackageState(
+ APP_ID_0,
+ mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
+ )
+ val requestingPackageState = mockPackageState(
+ APP_ID_1,
+ mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
+ )
+ addPackageState(permissionOwnerPackageState)
+ addPackageState(requestingPackageState)
+ addPermission(parsedPermission)
+ setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.INSTALL_GRANTED)
+
+ mutateState {
+ with(appIdPermissionPolicy) {
+ onAppIdRemoved(APP_ID_1)
+ }
+ }
+
+ val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
+ val expectedNewFlags = 0
+ assertWithMessage(
+ "After onAppIdRemoved() is called for appId $APP_ID_1 that requests a permission" +
+ " owns by appId $APP_ID_0 with existing permission flags. The actual permission" +
+ " flags $actualFlags should be null"
+ )
+ .that(actualFlags)
+ .isEqualTo(expectedNewFlags)
+ }
+
+ @Test
+ fun testOnPackageRemoved_packageIsRemoved_permissionsAreTrimmedAndStatesAreEvaluated() {
+ // TODO
+ // shouldn't reuse test cases because it's really different despite it's also for
+ // trim permission states. It's different because it's package removal
+ }
+
+ @Test
+ fun testOnPackageInstalled_nonSystemAppIsInstalled_upgradeExemptFlagIsCleared() {
+ // TODO
+ // should be fine for it to be its own test cases and not to re-use
+ // clearRestrictedPermissionImplicitExemption
+ }
+
+ @Test
+ fun testOnPackageInstalled_systemAppIsInstalled_upgradeExemptFlagIsRetained() {
+ // TODO
+ }
+
+ @Test
+ fun testOnPackageInstalled_requestedPermissionAlsoRequestedBySystemApp_exemptFlagIsRetained() {
+ // TODO
+ }
+
+ @Test
+ fun testOnPackageInstalled_restrictedPermissionsNotExempt_getsRestrictionFlags() {
+ // TODO
+ }
+
+ @Test
+ fun testOnPackageInstalled_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
+ // TODO
+ }
+
+ @Test
+ fun testOnStateMutated_notEmpty_isCalledForEachListener() {
+ // TODO
+ }
+
+ /**
+ * Mock an AndroidPackage with PACKAGE_NAME_0, PERMISSION_NAME_0 and PERMISSION_GROUP_NAME_0
+ */
+ protected fun mockSimpleAndroidPackage(): AndroidPackage =
+ mockAndroidPackage(
+ PACKAGE_NAME_0,
+ permissionGroups = listOf(defaultPermissionGroup),
+ permissions = listOf(defaultPermissionTree, defaultPermission)
+ )
+
+ protected inline fun mutateState(action: MutateStateScope.() -> Unit) {
+ newState = oldState.toMutable()
+ MutateStateScope(oldState, newState).action()
+ }
+
+ protected fun mockPackageState(
+ appId: Int,
+ packageName: String,
+ isSystem: Boolean = false,
+ ): PackageState =
+ mock {
+ whenever(this.appId).thenReturn(appId)
+ whenever(this.packageName).thenReturn(packageName)
+ whenever(androidPackage).thenReturn(null)
+ whenever(this.isSystem).thenReturn(isSystem)
+ }
+
+ protected fun mockPackageState(
+ appId: Int,
+ androidPackage: AndroidPackage,
+ isSystem: Boolean = false,
+ isPrivileged: Boolean = false,
+ isProduct: Boolean = false,
+ isInstantApp: Boolean = false,
+ isVendor: Boolean = false
+ ): PackageState =
+ mock {
+ whenever(this.appId).thenReturn(appId)
+ whenever(this.androidPackage).thenReturn(androidPackage)
+ val packageName = androidPackage.packageName
+ whenever(this.packageName).thenReturn(packageName)
+ whenever(this.isSystem).thenReturn(isSystem)
+ whenever(this.isPrivileged).thenReturn(isPrivileged)
+ whenever(this.isProduct).thenReturn(isProduct)
+ whenever(this.isVendor).thenReturn(isVendor)
+ val userStates = SparseArray<PackageUserState>().apply {
+ put(USER_ID_0, mock { whenever(this.isInstantApp).thenReturn(isInstantApp) })
+ }
+ whenever(this.userStates).thenReturn(userStates)
+ }
+
+ protected fun mockAndroidPackage(
+ packageName: String,
+ targetSdkVersion: Int = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ isRequestLegacyExternalStorage: Boolean = false,
+ adoptPermissions: List<String> = emptyList(),
+ implicitPermissions: Set<String> = emptySet(),
+ requestedPermissions: Set<String> = emptySet(),
+ permissionGroups: List<ParsedPermissionGroup> = emptyList(),
+ permissions: List<ParsedPermission> = emptyList(),
+ isSignatureMatching: Boolean = false
+ ): AndroidPackage =
+ mock {
+ whenever(this.packageName).thenReturn(packageName)
+ whenever(this.targetSdkVersion).thenReturn(targetSdkVersion)
+ whenever(this.isRequestLegacyExternalStorage).thenReturn(isRequestLegacyExternalStorage)
+ whenever(this.adoptPermissions).thenReturn(adoptPermissions)
+ whenever(this.implicitPermissions).thenReturn(implicitPermissions)
+ whenever(this.requestedPermissions).thenReturn(requestedPermissions)
+ whenever(this.permissionGroups).thenReturn(permissionGroups)
+ whenever(this.permissions).thenReturn(permissions)
+ val signingDetails = mock<SigningDetails> {
+ whenever(
+ hasCommonSignerWithCapability(any(), any())
+ ).thenReturn(isSignatureMatching)
+ whenever(hasAncestorOrSelf(any())).thenReturn(isSignatureMatching)
+ whenever(
+ checkCapability(any<SigningDetails>(), any())
+ ).thenReturn(isSignatureMatching)
+ }
+ whenever(this.signingDetails).thenReturn(signingDetails)
+ }
+
+ protected fun mockParsedPermission(
+ permissionName: String,
+ packageName: String,
+ backgroundPermission: String? = null,
+ group: String? = null,
+ protectionLevel: Int = PermissionInfo.PROTECTION_NORMAL,
+ flags: Int = 0,
+ isTree: Boolean = false
+ ): ParsedPermission =
+ mock {
+ whenever(name).thenReturn(permissionName)
+ whenever(this.packageName).thenReturn(packageName)
+ whenever(metaData).thenReturn(Bundle())
+ whenever(this.backgroundPermission).thenReturn(backgroundPermission)
+ whenever(this.group).thenReturn(group)
+ whenever(this.protectionLevel).thenReturn(protectionLevel)
+ whenever(this.flags).thenReturn(flags)
+ whenever(this.isTree).thenReturn(isTree)
+ }
+
+ protected fun mockParsedPermissionGroup(
+ permissionGroupName: String,
+ packageName: String,
+ ): ParsedPermissionGroup =
+ mock {
+ whenever(name).thenReturn(permissionGroupName)
+ whenever(this.packageName).thenReturn(packageName)
+ whenever(metaData).thenReturn(Bundle())
+ }
+
+ protected fun addPackageState(
+ packageState: PackageState,
+ state: MutableAccessState = oldState
+ ) {
+ state.mutateExternalState().apply {
+ setPackageStates(
+ packageStates.toMutableMap().apply {
+ put(packageState.packageName, packageState)
+ }
+ )
+ mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
+ .add(packageState.packageName)
+ }
+ }
+
+ protected fun addDisabledSystemPackageState(
+ packageState: PackageState,
+ state: MutableAccessState = oldState
+ ) = state.mutateExternalState().apply {
+ (disabledSystemPackageStates as ArrayMap)[packageState.packageName] = packageState
+ }
+
+ protected fun addPermission(
+ parsedPermission: ParsedPermission,
+ type: Int = Permission.TYPE_MANIFEST,
+ isReconciled: Boolean = true,
+ state: MutableAccessState = oldState
+ ) {
+ val permissionInfo = PackageInfoUtils.generatePermissionInfo(
+ parsedPermission,
+ PackageManager.GET_META_DATA.toLong()
+ )!!
+ val appId = state.externalState.packageStates[permissionInfo.packageName]!!.appId
+ val permission = Permission(permissionInfo, isReconciled, type, appId)
+ if (parsedPermission.isTree) {
+ state.mutateSystemState().mutatePermissionTrees()[permission.name] = permission
+ } else {
+ state.mutateSystemState().mutatePermissions()[permission.name] = permission
+ }
+ }
+
+ protected fun addPermissionGroup(
+ parsedPermissionGroup: ParsedPermissionGroup,
+ state: MutableAccessState = oldState
+ ) {
+ state.mutateSystemState().mutatePermissionGroups()[parsedPermissionGroup.name] =
+ PackageInfoUtils.generatePermissionGroupInfo(
+ parsedPermissionGroup,
+ PackageManager.GET_META_DATA.toLong()
+ )!!
+ }
+
+ protected fun getPermission(
+ permissionName: String,
+ state: MutableAccessState = newState
+ ): Permission? = state.systemState.permissions[permissionName]
+
+ protected fun getPermissionTree(
+ permissionTreeName: String,
+ state: MutableAccessState = newState
+ ): Permission? = state.systemState.permissionTrees[permissionTreeName]
+
+ protected fun getPermissionGroup(
+ permissionGroupName: String,
+ state: MutableAccessState = newState
+ ): PermissionGroupInfo? = state.systemState.permissionGroups[permissionGroupName]
+
+ protected fun getPermissionFlags(
+ appId: Int,
+ userId: Int,
+ permissionName: String,
+ state: MutableAccessState = newState
+ ): Int =
+ state.userStates[userId]?.appIdPermissionFlags?.get(appId).getWithDefault(permissionName, 0)
+
+ protected fun setPermissionFlags(
+ appId: Int,
+ userId: Int,
+ permissionName: String,
+ flags: Int,
+ state: MutableAccessState = oldState
+ ) =
+ state.mutateUserState(userId)!!.mutateAppIdPermissionFlags().mutateOrPut(appId) {
+ MutableIndexedMap()
+ }.put(permissionName, flags)
+
+ companion object {
+ @JvmStatic protected val PACKAGE_NAME_0 = "packageName0"
+ @JvmStatic protected val PACKAGE_NAME_1 = "packageName1"
+ @JvmStatic protected val PACKAGE_NAME_2 = "packageName2"
+ @JvmStatic protected val MISSING_ANDROID_PACKAGE = "missingAndroidPackage"
+ @JvmStatic protected val PLATFORM_PACKAGE_NAME = "android"
+
+ @JvmStatic protected val APP_ID_0 = 0
+ @JvmStatic protected val APP_ID_1 = 1
+ @JvmStatic protected val PLATFORM_APP_ID = 2
+
+ @JvmStatic protected val PERMISSION_GROUP_NAME_0 = "permissionGroupName0"
+ @JvmStatic protected val PERMISSION_GROUP_NAME_1 = "permissionGroupName1"
+
+ @JvmStatic protected val PERMISSION_TREE_NAME = "permissionTree"
+
+ @JvmStatic protected val PERMISSION_NAME_0 = "permissionName0"
+ @JvmStatic protected val PERMISSION_NAME_1 = "permissionName1"
+ @JvmStatic protected val PERMISSION_NAME_2 = "permissionName2"
+ @JvmStatic protected val PERMISSION_READ_EXTERNAL_STORAGE =
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ @JvmStatic protected val PERMISSION_POST_NOTIFICATIONS =
+ Manifest.permission.POST_NOTIFICATIONS
+ @JvmStatic protected val PERMISSION_BLUETOOTH_CONNECT =
+ Manifest.permission.BLUETOOTH_CONNECT
+ @JvmStatic protected val PERMISSION_ACCESS_BACKGROUND_LOCATION =
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ @JvmStatic protected val PERMISSION_ACCESS_MEDIA_LOCATION =
+ Manifest.permission.ACCESS_MEDIA_LOCATION
+
+ @JvmStatic protected val USER_ID_0 = 0
+ @JvmStatic protected val USER_ID_NEW = 1
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index fbad369..b8c18e07 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -91,6 +91,7 @@
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.TestUtils;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.mode.DisplayModeDirector.BrightnessObserver;
import com.android.server.display.mode.DisplayModeDirector.DesiredDisplayModeSpecs;
import com.android.server.sensors.SensorManagerInternal;
@@ -110,7 +111,9 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -121,10 +124,114 @@
@SmallTest
@RunWith(JUnitParamsRunner.class)
public class DisplayModeDirectorTest {
- // The tolerance within which we consider something approximately equals.
+ public static Collection<Object[]> getAppRequestedSizeTestCases() {
+ var appRequestedSizeTestCases = Arrays.asList(new Object[][] {
+ {DEFAULT_MODE_75.getModeId(), Float.POSITIVE_INFINITY,
+ DEFAULT_MODE_75.getRefreshRate(), Map.of()},
+ {APP_MODE_HIGH_90.getModeId(), Float.POSITIVE_INFINITY,
+ APP_MODE_HIGH_90.getRefreshRate(),
+ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
+ APP_MODE_HIGH_90.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()))},
+ {LIMIT_MODE_70.getModeId(), Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
+ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_HIGH_90.getPhysicalWidth(),
+ APP_MODE_HIGH_90.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
+ LIMIT_MODE_70.getPhysicalHeight()))},
+ {LIMIT_MODE_70.getModeId(), LIMIT_MODE_70.getRefreshRate(),
+ LIMIT_MODE_70.getRefreshRate(),
+ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_65.getPhysicalWidth(),
+ APP_MODE_65.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
+ LIMIT_MODE_70.getPhysicalHeight()))},
+ {LIMIT_MODE_70.getModeId(), LIMIT_MODE_70.getRefreshRate(),
+ LIMIT_MODE_70.getRefreshRate(),
+ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_65.getPhysicalWidth(),
+ APP_MODE_65.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forSizeAndPhysicalRefreshRatesRange(
+ 0, 0,
+ LIMIT_MODE_70.getPhysicalWidth(),
+ LIMIT_MODE_70.getPhysicalHeight(),
+ 0, Float.POSITIVE_INFINITY)), false},
+ {APP_MODE_65.getModeId(), APP_MODE_65.getRefreshRate(),
+ APP_MODE_65.getRefreshRate(),
+ Map.of(
+ Vote.PRIORITY_APP_REQUEST_SIZE,
+ Vote.forSize(APP_MODE_65.getPhysicalWidth(),
+ APP_MODE_65.getPhysicalHeight()),
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
+ Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.forSizeAndPhysicalRefreshRatesRange(
+ 0, 0,
+ LIMIT_MODE_70.getPhysicalWidth(),
+ LIMIT_MODE_70.getPhysicalHeight(),
+ 0, Float.POSITIVE_INFINITY)), true}});
+
+ final var res = new ArrayList<Object[]>(appRequestedSizeTestCases.size() * 2);
+
+ // Add additional argument for displayResolutionRangeVotingEnabled=false if not present.
+ for (var testCaseArrayArgs : appRequestedSizeTestCases) {
+ if (testCaseArrayArgs.length == 4) {
+ var testCaseListArgs = new ArrayList<>(Arrays.asList(testCaseArrayArgs));
+ testCaseListArgs.add(/* displayResolutionRangeVotingEnabled */ false);
+ res.add(testCaseListArgs.toArray());
+ } else {
+ res.add(testCaseArrayArgs);
+ }
+ }
+
+ // Add additional argument for displayResolutionRangeVotingEnabled=true if not present.
+ for (var testCaseArrayArgs : appRequestedSizeTestCases) {
+ if (testCaseArrayArgs.length == 4) {
+ var testCaseListArgs = new ArrayList<>(Arrays.asList(testCaseArrayArgs));
+ testCaseListArgs.add(/* displayResolutionRangeVotingEnabled */ true);
+ res.add(testCaseListArgs.toArray());
+ }
+ }
+
+ return res;
+ }
+
private static final String TAG = "DisplayModeDirectorTest";
private static final boolean DEBUG = false;
private static final float FLOAT_TOLERANCE = 0.01f;
+
+ private static final Display.Mode APP_MODE_65 = new Display.Mode(
+ /*modeId=*/65, /*width=*/1900, /*height=*/1900, 65);
+ private static final Display.Mode LIMIT_MODE_70 = new Display.Mode(
+ /*modeId=*/70, /*width=*/2000, /*height=*/2000, 70);
+ private static final Display.Mode DEFAULT_MODE_75 = new Display.Mode(
+ /*modeId=*/75, /*width=*/2500, /*height=*/2500, 75);
+ private static final Display.Mode APP_MODE_HIGH_90 = new Display.Mode(
+ /*modeId=*/90, /*width=*/3000, /*height=*/3000, 90);
+ private static final Display.Mode[] TEST_MODES = new Display.Mode[] {
+ new Display.Mode(
+ /*modeId=*/60, /*width=*/1900, /*height=*/1900, 60),
+ APP_MODE_65,
+ LIMIT_MODE_70,
+ DEFAULT_MODE_75,
+ APP_MODE_HIGH_90
+ };
+
private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
private static final int MODE_ID = 1;
private static final float TRANSITION_POINT = 0.763f;
@@ -142,6 +249,8 @@
public SensorManagerInternal mSensorManagerInternalMock;
@Mock
public DisplayManagerInternal mDisplayManagerInternalMock;
+ @Mock
+ private DisplayManagerFlags mDisplayManagerFlags;
@Before
public void setUp() throws Exception {
@@ -177,7 +286,7 @@
private DisplayModeDirector createDirectorFromModeArray(Display.Mode[] modes,
Display.Mode defaultMode) {
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector);
+ new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
director.setLoggingEnabled(true);
SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
supportedModesByDisplay.put(DISPLAY_ID, modes);
@@ -219,9 +328,8 @@
// should take precedence over lower priority votes.
{
int minFps = 60;
- int maxFps = 90;
- director = createDirectorFromFpsRange(60, 90);
- assertTrue(2 * numPriorities < maxFps - minFps + 1);
+ int maxFps = minFps + 2 * numPriorities;
+ director = createDirectorFromFpsRange(minFps, maxFps);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(DISPLAY_ID, votes);
@@ -472,6 +580,7 @@
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90);
}
+ /** Resolution range voting disabled */
@Test
public void testAppRequestRefreshRateRange() {
// Confirm that the app request range doesn't include flicker or min refresh rate settings,
@@ -530,6 +639,33 @@
assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
}
+ /** Tests for app requested size */
+ @Parameters(method = "getAppRequestedSizeTestCases")
+ @Test
+ public void testAppRequestedSize(final int expectedBaseModeId,
+ final float expectedPhysicalRefreshRate,
+ final float expectedAppRequestedRefreshRate,
+ final Map<Integer, Vote> votesWithPriorities,
+ final boolean displayResolutionRangeVotingEnabled) {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled())
+ .thenReturn(displayResolutionRangeVotingEnabled);
+ DisplayModeDirector director = createDirectorFromModeArray(TEST_MODES, DEFAULT_MODE_75);
+
+ SparseArray<Vote> votes = new SparseArray<>();
+ votesWithPriorities.forEach(votes::put);
+
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+ director.injectVotesByDisplay(votesByDisplay);
+
+ var desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.baseModeId).isEqualTo(expectedBaseModeId);
+ assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.primary.physical.max).isAtLeast(expectedPhysicalRefreshRate);
+ assertThat(desiredSpecs.appRequest.physical.min).isAtMost(0);
+ assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(expectedAppRequestedRefreshRate);
+ }
+
void verifySpecsWithRefreshRateSettings(DisplayModeDirector director, float minFps,
float peakFps, float defaultFps, RefreshRateRanges primary,
RefreshRateRanges appRequest) {
@@ -843,7 +979,7 @@
@Test
public void testStaleAppRequestSize() {
DisplayModeDirector director =
- new DisplayModeDirector(mContext, mHandler, mInjector);
+ new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
Display.Mode[] modes = new Display.Mode[] {
new Display.Mode(1, 1280, 720, 60),
};
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
new file mode 100644
index 0000000..ff91d34
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -0,0 +1,446 @@
+/*
+ * 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.mode;
+
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.Mode.INVALID_MODE_ID;
+
+
+import static com.android.server.display.mode.DisplayModeDirector.SYNCHRONIZED_REFRESH_RATE_TOLERANCE;
+import static com.android.server.display.mode.Vote.PRIORITY_LIMIT_MODE;
+import static com.android.server.display.mode.Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE;
+import static com.android.server.display.mode.Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE;
+import static com.android.server.display.mode.VotesStorage.GLOBAL_ID;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.DeviceConfigInterface;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.R;
+import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.sensors.SensorManagerInternal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import junitparams.JUnitParamsRunner;
+
+
+@SmallTest
+@RunWith(JUnitParamsRunner.class)
+public class DisplayObserverTest {
+ private static final int EXTERNAL_DISPLAY = 1;
+ private static final int MAX_WIDTH = 1920;
+ private static final int MAX_HEIGHT = 1080;
+ private static final int MAX_REFRESH_RATE = 60;
+
+ private final Display.Mode[] mInternalDisplayModes = new Display.Mode[] {
+ new Display.Mode(/*modeId=*/ 0, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ (float) MAX_REFRESH_RATE / 2),
+ new Display.Mode(/*modeId=*/ 1, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ MAX_REFRESH_RATE),
+ new Display.Mode(/*modeId=*/ 2, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ MAX_REFRESH_RATE * 2),
+ new Display.Mode(/*modeId=*/ 3, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE * 2),
+ new Display.Mode(/*modeId=*/ 4, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE),
+ new Display.Mode(/*modeId=*/ 5, MAX_WIDTH * 2, MAX_HEIGHT * 2,
+ MAX_REFRESH_RATE),
+ new Display.Mode(/*modeId=*/ 6, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ MAX_REFRESH_RATE * 3),
+ };
+
+ private final Display.Mode[] mExternalDisplayModes = new Display.Mode[] {
+ new Display.Mode(/*modeId=*/ 0, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ (float) MAX_REFRESH_RATE / 2),
+ new Display.Mode(/*modeId=*/ 1, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ MAX_REFRESH_RATE),
+ new Display.Mode(/*modeId=*/ 2, MAX_WIDTH / 2, MAX_HEIGHT / 2,
+ MAX_REFRESH_RATE * 2),
+ new Display.Mode(/*modeId=*/ 3, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE * 2),
+ new Display.Mode(/*modeId=*/ 4, MAX_WIDTH, MAX_HEIGHT, MAX_REFRESH_RATE),
+ new Display.Mode(/*modeId=*/ 5, MAX_WIDTH * 2, MAX_HEIGHT * 2,
+ MAX_REFRESH_RATE),
+ };
+
+ private DisplayModeDirector mDmd;
+ private Context mContext;
+ private DisplayModeDirector.Injector mInjector;
+ private Handler mHandler;
+ private DisplayManager.DisplayListener mObserver;
+ private Resources mResources;
+ @Mock
+ private DisplayManagerFlags mDisplayManagerFlags;
+ private int mExternalDisplayUserPreferredModeId = INVALID_MODE_ID;
+ private int mInternalDisplayUserPreferredModeId = INVALID_MODE_ID;
+ private Display mDefaultDisplay;
+ private Display mExternalDisplay;
+
+ /** Setup tests. */
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mHandler = new Handler(Looper.getMainLooper());
+ mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ mResources = mock(Resources.class);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate))
+ .thenReturn(0);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth))
+ .thenReturn(0);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight))
+ .thenReturn(0);
+ when(mResources.getBoolean(R.bool.config_refreshRateSynchronizationEnabled))
+ .thenReturn(false);
+
+ // Necessary configs to initialize DisplayModeDirector
+ when(mResources.getIntArray(R.array.config_brightnessThresholdsOfPeakRefreshRate))
+ .thenReturn(new int[]{5});
+ when(mResources.getIntArray(R.array.config_ambientThresholdsOfPeakRefreshRate))
+ .thenReturn(new int[]{10});
+ when(mResources.getIntArray(
+ R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate))
+ .thenReturn(new int[]{250});
+ when(mResources.getIntArray(
+ R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
+ .thenReturn(new int[]{7000});
+ }
+
+ /** No vote for user preferred mode */
+ @Test
+ public void testExternalDisplay_notVotedUserPreferredMode() {
+ var preferredMode = mExternalDisplayModes[5];
+ mExternalDisplayUserPreferredModeId = preferredMode.getModeId();
+
+ init();
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+
+ // Testing that the vote is not added when display is added because feature is disabled
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+
+ // Testing that the vote is not present after display is removed
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+
+ // Testing that the vote is not added when display is changed because feature is disabled
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ }
+
+ /** Vote for user preferred mode */
+ @Test
+ public void testExternalDisplay_voteUserPreferredMode() {
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ var preferredMode = mExternalDisplayModes[5];
+ mExternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ var expectedVote = Vote.forSize(
+ preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight());
+ init();
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedVote);
+
+ mExternalDisplayUserPreferredModeId = INVALID_MODE_ID;
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+
+ preferredMode = mExternalDisplayModes[4];
+ mExternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ expectedVote = Vote.forSize(
+ preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight());
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedVote);
+
+ // Testing that the vote is removed.
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ }
+
+ /** External display: Do not apply limit to user preferred mode */
+ @Test
+ public void testExternalDisplay_doNotApplyLimitToUserPreferredMode() {
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate))
+ .thenReturn(MAX_REFRESH_RATE);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth))
+ .thenReturn(MAX_WIDTH);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight))
+ .thenReturn(MAX_HEIGHT);
+
+ var preferredMode = mExternalDisplayModes[5];
+ mExternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ var expectedResolutionVote = Vote.forSize(preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight());
+ init();
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedResolutionVote);
+
+ // Testing that the vote is removed.
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ }
+
+ /** Default display: Do not apply limit to user preferred mode */
+ @Test
+ public void testDefaultDisplayAdded_notAppliedLimitToUserPreferredMode() {
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate))
+ .thenReturn(MAX_REFRESH_RATE);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth))
+ .thenReturn(MAX_WIDTH);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight))
+ .thenReturn(MAX_HEIGHT);
+ var preferredMode = mInternalDisplayModes[5];
+ mInternalDisplayUserPreferredModeId = preferredMode.getModeId();
+ var expectedResolutionVote = Vote.forSize(preferredMode.getPhysicalWidth(),
+ preferredMode.getPhysicalHeight());
+ init();
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ mObserver.onDisplayAdded(DEFAULT_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(expectedResolutionVote);
+ mObserver.onDisplayRemoved(DEFAULT_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
+ .isEqualTo(null);
+ }
+
+ /** Default display added, no mode limit set */
+ @Test
+ public void testDefaultDisplayAdded() {
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate))
+ .thenReturn(MAX_REFRESH_RATE);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth))
+ .thenReturn(MAX_WIDTH);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight))
+ .thenReturn(MAX_HEIGHT);
+ init();
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ mObserver.onDisplayAdded(DEFAULT_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ }
+
+ /** External display added, apply resolution refresh rate limit */
+ @Test
+ public void testExternalDisplayAdded_applyResolutionRefreshRateLimit() {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate))
+ .thenReturn(MAX_REFRESH_RATE);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth))
+ .thenReturn(MAX_WIDTH);
+ when(mResources.getInteger(R.integer.config_externalDisplayPeakHeight))
+ .thenReturn(MAX_HEIGHT);
+ init();
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(
+ Vote.forSizeAndPhysicalRefreshRatesRange(0, 0,
+ MAX_WIDTH, MAX_HEIGHT,
+ /*minPhysicalRefreshRate=*/ 0, MAX_REFRESH_RATE));
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ }
+
+ /** External display added, disabled resolution refresh rate limit. */
+ @Test
+ public void testExternalDisplayAdded_disabledResolutionRefreshRateLimit() {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ init();
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ mObserver.onDisplayChanged(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+ assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+ }
+
+ /** External display added, applied refresh rates synchronization */
+ @Test
+ public void testExternalDisplayAdded_appliedRefreshRatesSynchronization() {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_refreshRateSynchronizationEnabled))
+ .thenReturn(true);
+ init();
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(
+ Vote.forPhysicalRefreshRates(
+ MAX_REFRESH_RATE - SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
+ MAX_REFRESH_RATE + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
+
+ // Remove external display and check that sync vote is no longer present.
+ mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
+
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ }
+
+ /** External display added, disabled feature refresh rates synchronization */
+ @Test
+ public void testExternalDisplayAdded_disabledFeatureRefreshRatesSynchronization() {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_refreshRateSynchronizationEnabled))
+ .thenReturn(true);
+ init();
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ }
+
+ /** External display not applied refresh rates synchronization, because
+ * config_refreshRateSynchronizationEnabled is false. */
+ @Test
+ public void testExternalDisplay_notAppliedRefreshRatesSynchronization() {
+ when(mDisplayManagerFlags.isDisplayResolutionRangeVotingEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isUserPreferredModeVoteEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(true);
+ init();
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
+ assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+ }
+
+ private void init() {
+ mInjector = mock(DisplayModeDirector.Injector.class);
+ doAnswer(invocation -> {
+ assertThat(mObserver).isNull();
+ mObserver = invocation.getArgument(0);
+ return null;
+ }).when(mInjector).registerDisplayListener(any(), any());
+
+ doAnswer(c -> {
+ DisplayInfo info = c.getArgument(1);
+ info.type = Display.TYPE_INTERNAL;
+ info.displayId = DEFAULT_DISPLAY;
+ info.defaultModeId = 0;
+ info.supportedModes = mInternalDisplayModes;
+ info.userPreferredModeId = mInternalDisplayUserPreferredModeId;
+ return true;
+ }).when(mInjector).getDisplayInfo(eq(DEFAULT_DISPLAY), /*displayInfo=*/ any());
+
+ doAnswer(c -> {
+ DisplayInfo info = c.getArgument(1);
+ info.type = Display.TYPE_EXTERNAL;
+ info.displayId = EXTERNAL_DISPLAY;
+ info.defaultModeId = 0;
+ info.supportedModes = mExternalDisplayModes;
+ info.userPreferredModeId = mExternalDisplayUserPreferredModeId;
+ return true;
+ }).when(mInjector).getDisplayInfo(eq(EXTERNAL_DISPLAY), /*displayInfo=*/ any());
+
+ doAnswer(c -> mock(SensorManagerInternal.class)).when(mInjector).getSensorManagerInternal();
+ doAnswer(c -> mock(DeviceConfigInterface.class)).when(mInjector).getDeviceConfig();
+
+ mDefaultDisplay = mock(Display.class);
+ when(mDefaultDisplay.getDisplayId()).thenReturn(DEFAULT_DISPLAY);
+ doAnswer(c -> mInjector.getDisplayInfo(DEFAULT_DISPLAY, c.getArgument(0)))
+ .when(mDefaultDisplay).getDisplayInfo(/*displayInfo=*/ any());
+
+ mExternalDisplay = mock(Display.class);
+ when(mExternalDisplay.getDisplayId()).thenReturn(EXTERNAL_DISPLAY);
+ doAnswer(c -> mInjector.getDisplayInfo(EXTERNAL_DISPLAY, c.getArgument(0)))
+ .when(mExternalDisplay).getDisplayInfo(/*displayInfo=*/ any());
+
+ when(mInjector.getDisplays()).thenReturn(new Display[] {mDefaultDisplay, mExternalDisplay});
+
+ mDmd = new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
+ mDmd.start(null);
+ assertThat(mObserver).isNotNull();
+ }
+
+ @Nullable
+ private Vote getVote(final int displayId, final int priority) {
+ return mDmd.getVote(displayId, priority);
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
index 287fdd5..50e2392 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.util.SparseArray;
@@ -72,6 +73,18 @@
verify(mVotesListener).onChanged();
}
+ /** Verifies that adding the same vote twice results in a single call to onChanged */
+ @Test
+ public void notifiesVoteListenerCalledOnceIfVoteUpdatedTwice() {
+ // WHEN updateVote is called
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY_OTHER, VOTE_OTHER);
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY_OTHER, VOTE);
+ // THEN listener is notified, but only when vote changes.
+ verify(mVotesListener, times(3)).onChanged();
+ }
+
@Test
public void addsAnotherVoteToStorageWithDifferentPriority() {
// GIVEN vote storage with one vote
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 82b7540..2f0257a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -39,11 +39,16 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.DexmakerShareClassLoaderRule;
import android.view.Display;
+import android.view.WindowManager;
import com.android.server.accessibility.magnification.MagnificationProcessor;
import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -76,6 +81,9 @@
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
AccessibilityServiceConnection mConnection;
@Mock AccessibilityUserState mMockUserState;
@@ -113,6 +121,8 @@
when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
when(mMockA11yTrace.isA11yTracingEnabled()).thenReturn(false);
+ when(mMockContext.getSystemService(Context.DISPLAY_SERVICE))
+ .thenReturn(new DisplayManager(mMockContext));
mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
@@ -168,6 +178,18 @@
assertFalse(mConnection.getServiceInfo().crashed);
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+ public void onServiceConnected_addsWindowTokens() {
+ setServiceBinding(COMPONENT_NAME);
+ mConnection.bindLocked();
+ mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
+
+ verify(mMockWindowManagerInternal).addWindowToken(
+ any(), eq(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY),
+ anyInt(), eq(null));
+ }
+
private void setServiceBinding(ComponentName componentName) {
when(mMockUserState.getBindingServicesLocked())
.thenReturn(new HashSet<>(Arrays.asList(componentName)));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b4558b2..63281b7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -35,6 +35,7 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -46,6 +47,9 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Color;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.testing.DexmakerShareClassLoaderRule;
@@ -88,6 +92,9 @@
@Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Mock private AccessibilityServiceInfo mMockServiceInfo;
@Mock private AccessibilityServiceConnection mMockConnection;
@@ -188,7 +195,7 @@
mUserState.addServiceLocked(mMockConnection);
- verify(mMockConnection, never()).onAdded();
+ verify(mMockListener, never()).onServiceInfoChangedLocked(any());
}
@Test
@@ -197,13 +204,24 @@
mUserState.addServiceLocked(mMockConnection);
- verify(mMockConnection).onAdded();
assertTrue(mUserState.getBoundServicesLocked().contains(mMockConnection));
assertEquals(mMockConnection, mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
}
@Test
+ // addServiceLocked only calls addWindowTokensForAllDisplays when
+ // FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK is off, so skip the test if it is on.
+ @RequiresFlagsDisabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+ public void addService_flagDisabled_addsWindowTokens() {
+ when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+
+ mUserState.addServiceLocked(mMockConnection);
+
+ verify(mMockConnection).addWindowTokensForAllDisplays();
+ }
+
+ @Test
public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() {
// When soft kb show mode is hidden in settings and is auto in state.
putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 4ce9ba0..3ee5f61 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -21,8 +21,11 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,6 +39,10 @@
import android.content.pm.ServiceInfo;
import android.hardware.display.DisplayManager;
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.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -43,6 +50,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -58,6 +66,9 @@
MessageCapturingHandler mMessageCapturingHandler;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Mock Context mMockContext;
@Mock AccessibilityServiceInfo mMockServiceInfo;
@Mock ResolveInfo mMockResolveInfo;
@@ -197,6 +208,24 @@
assertEquals(0, mUiAutomationManager.getRequestedEventMaskLocked());
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+ public void registerUiAutomationService_callsAddWindowTokenUsingHandler() {
+ register(0);
+ // registerUiTestAutomationServiceLocked() should not directly call addWindowToken.
+ verify(mMockWindowManagerInternal, never()).addWindowToken(
+ any(), anyInt(), anyInt(), any());
+
+ // Advance UiAutomationManager#UiAutomationService's handler.
+ mMessageCapturingHandler.sendAllMessages();
+
+ // After advancing the handler we expect addWindowToken to have been called
+ // by the UiAutomationService instance.
+ verify(mMockWindowManagerInternal).addWindowToken(
+ any(), eq(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY),
+ anyInt(), eq(null));
+ }
+
private void register(int flags) {
mUiAutomationManager.registerUiTestAutomationServiceLocked(mMockOwner,
mMockAccessibilityServiceClient, mMockContext, mMockServiceInfo, SERVICE_ID,
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 349a597..430f600 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
@@ -687,7 +687,7 @@
swipeAndHold(initCoords, edgeCoords);
assertTrue(mMgh.mCurrentState == mMgh.mSinglePanningState);
- assertTrue(mMgh.mSinglePanningState.mOverscrollState == mMgh.OVERSCROLL_LEFT_EDGE);
+ assertTrue(mMgh.mOverscrollHandler.mOverscrollState == mMgh.OVERSCROLL_LEFT_EDGE);
assertTrue(isZoomed());
}
@@ -711,7 +711,7 @@
swipeAndHold(initCoords, edgeCoords);
assertTrue(mMgh.mCurrentState == mMgh.mSinglePanningState);
- assertTrue(mMgh.mSinglePanningState.mOverscrollState == mMgh.OVERSCROLL_RIGHT_EDGE);
+ assertTrue(mMgh.mOverscrollHandler.mOverscrollState == mMgh.OVERSCROLL_RIGHT_EDGE);
assertTrue(isZoomed());
}
@@ -734,7 +734,7 @@
swipeAndHold(initCoords, edgeCoords);
- assertTrue(mMgh.mSinglePanningState.mOverscrollState == mMgh.OVERSCROLL_VERTICAL_EDGE);
+ assertTrue(mMgh.mOverscrollHandler.mOverscrollState == mMgh.OVERSCROLL_VERTICAL_EDGE);
assertTrue(isZoomed());
}
@@ -756,7 +756,7 @@
swipeAndHold(initCoords, edgeCoords);
- assertTrue(mMgh.mSinglePanningState.mOverscrollState == mMgh.OVERSCROLL_NONE);
+ assertTrue(mMgh.mOverscrollHandler.mOverscrollState == mMgh.OVERSCROLL_NONE);
assertTrue(isZoomed());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 020afdb..8d7b5cb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -141,6 +141,7 @@
import com.android.server.notification.PermissionHelper.PackagePermission;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.protobuf.InvalidProtocolBufferException;
import org.json.JSONArray;
@@ -563,7 +564,7 @@
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, false, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, false, true, false, true, null).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -647,7 +648,7 @@
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, false, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, false, true, false, true, null).getList();
boolean foundNcg = false;
for (NotificationChannelGroup actual : actualGroups) {
if (ncg.getId().equals(actual.getId())) {
@@ -2620,6 +2621,16 @@
}
@Test
+ public void testOnlyHasDefaultChannel() throws Exception {
+ assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
+
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false,
+ UID_N_MR1, false);
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ }
+
+ @Test
public void testCreateDeletedChannel() throws Exception {
long[] vibration = new long[]{100, 67, 145, 156};
NotificationChannel channel =
@@ -2644,16 +2655,6 @@
}
@Test
- public void testOnlyHasDefaultChannel() throws Exception {
- assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
- assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
-
- mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false,
- UID_N_MR1, false);
- assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
- }
-
- @Test
public void testCreateChannel_defaultChannelId() throws Exception {
try {
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, new NotificationChannel(
@@ -2884,7 +2885,7 @@
UID_N_MR1});
assertEquals(0, mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, true, true, false).getList().size());
+ PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList().size());
}
@Test
@@ -3022,7 +3023,7 @@
UID_N_MR1, false);
List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList();
assertEquals(3, actual.size());
for (NotificationChannelGroup group : actual) {
if (group.getId() == null) {
@@ -3056,14 +3057,15 @@
channel1.setGroup(ncg.getId());
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false,
UID_N_MR1, false);
- mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true, false, true, null)
+ .getList();
channel1.setImportance(IMPORTANCE_LOW);
mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true,
UID_N_MR1, false);
List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+ PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList();
assertEquals(2, actual.size());
for (NotificationChannelGroup group : actual) {
@@ -3089,7 +3091,7 @@
UID_N_MR1, false);
List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
- PKG_N_MR1, UID_N_MR1, false, false, true).getList();
+ PKG_N_MR1, UID_N_MR1, false, false, true, true, null).getList();
assertEquals(2, actual.size());
for (NotificationChannelGroup group : actual) {
@@ -5786,6 +5788,62 @@
assertFalse(isUserSet);
}
+ @Test
+ public void testGetNotificationChannelGroups_withChannelFilter_includeBlocked() {
+ NotificationChannel channel =
+ new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false,
+ UID_N_MR1, false);
+ // modifying same object, don't need to call updateNotificationChannel
+ channel.setImportance(IMPORTANCE_NONE);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false,
+ UID_N_MR1, false);
+
+ NotificationChannel channel3 =
+ new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false,
+ UID_N_MR1, false);
+
+ Set<String> filter = ImmutableSet.of("id3");
+
+ NotificationChannelGroup actual = mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, false, true, false, true, filter).getList().get(0);
+ assertEquals(2, actual.getChannels().size());
+ assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id3")).count());
+ assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count());
+ }
+
+ @Test
+ public void testGetNotificationChannelGroups_withChannelFilter_doNotIncludeBlocked() {
+ NotificationChannel channel =
+ new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false,
+ UID_N_MR1, false);
+ // modifying same object, don't need to call updateNotificationChannel
+ channel.setImportance(IMPORTANCE_NONE);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false,
+ UID_N_MR1, false);
+
+ NotificationChannel channel3 =
+ new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false,
+ UID_N_MR1, false);
+
+ Set<String> filter = ImmutableSet.of("id3");
+
+ NotificationChannelGroup actual = mHelper.getNotificationChannelGroups(
+ PKG_N_MR1, UID_N_MR1, false, true, false, false, filter).getList().get(0);
+ assertEquals(1, actual.getChannels().size());
+ assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id3")).count());
+ assertEquals(0, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count());
+ }
+
private static NotificationChannel cloneChannel(NotificationChannel original) {
Parcel parcel = Parcel.obtain();
try {
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 61c4d06..8db09f9 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -46,6 +46,7 @@
import static java.util.Collections.unmodifiableMap;
import android.content.Context;
+import android.os.Looper;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.view.InputDevice;
@@ -98,6 +99,10 @@
* settings values.
*/
protected final void setUpPhoneWindowManager(boolean supportSettingsUpdate) {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
doReturn(mSettingsProviderRule.mockContentResolver(mContext))
.when(mContext).getContentResolver();
mPhoneWindowManager = new TestPhoneWindowManager(mContext, supportSettingsUpdate);
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 6e2c4bd..ef28ffa 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -70,6 +70,7 @@
import android.hardware.input.InputManager;
import android.media.AudioManagerInternal;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
@@ -77,7 +78,6 @@
import android.os.UserHandle;
import android.os.Vibrator;
import android.os.VibratorInfo;
-import android.os.test.TestLooper;
import android.service.dreams.DreamManagerInternal;
import android.telecom.TelecomManager;
import android.util.FeatureFlagUtils;
@@ -160,8 +160,8 @@
@Mock private KeyguardServiceDelegate mKeyguardServiceDelegate;
private StaticMockitoSession mMockitoSession;
+ private HandlerThread mHandlerThread;
private Handler mHandler;
- private TestLooper mTestLooper;
private class TestInjector extends PhoneWindowManager.Injector {
TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
@@ -184,11 +184,12 @@
TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
MockitoAnnotations.initMocks(this);
- mTestLooper = new TestLooper();
- mHandler = new Handler(mTestLooper.getLooper());
+ mHandlerThread = new HandlerThread("fake window manager");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
mContext = mockingDetails(context).isSpy() ? context : spy(context);
- mHandler.post(() -> setUp(supportSettingsUpdate));
- mTestLooper.dispatchAll();
+ mHandler.runWithScissors(() -> setUp(supportSettingsUpdate), 0 /* timeout */);
+ waitForIdle();
}
private void setUp(boolean supportSettingsUpdate) {
@@ -300,6 +301,7 @@
}
void tearDown() {
+ mHandlerThread.quitSafely();
LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
Mockito.reset(mPhoneWindowManager);
mMockitoSession.finishMocking();
@@ -326,7 +328,7 @@
}
void waitForIdle() {
- mTestLooper.dispatchAll();
+ mHandler.runWithScissors(() -> { }, 0 /* timeout */);
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 0494dfd..f65cb93 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -147,14 +147,12 @@
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner.Stub;
import android.view.IWindowManager;
-import android.view.IWindowSession;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.window.TaskSnapshot;
import androidx.test.filters.MediumTest;
@@ -2073,7 +2071,7 @@
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
params.width = params.height = WindowManager.LayoutParams.MATCH_PARENT;
final TestWindowState w = new TestWindowState(
- mAtm.mWindowManager, mock(Session.class), new TestIWindow(), params, activity);
+ mAtm.mWindowManager, getTestSession(), new TestIWindow(), params, activity);
activity.addWindow(w);
// Assume the activity is launching in different rotation, and there was an available
@@ -2083,23 +2081,8 @@
.build();
setRotatedScreenOrientationSilently(activity);
activity.setVisible(false);
-
- final IWindowSession session = WindowManagerGlobal.getWindowSession();
- spyOn(session);
- try {
- // Return error to skip unnecessary operation.
- doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay(
- any() /* window */, any() /* attrs */,
- anyInt() /* viewVisibility */, anyInt() /* displayId */,
- anyInt() /* requestedVisibleTypes */, any() /* outInputChannel */,
- any() /* outInsetsState */, any() /* outActiveControls */,
- any() /* outAttachedFrame */, any() /* outSizeCompatScale */);
- mAtm.mWindowManager.mStartingSurfaceController
- .createTaskSnapshotSurface(activity, snapshot);
- } catch (RemoteException ignored) {
- } finally {
- reset(session);
- }
+ mAtm.mWindowManager.mStartingSurfaceController
+ .createTaskSnapshotSurface(activity, snapshot);
// Because the rotation of snapshot and the corresponding top activity are different, fixed
// rotation should be applied when creating snapshot surface if the display rotation may be
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index f30ecbe..8e7ba70 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -337,6 +337,7 @@
WindowState appWindow = task.getTopVisibleAppMainWindow();
WindowOnBackInvokedDispatcher dispatcher =
new WindowOnBackInvokedDispatcher(context);
+ spyOn(appWindow.mSession);
doAnswer(invocation -> {
appWindow.setOnBackInvokedCallbackInfo(invocation.getArgument(1));
return null;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 1ad04a2..cd0389d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -700,7 +700,7 @@
}
private WindowState createWindowState(WindowToken token) {
- return new WindowState(mWm, mock(Session.class), new TestIWindow(), token,
+ return new WindowState(mWm, getTestSession(), new TestIWindow(), token,
null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(),
View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */,
false /* ownerCanAddInternalSystemWindow */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index ae4ebc1..c2b7fec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -573,7 +573,10 @@
assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
// Add a window to the second display, and it should be focused
- final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2");
+ final ActivityRecord app2 = new ActivityBuilder(mAtm)
+ .setTask(new TaskBuilder(mSupervisor).setDisplay(dc).build())
+ .setUseProcess(window1.getProcess()).setOnTop(true).build();
+ final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, app2, "window2");
window2.mActivityRecord.mTargetSdk = targetSdk;
updateFocusedWindow();
assertTrue(window2.isFocused());
@@ -1088,7 +1091,7 @@
assertFalse(dc.getRotationReversionController().isAnyOverrideActive());
dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED,
- ROTATION_90);
+ ROTATION_90, /* caller= */ "DisplayContentTests");
updateAllDisplayContentAndRotation(dc);
assertEquals(ROTATION_90, dc.getDisplayRotation()
.rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_90));
@@ -1107,7 +1110,7 @@
assertEquals(ROTATION_90, dc.getDisplayRotation()
.rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_0));
dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE,
- ROTATION_0);
+ ROTATION_0, /* caller= */ "DisplayContentTests");
}
@Test
@@ -1134,7 +1137,8 @@
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
dc.getDisplayRotation().setUserRotation(
- WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_180);
+ WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_180,
+ /* caller= */ "DisplayContentTests");
final int newOrientation = getRotatedOrientation(dc);
final Task task = new TaskBuilder(mSupervisor)
@@ -1174,7 +1178,8 @@
dc.getDisplayRotation().setFixedToUserRotation(
IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
dc.getDisplayRotation().setUserRotation(
- WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0);
+ WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0,
+ /* caller= */ "DisplayContentTests");
dc.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
final int newOrientation = getRotatedOrientation(dc);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 915b387..e14568d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -1265,7 +1265,7 @@
}
private void freezeRotation(int rotation) {
- mTarget.freezeRotation(rotation);
+ mTarget.freezeRotation(rotation, /* caller= */ "DisplayRotationTests");
if (mTarget.isDefaultDisplay) {
mAccelerometerRotationObserver.onChange(false);
@@ -1274,7 +1274,7 @@
}
private void thawRotation() {
- mTarget.thawRotation();
+ mTarget.thawRotation(/* caller= */ "DisplayRotationTests");
if (mTarget.isDefaultDisplay) {
mAccelerometerRotationObserver.onChange(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 4526d18..50fe042 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -55,7 +55,6 @@
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.view.DragEvent;
-import android.view.IWindowSessionCallback;
import android.view.InputChannel;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -98,6 +97,7 @@
private static final String TEST_PACKAGE = "com.test.package";
private TestDragDropController mTarget;
+ private WindowProcessController mProcess;
private WindowState mWindow;
private IBinder mToken;
@@ -137,10 +137,9 @@
* Creates a window state which can be used as a drop target.
*/
private WindowState createDropTargetWindow(String name, int ownerId) {
- final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
- final Task rootTask = createTask(mDisplayContent);
- final Task task = createTaskInRootTask(rootTask, ownerId);
- task.addChild(activity, 0);
+ final Task task = new TaskBuilder(mSupervisor).setUserId(ownerId).build();
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
+ .setUseProcess(mProcess).build();
// Use a new TestIWindow so we don't collect events for other windows
final WindowState window = createWindow(
@@ -167,6 +166,8 @@
@Before
public void setUp() throws Exception {
mTarget = new TestDragDropController(mWm, mWm.mH.getLooper());
+ mProcess = mSystemServicesTestRule.addProcess(TEST_PACKAGE, "testProc",
+ TEST_PID, TEST_UID);
mWindow = createDropTargetWindow("Drag test window", 0);
doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
when(mWm.mInputManager.transferTouchFocus(any(InputChannel.class),
@@ -221,8 +222,6 @@
@Test
public void testPrivateInterceptGlobalDragDropFlagChecksPermission() {
- spyOn(mWm.mContext);
-
DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
attrs.privateFlags |= PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
@@ -323,10 +322,7 @@
@Test
public void testValidateAppActivityArguments() {
- final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
- @Override
- public void onAnimatorScaleChanged(float scale) {}
- });
+ final Session session = getTestSession();
try {
session.validateAndResolveDragMimeTypeExtras(
createClipDataForActivity(mock(PendingIntent.class), null), TEST_UID, TEST_PID,
@@ -364,10 +360,7 @@
public void testValidateAppShortcutArguments() {
doReturn(PERMISSION_GRANTED).when(mWm.mContext)
.checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
- final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
- @Override
- public void onAnimatorScaleChanged(float scale) {}
- });
+ final Session session = createTestSession(mAtm);
try {
session.validateAndResolveDragMimeTypeExtras(
createClipDataForShortcut(null, "test_shortcut_id", mock(UserHandle.class)),
@@ -398,10 +391,7 @@
public void testValidateProfileAppShortcutArguments_notCallingUid() {
doReturn(PERMISSION_GRANTED).when(mWm.mContext)
.checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
- final Session session = Mockito.spy(new Session(mWm, new IWindowSessionCallback.Stub() {
- @Override
- public void onAnimatorScaleChanged(float scale) {}
- }));
+ final Session session = createTestSession(mAtm);
final ShortcutServiceInternal shortcutService = mock(ShortcutServiceInternal.class);
final Intent[] shortcutIntents = new Intent[1];
shortcutIntents[0] = new Intent();
@@ -443,10 +433,7 @@
public void testValidateAppTaskArguments() {
doReturn(PERMISSION_GRANTED).when(mWm.mContext)
.checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
- final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
- @Override
- public void onAnimatorScaleChanged(float scale) {}
- });
+ final Session session = createTestSession(mAtm);
try {
final ClipData clipData = new ClipData(
new ClipDescription("drag", new String[] { MIMETYPE_APPLICATION_TASK }),
@@ -462,10 +449,7 @@
@Test
public void testValidateFlags() {
- final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
- @Override
- public void onAnimatorScaleChanged(float scale) {}
- });
+ final Session session = getTestSession();
try {
session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION);
fail("Expected failure without permission");
@@ -478,10 +462,7 @@
public void testValidateFlagsWithPermission() {
doReturn(PERMISSION_GRANTED).when(mWm.mContext)
.checkCallingOrSelfPermission(eq(START_TASKS_FROM_RECENTS));
- final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
- @Override
- public void onAnimatorScaleChanged(float scale) {}
- });
+ final Session session = createTestSession(mAtm);
try {
session.validateDragFlags(View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION);
// Expected pass
@@ -571,7 +552,8 @@
assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(),
new InputChannel(), true /* isDragDrop */));
- mToken = mTarget.performDrag(0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0, data);
+ mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient,
+ flag, surface, 0, 0, 0, 0, 0, data);
assertNotNull(mToken);
r.run();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 8f68c0f..1ceb1a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4739,24 +4739,20 @@
assertEquals(new Rect(1050, 0, 1750, 1400), mActivity.getBounds());
}
- private static WindowState addWindowToActivity(ActivityRecord activity) {
+ private WindowState addWindowToActivity(ActivityRecord activity) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
params.setFitInsetsSides(0);
params.setFitInsetsTypes(0);
final TestWindowState w = new TestWindowState(
- activity.mWmService, mock(Session.class), new TestIWindow(), params, activity);
+ activity.mWmService, getTestSession(), new TestIWindow(), params, activity);
makeWindowVisible(w);
w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
activity.addWindow(w);
return w;
}
- private static TestWindowState addStatusBar(DisplayContent displayContent) {
- final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
- doReturn(true).when(displayPolicy).hasStatusBar();
- displayPolicy.onConfigurationChanged();
-
+ private TestWindowState addStatusBar(DisplayContent displayContent) {
final TestWindowToken token = createTestWindowToken(
TYPE_STATUS_BAR, displayContent);
final WindowManager.LayoutParams attrs =
@@ -4772,11 +4768,12 @@
new InsetsFrameProvider(owner, 0, WindowInsets.Type.mandatorySystemGestures())
};
final TestWindowState statusBar = new TestWindowState(
- displayContent.mWmService, mock(Session.class), new TestIWindow(), attrs, token);
+ displayContent.mWmService, getTestSession(), new TestIWindow(), attrs, token);
token.addWindow(statusBar);
statusBar.setRequestedSize(displayContent.mBaseDisplayWidth,
SystemBarUtils.getStatusBarHeight(displayContent.getDisplayUiContext()));
+ final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.addWindowLw(statusBar, attrs);
displayPolicy.layoutWindowLw(statusBar, null, displayContent.mDisplayFrames);
return statusBar;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index fb27d63..0c58069 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -610,7 +610,7 @@
// display.
final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
- dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0, /* caller= */ "TaskTests");
final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
@@ -642,7 +642,7 @@
// Setting app to fixed landscape and changing display
top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
// Fix the display orientation to portrait which is 90 degrees for the test display.
- dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90, /* caller= */ "TaskTests");
// Fixed orientation request should be resolved on activity level. Task fills display
// bounds.
@@ -681,7 +681,7 @@
// display.
final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
- dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0, /* caller= */ "TaskTests");
final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 76576f7..e86fc36 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -82,7 +82,6 @@
import android.util.MergedConfiguration;
import android.view.ContentRecordingSession;
import android.view.IWindow;
-import android.view.IWindowSessionCallback;
import android.view.InputChannel;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -496,11 +495,7 @@
spyOn(mWm.mWindowContextListenerController);
final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD, mDefaultDisplay);
- final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
- @Override
- public void onAnimatorScaleChanged(float v) throws RemoteException {
- }
- });
+ final Session session = getTestSession();
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
TYPE_APPLICATION_ATTACHED_DIALOG);
params.token = windowToken.token;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 0ddd31355..014d57d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -95,7 +95,6 @@
import android.util.MergedConfiguration;
import android.view.Gravity;
import android.view.IWindow;
-import android.view.IWindowSessionCallback;
import android.view.InputWindowHandle;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
@@ -1332,12 +1331,7 @@
final WindowToken windowToken = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
mDisplayContent);
final IWindow client = new TestIWindow();
- final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
- @Override
- public void onAnimatorScaleChanged(float v) throws RemoteException {
-
- }
- });
+ final Session session = getTestSession();
final ClientWindowFrames outFrames = new ClientWindowFrames();
final MergedConfiguration outConfig = new MergedConfiguration();
final SurfaceControl outSurfaceControl = new SurfaceControl();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 99688da..9146889 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -94,6 +94,7 @@
import android.view.Gravity;
import android.view.IDisplayWindowInsetsController;
import android.view.IWindow;
+import android.view.IWindowSessionCallback;
import android.view.InsetsFrameProvider;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -153,7 +154,7 @@
ActivityTaskSupervisor mSupervisor;
WindowManagerService mWm;
private final IWindow mIWindow = new TestIWindow();
- private Session mMockSession;
+ private Session mTestSession;
private boolean mUseFakeSettingsProvider;
DisplayInfo mDisplayInfo = new DisplayInfo();
@@ -231,7 +232,6 @@
suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget());
mTransaction = mSystemServicesTestRule.mTransaction;
- mMockSession = mock(Session.class);
mContext.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY).getDisplayInfo(mDisplayInfo);
@@ -508,6 +508,54 @@
return statusBar;
}
+ Session getTestSession() {
+ if (mTestSession != null) {
+ return mTestSession;
+ }
+ mTestSession = createTestSession(mAtm);
+ return mTestSession;
+ }
+
+ private Session getTestSession(WindowToken token) {
+ final ActivityRecord r = token.asActivityRecord();
+ if (r == null || r.app == null) {
+ return getTestSession();
+ }
+ // If the activity has a process, let the window session belonging to activity use the
+ // process of the activity.
+ int pid = r.app.getPid();
+ if (pid == 0) {
+ // See SystemServicesTestRule#addProcess, pid 0 isn't added to the map. So generate
+ // a non-zero pid to initialize it.
+ final int numPid = mAtm.mProcessMap.getPidMap().size();
+ pid = numPid > 0 ? mAtm.mProcessMap.getPidMap().keyAt(numPid - 1) + 1 : 1;
+ r.app.setPid(pid);
+ mAtm.mProcessMap.put(pid, r.app);
+ } else {
+ final WindowState win = mRootWindowContainer.getWindow(w -> w.getProcess() == r.app);
+ if (win != null) {
+ // Reuse the same Session if there is a window uses the same process.
+ return win.mSession;
+ }
+ }
+ return createTestSession(mAtm, pid, r.getUid());
+ }
+
+ static Session createTestSession(ActivityTaskManagerService atms) {
+ return createTestSession(atms, WindowManagerService.MY_PID, WindowManagerService.MY_UID);
+ }
+
+ static Session createTestSession(ActivityTaskManagerService atms, int pid, int uid) {
+ if (atms.mProcessMap.getProcess(pid) == null) {
+ SystemServicesTestRule.addProcess(atms, "testPkg", "testProc", pid, uid);
+ }
+ return new Session(atms.mWindowManager, new IWindowSessionCallback.Stub() {
+ @Override
+ public void onAnimatorScaleChanged(float scale) {
+ }
+ }, pid, uid);
+ }
+
WindowState createAppWindow(Task task, int type, String name) {
final ActivityRecord activity = createNonAttachedActivityRecord(task.getDisplayContent());
task.addChild(activity, 0);
@@ -587,7 +635,7 @@
WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
int ownerId, boolean ownerCanAddInternalSystemWindow, IWindow iwindow) {
return createWindow(parent, type, token, name, ownerId, UserHandle.getUserId(ownerId),
- ownerCanAddInternalSystemWindow, mWm, mMockSession, iwindow,
+ ownerCanAddInternalSystemWindow, mWm, getTestSession(token), iwindow,
mSystemServicesTestRule.getPowerManagerWrapper());
}
@@ -891,7 +939,7 @@
TestWindowState createWindowState(WindowManager.LayoutParams attrs, WindowToken token) {
SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock);
- return new TestWindowState(mWm, mMockSession, mIWindow, attrs, token);
+ return new TestWindowState(mWm, getTestSession(), mIWindow, attrs, token);
}
/** Creates a {@link DisplayContent} as parts of simulate display info for test. */
@@ -1705,8 +1753,7 @@
final WindowState window = WindowTestsBase.createWindow(null,
TYPE_APPLICATION_STARTING, activity,
"Starting window", 0 /* ownerId */, 0 /* userId*/,
- false /* internalWindows */, mWMService, mock(Session.class),
- iWindow,
+ false /* internalWindows */, mWMService, createTestSession(mAtm), iWindow,
mPowerManagerWrapper);
activity.mStartingWindow = window;
mAppWindowMap.put(info.appToken, window);
diff --git a/tests/BinderLeakTest/Android.bp b/tests/BinderLeakTest/Android.bp
new file mode 100644
index 0000000..78b0ede
--- /dev/null
+++ b/tests/BinderLeakTest/Android.bp
@@ -0,0 +1,40 @@
+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"],
+}
+
+filegroup {
+ name: "binder_leak_test_aidl",
+ srcs: ["**/*.aidl"],
+ path: "aidl",
+}
+
+java_defaults {
+ name: "BinderTest.defaults",
+ srcs: [
+ "**/*.java",
+ ":binder_leak_test_aidl",
+ ],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ ],
+}
+
+// Built with target_sdk_version: current
+android_test {
+ name: "BinderLeakTest",
+ defaults: ["BinderTest.defaults"],
+}
+
+// Built with target_sdk_version: 33
+android_test {
+ name: "BinderLeakTest_legacy",
+ defaults: ["BinderTest.defaults"],
+ manifest: "AndroidManifest_legacy.xml",
+}
diff --git a/tests/BinderLeakTest/AndroidManifest.xml b/tests/BinderLeakTest/AndroidManifest.xml
new file mode 100644
index 0000000..756def7
--- /dev/null
+++ b/tests/BinderLeakTest/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.binder">
+ <application>
+ <service
+ android:name=".MyService"
+ android:enabled="true"
+ android:exported="true"
+ android:process=":service">
+ </service>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test.binder"
+ android:label="Binder leak test">
+ </instrumentation>
+</manifest>
diff --git a/tests/BinderLeakTest/AndroidManifest_legacy.xml b/tests/BinderLeakTest/AndroidManifest_legacy.xml
new file mode 100644
index 0000000..03d1dfd
--- /dev/null
+++ b/tests/BinderLeakTest/AndroidManifest_legacy.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.binder">
+ <uses-sdk android:minSdkVersion="33"
+ android:targetSdkVersion="33"
+ android:maxSdkVersion="33" />
+ <application>
+ <service
+ android:name=".MyService"
+ android:enabled="true"
+ android:exported="true"
+ android:process=":service">
+ </service>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test.binder"
+ android:label="Binder leak test">
+ </instrumentation>
+</manifest>
diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl
new file mode 100644
index 0000000..a721959
--- /dev/null
+++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl
@@ -0,0 +1,5 @@
+package com.android.test.binder;
+
+interface IFoo {
+
+}
diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl
new file mode 100644
index 0000000..b487f51
--- /dev/null
+++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl
@@ -0,0 +1,10 @@
+package com.android.test.binder;
+import com.android.test.binder.IFoo;
+
+interface IFooProvider {
+ IFoo createFoo();
+
+ boolean isFooGarbageCollected();
+
+ oneway void killProcess();
+}
diff --git a/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java
new file mode 100644
index 0000000..f07317f
--- /dev/null
+++ b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.test.binder;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.rule.ServiceTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+public class BinderTest {
+ @Rule
+ public final ServiceTestRule serviceRule = new ServiceTestRule();
+
+ @Test
+ public void testDeathRecipientLeaksOrNot()
+ throws RemoteException, TimeoutException, InterruptedException {
+ Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MyService.class);
+ IFooProvider provider = IFooProvider.Stub.asInterface(serviceRule.bindService(intent));
+ FooHolder holder = new FooHolder(provider.createFoo());
+
+ // ref will get enqueued right after holder is finalized for gc.
+ ReferenceQueue<FooHolder> refQueue = new ReferenceQueue<>();
+ PhantomReference<FooHolder> ref = new PhantomReference<>(holder, refQueue);
+
+ DeathRecorder deathRecorder = new DeathRecorder();
+ holder.registerDeathRecorder(deathRecorder);
+
+ if (getSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ /////////////////////////////////////////////
+ // New behavior
+ //
+ // Reference chain at this moment:
+ // holder --(java strong ref)--> FooHolder
+ // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy
+ // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy
+ // BinderProxy --(binder ref)--> Foo.Stub
+ // In other words, the variable "holder" is the root of the reference chain.
+
+ // By setting the variable to null, we make FooHolder, IFoo.Proxy, BinderProxy, and even
+ // Foo.Stub unreachable.
+ holder = null;
+
+ // Ensure that the objects are garbage collected
+ forceGc();
+ assertEquals(ref, refQueue.poll());
+ assertTrue(provider.isFooGarbageCollected());
+
+ // The binder has died, but we don't get notified since the death recipient is GC'ed.
+ provider.killProcess();
+ Thread.sleep(1000); // give some time for the service process to die and reaped
+ assertFalse(deathRecorder.deathRecorded);
+ } else {
+ /////////////////////////////////////////////
+ // Legacy behavior
+ //
+ // Reference chain at this moment:
+ // JavaDeathRecipient --(JNI strong ref)--> FooHolder
+ // holder --(java strong ref)--> FooHolder
+ // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy
+ // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy
+ // BinderProxy --(binder ref)--> Foo.Stub
+ // So, BOTH JavaDeathRecipient and holder are roots of the reference chain.
+
+ // Even if we set holder to null, it doesn't make other objects unreachable; they are
+ // still reachable via the JNI strong ref.
+ holder = null;
+
+ // Check that objects are not garbage collected
+ forceGc();
+ assertNotEquals(ref, refQueue.poll());
+ assertFalse(provider.isFooGarbageCollected());
+
+ // The legacy behavior is getting notified even when there's no reference
+ provider.killProcess();
+ Thread.sleep(1000); // give some time for the service process to die and reaped
+ assertTrue(deathRecorder.deathRecorded);
+ }
+ }
+
+ static class FooHolder implements IBinder.DeathRecipient {
+ private IFoo mProxy;
+ private DeathRecorder mDeathRecorder;
+
+ FooHolder(IFoo proxy) throws RemoteException {
+ proxy.asBinder().linkToDeath(this, 0);
+
+ // A strong reference from DeathRecipient(this) to the binder proxy is created here
+ mProxy = proxy;
+ }
+
+ public void registerDeathRecorder(DeathRecorder dr) {
+ mDeathRecorder = dr;
+ }
+
+ @Override
+ public void binderDied() {
+ if (mDeathRecorder != null) {
+ mDeathRecorder.deathRecorded = true;
+ }
+ }
+ }
+
+ static class DeathRecorder {
+ public boolean deathRecorded = false;
+ }
+
+ // Try calling System.gc() until an orphaned object is confirmed to be finalized
+ private static void forceGc() {
+ Object obj = new Object();
+ ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
+ PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue);
+ obj = null; // make it an orphan
+ while (refQueue.poll() != ref) {
+ System.gc();
+ }
+ }
+
+ private static int getSdkVersion() {
+ return ApplicationProvider.getApplicationContext().getApplicationInfo().targetSdkVersion;
+ }
+}
diff --git a/tests/BinderLeakTest/java/com/android/test/binder/MyService.java b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java
new file mode 100644
index 0000000..c701253
--- /dev/null
+++ b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java
@@ -0,0 +1,63 @@
+/*
+ * 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.test.binder;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+
+public class MyService extends Service {
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new IFooProvider.Stub() {
+ ReferenceQueue<IFoo> mRefQueue = new ReferenceQueue<>();
+ PhantomReference<IFoo> mRef;
+
+ @Override
+ public IFoo createFoo() throws RemoteException {
+ IFoo binder = new IFoo.Stub() {};
+ mRef = new PhantomReference<>(binder, mRefQueue);
+ return binder;
+ }
+
+ @Override
+ public boolean isFooGarbageCollected() throws RemoteException {
+ forceGc();
+ return mRefQueue.poll() == mRef;
+ }
+
+ @Override
+ public void killProcess() throws RemoteException {
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }
+ };
+ }
+
+ private static void forceGc() {
+ Object obj = new Object();
+ ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
+ PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue);
+ obj = null; // make it an orphan
+ while (refQueue.poll() != ref) {
+ System.gc();
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index ee22d07..badd7c8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -50,7 +50,6 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 298544839)
class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
@@ -92,6 +91,7 @@
* Checks that the transition starts with [testApp1]'s windows filling/covering exactly the
* entirety of the display.
*/
+ @FlakyTest(bugId = 298544839)
@Test
open fun startsWithApp1WindowsCoverFullScreen() {
flicker.assertWmStart {
@@ -120,6 +120,7 @@
* Checks that [testApp2] windows fill the entire screen (i.e. is "fullscreen") at the end of
* the transition once we have fully quick switched from [testApp1] back to the [testApp2].
*/
+ @FlakyTest(bugId = 298544839)
@Test
open fun endsWithApp2WindowsCoveringFullScreen() {
flicker.assertWmEnd { this.visibleRegion(testApp2).coversExactly(startDisplayBounds) }
@@ -129,6 +130,7 @@
* Checks that [testApp2] layers fill the entire screen (i.e. is "fullscreen") at the end of the
* transition once we have fully quick switched from [testApp1] back to the [testApp2].
*/
+ @FlakyTest(bugId = 298544839)
@Test
open fun endsWithApp2LayersCoveringFullScreen() {
flicker.assertLayersEnd {
@@ -141,6 +143,7 @@
* Checks that [testApp2] is the top window at the end of the transition once we have fully
* quick switched from [testApp1] back to the [testApp2].
*/
+ @FlakyTest(bugId = 298544839)
@Test
open fun endsWithApp2BeingOnTop() {
flicker.assertWmEnd { this.isAppWindowOnTop(testApp2) }
@@ -165,6 +168,7 @@
* Checks that [testApp2]'s layer starts off invisible and becomes visible at some point before
* the end of the transition and then stays visible until the end of the transition.
*/
+ @FlakyTest(bugId = 298544839)
@Test
open fun app2LayerBecomesAndStaysVisible() {
flicker.assertLayers { this.isInvisible(testApp2).then().isVisible(testApp2) }
@@ -174,6 +178,7 @@
* Checks that [testApp1]'s window starts off visible and becomes invisible at some point before
* the end of the transition and then stays invisible until the end of the transition.
*/
+ @FlakyTest(bugId = 298544839)
@Test
open fun app1WindowBecomesAndStaysInvisible() {
flicker.assertWm { this.isAppWindowVisible(testApp1).then().isAppWindowInvisible(testApp1) }
@@ -193,6 +198,7 @@
* Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
* visible.
*/
+ @FlakyTest(bugId = 298544839)
@Test
open fun app2WindowIsVisibleOnceApp1WindowIsInvisible() {
flicker.assertWm {
@@ -211,6 +217,7 @@
* Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
* visible.
*/
+ @FlakyTest(bugId = 298544839)
@Test
open fun app2LayerIsVisibleOnceApp1LayerIsInvisible() {
flicker.assertLayers {
@@ -225,6 +232,7 @@
}
/** {@inheritDoc} */
+ @FlakyTest(bugId = 298544839)
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
@@ -233,31 +241,45 @@
@Test
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+ @FlakyTest(bugId = 298544839)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
- @Test override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
- @Test override fun entireScreenCovered() = super.entireScreenCovered()
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+ @FlakyTest(bugId = 298544839)
@Test
override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+ @FlakyTest(bugId = 298544839)
@Test
override fun navBarWindowIsVisibleAtStartAndEnd() = super.navBarWindowIsVisibleAtStartAndEnd()
+ @FlakyTest(bugId = 298544839)
@Test
override fun statusBarLayerIsVisibleAtStartAndEnd() =
super.statusBarLayerIsVisibleAtStartAndEnd()
+ @FlakyTest(bugId = 298544839)
@Test
override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
- @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
- @Test override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+ @FlakyTest(bugId = 298544839)
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 06cbeb5..330bc84 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -124,7 +124,7 @@
@SmallTest
public void testSET_ORIENTATION() {
try {
- mWm.freezeRotation(-1);
+ mWm.freezeRotation(/* rotation= */ -1, /* caller= */ "WindowManagerPermissionTests");
fail("IWindowManager.freezeRotation did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -134,7 +134,7 @@
}
try {
- mWm.thawRotation();
+ mWm.thawRotation(/* called= */ "WindowManagerPermissionTests");
fail("IWindowManager.thawRotation did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 1aa4859..0c1d88a 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -13,11 +13,11 @@
}
SourceFile: "HostSideTestClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -33,11 +33,11 @@
}
SourceFile: "HostSideTestKeep.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -56,11 +56,11 @@
}
SourceFile: "HostSideTestNativeSubstitutionClass.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -76,11 +76,11 @@
}
SourceFile: "HostSideTestRemove.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -96,11 +96,11 @@
}
SourceFile: "HostSideTestStub.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -119,11 +119,11 @@
}
SourceFile: "HostSideTestSubstitute.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -139,11 +139,11 @@
}
SourceFile: "HostSideTestThrow.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -159,11 +159,11 @@
}
SourceFile: "HostSideTestWholeClassKeep.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -179,11 +179,11 @@
}
SourceFile: "HostSideTestWholeClassStub.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 1: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -199,7 +199,7 @@
}
SourceFile: "HostSideTestSuppress.java"
RuntimeVisibleAnnotations:
- 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD]
)
@@ -217,9 +217,9 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -230,11 +230,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: ireturn
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public static int getOneStub();
@@ -242,11 +242,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: ireturn
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkCallerCheck.java"
@@ -267,9 +267,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -280,8 +280,8 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+ x: ireturn
LineNumberTable:
public static int getOne_noCheck();
@@ -289,13 +289,13 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+ x: ireturn
LineNumberTable:
}
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -314,14 +314,14 @@
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public int remove;
@@ -333,21 +333,21 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOne(int);
@@ -355,17 +355,17 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 6 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOneInner(int);
@@ -373,17 +373,17 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 4 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public void toBeRemoved(java.lang.String);
@@ -391,17 +391,17 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 8 1 foo Ljava/lang/String;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestRemove
public int addTwo(int);
@@ -409,20 +409,20 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String not supported on host side
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String not supported on host side
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 10 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -432,10 +432,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -446,9 +446,9 @@
descriptor: (I)I
flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -458,10 +458,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -472,14 +472,14 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestThrow
public java.lang.String visibleButUsesUnsupportedMethod();
@@ -487,22 +487,22 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkClassAnnotations.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
@@ -532,15 +532,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -551,10 +551,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -566,10 +566,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -581,10 +581,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -596,20 +596,20 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String not supported on host side
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String not supported on host side
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
0 10 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -619,10 +619,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -633,9 +633,9 @@
descriptor: (I)I
flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestSubstitute(
suffix="_host"
)
@@ -645,10 +645,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -659,8 +659,8 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -671,9 +671,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -681,7 +681,7 @@
}
SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
@@ -702,9 +702,9 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -715,11 +715,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 3: aload_0
- 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
- 9: pop
- 10: return
+ x: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: aload_0
+ x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+ x: pop
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -734,16 +734,16 @@
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class java/util/HashSet
- 3: dup
- 4: invokespecial #x // Method java/util/HashSet."<init>":()V
- 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 10: return
+ x: new #x // class java/util/HashSet
+ x: dup
+ x: invokespecial #x // Method java/util/HashSet."<init>":()V
+ x: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -763,9 +763,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -776,18 +776,18 @@
flags: (0x0008) ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: putstatic #x // Field sInitialized:Z
- 4: return
+ x: iconst_1
+ x: putstatic #x // Field sInitialized:Z
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeInvisibleAnnotations:
- 0: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
- 1: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
@@ -803,9 +803,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -816,18 +816,18 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=0
- 0: new #x // class java/lang/IllegalStateException
- 3: dup
- 4: ldc #x // String Inner exception
- 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
- 9: athrow
- 10: astore_0
- 11: new #x // class java/lang/RuntimeException
- 14: dup
- 15: ldc #x // String Outer exception
- 17: aload_0
- 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
- 21: athrow
+ x: new #x // class java/lang/IllegalStateException
+ x: dup
+ x: ldc #x // String Inner exception
+ x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ x: astore_0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Outer exception
+ x: aload_0
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+ x: athrow
Exception table:
from to target type
0 10 10 Class java/lang/Exception
@@ -841,7 +841,7 @@
}
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
@@ -869,15 +869,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -888,10 +888,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -903,10 +903,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -918,10 +918,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -933,11 +933,11 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String not supported on host side
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String not supported on host side
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -949,10 +949,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -968,10 +968,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -982,8 +982,8 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -994,9 +994,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1017,9 +1017,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1034,9 +1034,9 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
- 0: iload_0
- 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
- 4: ireturn
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1051,10 +1051,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
- 5: lreturn
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1063,9 +1063,9 @@
}
SourceFile: "TinyFrameworkNative.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
value="TinyFrameworkNative_host"
)
@@ -1083,9 +1083,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1096,10 +1096,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1110,10 +1110,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: ladd
- 3: lreturn
+ x: lload_0
+ x: lload_2
+ x: ladd
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1122,7 +1122,7 @@
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -1142,12 +1142,12 @@
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1159,9 +1159,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_1
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1172,9 +1172,9 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1200,9 +1200,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1213,9 +1213,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_2
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1226,9 +1226,9 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1258,12 +1258,12 @@
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1275,9 +1275,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_3
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1288,9 +1288,9 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1316,9 +1316,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1329,9 +1329,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: iconst_4
- 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 4: areturn
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1342,9 +1342,9 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1374,12 +1374,12 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iload_1
- 6: putfield #x // Field value:I
- 9: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1412,15 +1412,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: aload_0
- 10: iconst_5
- 11: putfield #x // Field value:I
- 14: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_5
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1429,7 +1429,7 @@
}
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
InnerClasses:
@@ -1448,9 +1448,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1461,9 +1461,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: bipush 7
- 2: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 5: areturn
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1474,9 +1474,9 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1507,12 +1507,12 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: bipush 6
- 7: putfield #x // Field value:I
- 10: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: bipush 6
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1523,16 +1523,16 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
}
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
InnerClasses:
@@ -1552,10 +1552,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
- 5: return
+ x: aload_0
+ x: iload_1
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1591,15 +1591,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 8: dup
- 9: aload_0
- 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
- 16: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1610,11 +1610,11 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 3: dup
- 4: aload_0
- 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 8: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1626,10 +1626,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
@@ -1638,16 +1638,16 @@
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
- 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
- 10: return
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 6e1528a..43ceec4 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -12,33 +12,33 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int getOneStub();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
@@ -55,44 +55,44 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int getOne_withCheck();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int getOne_noCheck();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -109,7 +109,7 @@
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
@@ -117,13 +117,13 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOne(int);
@@ -131,13 +131,13 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addTwo(int);
@@ -145,47 +145,47 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int nativeAddThree(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkClassAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
@@ -215,97 +215,97 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addOne(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addOneInner(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public void toBeRemoved(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addTwo(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int nativeAddThree(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String unsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
@@ -326,22 +326,22 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static void onClassLoaded(java.lang.Class<?>);
descriptor: (Ljava/lang/Class;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // (Ljava/lang/Class<*>;)V
static {};
@@ -349,20 +349,20 @@
flags: (0x0008) ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -382,35 +382,35 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
- 1: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
@@ -426,31 +426,31 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int testException();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
@@ -470,61 +470,61 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addOne(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public int addTwo(int);
descriptor: (I)I
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static int nativeAddThree(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkForTextPolicy.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
@@ -540,11 +540,11 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static native int nativeAddTwo(int);
descriptor: (I)I
@@ -555,11 +555,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static native long nativeLongPlus(long, long);
descriptor: (JJ)J
@@ -570,22 +570,22 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=4, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
value="TinyFrameworkNative_host"
)
@@ -607,19 +607,19 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
@@ -644,22 +644,22 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
@@ -680,22 +680,22 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
descriptor: ()Ljava/util/function/Supplier;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
}
InnerClasses:
@@ -703,12 +703,12 @@
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
@@ -725,20 +725,20 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
@@ -765,22 +765,22 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.util.function.Supplier<java.lang.Integer> getSupplier();
descriptor: ()Ljava/util/function/Supplier;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
@@ -788,11 +788,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
static {};
@@ -800,11 +800,11 @@
flags: (0x0008) ACC_STATIC
Code:
stack=3, locals=0, args_size=0
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: ldc #x // String Stub!
- 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 9: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
}
InnerClasses:
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
@@ -818,12 +818,12 @@
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 5672e9c..faf0a46 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -13,13 +13,13 @@
}
SourceFile: "HostSideTestClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -35,13 +35,13 @@
}
SourceFile: "HostSideTestKeep.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -60,13 +60,13 @@
}
SourceFile: "HostSideTestNativeSubstitutionClass.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -82,13 +82,13 @@
}
SourceFile: "HostSideTestRemove.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -104,13 +104,13 @@
}
SourceFile: "HostSideTestStub.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -129,13 +129,13 @@
}
SourceFile: "HostSideTestSubstitute.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -151,13 +151,13 @@
}
SourceFile: "HostSideTestThrow.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x,e#x.#x])
+ x: #x(#x=[e#x.#x,e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -173,13 +173,13 @@
}
SourceFile: "HostSideTestWholeClassKeep.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -195,13 +195,13 @@
}
SourceFile: "HostSideTestWholeClassStub.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
- 1: #x(#x=[e#x.#x])
+ x: #x(#x=[e#x.#x])
java.lang.annotation.Target(
value=[Ljava/lang/annotation/ElementType;.TYPE]
)
- 2: #x(#x=e#x.#x)
+ x: #x(#x=e#x.#x)
java.lang.annotation.Retention(
value=Ljava/lang/annotation/RetentionPolicy;.CLASS
)
@@ -219,9 +219,9 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -232,17 +232,17 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=0, args_size=0
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
- 2: ldc #x // String getOneKeep
- 4: ldc #x // String ()I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_1
- 16: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ x: ldc #x // String getOneKeep
+ x: ldc #x // String ()I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public static int getOneStub();
@@ -250,20 +250,20 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: iconst_1
- 1: ireturn
+ x: iconst_1
+ x: ireturn
LineNumberTable:
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
@@ -280,9 +280,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -293,8 +293,8 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+ x: ireturn
LineNumberTable:
public static int getOne_noCheck();
@@ -302,20 +302,20 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
- 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
- 3: ireturn
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+ x: ireturn
LineNumberTable:
}
InnerClasses:
private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
SourceFile: "TinyFrameworkCallerCheck.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -332,14 +332,14 @@
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int keep;
descriptor: I
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
private static {};
@@ -347,31 +347,31 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
- 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- 7: return
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOne(int);
@@ -379,17 +379,17 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
0 6 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
public int addOneInner(int);
@@ -397,23 +397,23 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- 2: ldc #x // String addOneInner
- 4: ldc #x // String (I)I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iload_1
- 16: iconst_1
- 17: iadd
- 18: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
15 4 1 value I
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestKeep
public int addTwo(int);
@@ -421,10 +421,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -436,10 +436,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -450,20 +450,20 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
- 2: ldc #x // String unsupportedMethod
- 4: ldc #x // String ()Ljava/lang/String;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
- 18: new #x // class java/lang/RuntimeException
- 21: dup
- 22: ldc #x // String Unreachable
- 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 27: athrow
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestThrow
public java.lang.String visibleButUsesUnsupportedMethod();
@@ -471,27 +471,27 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
}
SourceFile: "TinyFrameworkClassAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
@@ -521,15 +521,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -540,10 +540,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -555,10 +555,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_1
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -570,10 +570,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: new #x // class java/lang/RuntimeException
- 3: dup
- 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
- 7: athrow
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ x: athrow
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -585,10 +585,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -600,10 +600,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -614,8 +614,8 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: ldc #x // String This value shouldn\'t be seen on the host side.
- 2: areturn
+ x: ldc #x // String This value shouldn\'t be seen on the host side.
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -626,9 +626,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -636,12 +636,12 @@
}
SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
Compiled from "TinyFrameworkClassLoadHook.java"
@@ -662,9 +662,9 @@
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -675,11 +675,11 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 3: aload_0
- 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
- 9: pop
- 10: return
+ x: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: aload_0
+ x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+ x: pop
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -694,21 +694,21 @@
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class java/util/HashSet
- 3: dup
- 4: invokespecial #x // Method java/util/HashSet."<init>":()V
- 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
- 10: return
+ x: new #x // class java/util/HashSet
+ x: dup
+ x: invokespecial #x // Method java/util/HashSet."<init>":()V
+ x: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassLoadHook.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -728,9 +728,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -741,26 +741,26 @@
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
- 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
- 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- 7: iconst_1
- 8: putstatic #x // Field sInitialized:Z
- 11: return
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: iconst_1
+ x: putstatic #x // Field sInitialized:Z
+ x: return
LineNumberTable:
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestClassLoadHook(
value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
)
- 1: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
@@ -776,9 +776,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -789,18 +789,18 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=0
- 0: new #x // class java/lang/IllegalStateException
- 3: dup
- 4: ldc #x // String Inner exception
- 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
- 9: athrow
- 10: astore_0
- 11: new #x // class java/lang/RuntimeException
- 14: dup
- 15: ldc #x // String Outer exception
- 17: aload_0
- 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
- 21: athrow
+ x: new #x // class java/lang/IllegalStateException
+ x: dup
+ x: ldc #x // String Inner exception
+ x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ x: astore_0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Outer exception
+ x: aload_0
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+ x: athrow
Exception table:
from to target type
0 10 10 Class java/lang/Exception
@@ -814,12 +814,12 @@
}
SourceFile: "TinyFrameworkExceptionTester.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
Compiled from "TinyFrameworkForTextPolicy.java"
@@ -843,25 +843,25 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
- 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
- 7: return
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iconst_1
- 6: putfield #x // Field stub:I
- 9: aload_0
- 10: iconst_2
- 11: putfield #x // Field keep:I
- 14: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_1
+ x: putfield #x // Field stub:I
+ x: aload_0
+ x: iconst_2
+ x: putfield #x // Field keep:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -872,10 +872,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokevirtual #x // Method addOneInner:(I)I
- 5: ireturn
+ x: aload_0
+ x: iload_1
+ x: invokevirtual #x // Method addOneInner:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -887,16 +887,16 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- 2: ldc #x // String addOneInner
- 4: ldc #x // String (I)I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iload_1
- 16: iconst_1
- 17: iadd
- 18: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String addOneInner
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_1
+ x: iconst_1
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -908,10 +908,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: iload_1
- 1: iconst_2
- 2: iadd
- 3: ireturn
+ x: iload_1
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -923,10 +923,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
- 0: iload_0
- 1: iconst_3
- 2: iadd
- 3: ireturn
+ x: iload_0
+ x: iconst_3
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -937,27 +937,27 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
- 2: ldc #x // String unsupportedMethod
- 4: ldc #x // String ()Ljava/lang/String;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
- 18: new #x // class java/lang/RuntimeException
- 21: dup
- 22: ldc #x // String Unreachable
- 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- 27: athrow
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ x: ldc #x // String unsupportedMethod
+ x: ldc #x // String ()Ljava/lang/String;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Unreachable
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
public java.lang.String visibleButUsesUnsupportedMethod();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
- 4: areturn
+ x: aload_0
+ x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -965,9 +965,9 @@
}
SourceFile: "TinyFrameworkForTextPolicy.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
@@ -983,9 +983,9 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -996,18 +996,18 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
- 0: iload_0
- 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
- 4: ireturn
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
public static int nativeAddTwo_should_be_like_this(int);
descriptor: (I)I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
- 0: iload_0
- 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
- 4: ireturn
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1018,20 +1018,20 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
- 5: lreturn
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
public static long nativeLongPlus_should_be_like_this(long, long);
descriptor: (JJ)J
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: lload_0
- 1: lload_2
- 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
- 5: lreturn
+ x: lload_0
+ x: lload_2
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1040,14 +1040,14 @@
}
SourceFile: "TinyFrameworkNative.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
- 1: #x(#x=s#x)
+ x: #x(#x=s#x)
android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
value="TinyFrameworkNative_host"
)
@@ -1065,15 +1065,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- 2: ldc #x // String <init>
- 4: ldc #x // String ()V
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokespecial #x // Method java/lang/Object."<init>":()V
- 19: return
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1084,16 +1084,16 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- 2: ldc #x // String nativeAddTwo
- 4: ldc #x // String (I)I
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iload_0
- 16: iconst_2
- 17: iadd
- 18: ireturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeAddTwo
+ x: ldc #x // String (I)I
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iload_0
+ x: iconst_2
+ x: iadd
+ x: ireturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1104,16 +1104,16 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
- 2: ldc #x // String nativeLongPlus
- 4: ldc #x // String (JJ)J
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: lload_0
- 16: lload_2
- 17: ladd
- 18: lreturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ x: ldc #x // String nativeLongPlus
+ x: ldc #x // String (JJ)J
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: lload_0
+ x: lload_2
+ x: ladd
+ x: lreturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1122,10 +1122,10 @@
}
SourceFile: "TinyFrameworkNative_host.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassKeep
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
Compiled from "TinyFrameworkNestedClasses.java"
@@ -1145,12 +1145,12 @@
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1162,15 +1162,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_1
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_1
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1181,15 +1181,15 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1201,7 +1201,7 @@
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
@@ -1218,9 +1218,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1231,15 +1231,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_2
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_2
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1250,15 +1250,15 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1270,7 +1270,7 @@
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
@@ -1291,12 +1291,12 @@
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1308,15 +1308,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_3
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_3
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1327,15 +1327,15 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1347,7 +1347,7 @@
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
@@ -1364,9 +1364,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1377,15 +1377,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: iconst_4
- 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: iconst_4
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1396,15 +1396,15 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1416,7 +1416,7 @@
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
@@ -1437,12 +1437,12 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: iload_1
- 6: putfield #x // Field value:I
- 9: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1453,9 +1453,9 @@
public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
@@ -1480,15 +1480,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: aload_1
- 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
- 5: aload_0
- 6: invokespecial #x // Method java/lang/Object."<init>":()V
- 9: aload_0
- 10: iconst_5
- 11: putfield #x // Field value:I
- 14: return
+ x: aload_0
+ x: aload_1
+ x: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iconst_5
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1499,12 +1499,12 @@
public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
@@ -1521,9 +1521,9 @@
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1534,15 +1534,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Integer;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: bipush 7
- 17: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 20: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Integer;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: bipush 7
+ x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1553,15 +1553,15 @@
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
- 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 2: ldc #x // String get
- 4: ldc #x // String ()Ljava/lang/Object;
- 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
- 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
- 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
- 15: aload_0
- 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
- 19: areturn
+ x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: ldc #x // String get
+ x: ldc #x // String ()Ljava/lang/Object;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ x: aload_0
+ x: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1574,7 +1574,7 @@
Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
@@ -1595,12 +1595,12 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: bipush 6
- 7: putfield #x // Field value:I
- 10: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: bipush 6
+ x: putfield #x // Field value:I
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1611,10 +1611,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
}
@@ -1623,12 +1623,12 @@
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
@@ -1645,10 +1645,10 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
- 0: aload_0
- 1: iload_1
- 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
- 5: return
+ x: aload_0
+ x: iload_1
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1660,9 +1660,9 @@
public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
@@ -1689,15 +1689,15 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #x // Method java/lang/Object."<init>":()V
- 4: aload_0
- 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
- 8: dup
- 9: aload_0
- 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
- 16: return
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1708,11 +1708,11 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
- 3: dup
- 4: aload_0
- 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
- 8: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ x: dup
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ x: areturn
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
@@ -1724,10 +1724,10 @@
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
- 7: areturn
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+ x: areturn
LineNumberTable:
Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
@@ -1736,11 +1736,11 @@
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
- 3: dup
- 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
- 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
- 10: return
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ x: dup
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+ x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ x: return
LineNumberTable:
}
InnerClasses:
@@ -1755,12 +1755,12 @@
#x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
SourceFile: "TinyFrameworkNestedClasses.java"
RuntimeVisibleAnnotations:
- 0: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
- 1: #x()
+ x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
RuntimeInvisibleAnnotations:
- 0: #x()
+ x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/scripts/dump-jar b/tools/hoststubgen/scripts/dump-jar
index 93729fb..992665e 100755
--- a/tools/hoststubgen/scripts/dump-jar
+++ b/tools/hoststubgen/scripts/dump-jar
@@ -93,6 +93,7 @@
if (( $simple )) ; then
# For "simple output" mode,
# - Normalize the constant numbers (replace with "#x")
+ # - Normalize byte code offsets and other similar numbers. (e.g. "0:" -> "x:")
# - Remove the constant pool
# - Remove the line number table
# - Some other transient lines
@@ -100,6 +101,7 @@
# `/PATTERN-1/,/PATTERN-1/{//!d}` is a trick to delete lines between two patterns, without
# the start and the end lines.
sed -e 's/#[0-9][0-9]*/#x/g' \
+ -e 's/^\( *\)[0-9][0-9]*:/\1x:/' \
-e '/^Constant pool:/,/^[^ ]/{//!d}' \
-e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \
-e '/SHA-256 checksum/d' \